Merge "Introduce TracingServiceProxy System Services"
diff --git a/Android.bp b/Android.bp
index 402cf1c..6a2bd95 100644
--- a/Android.bp
+++ b/Android.bp
@@ -574,6 +574,7 @@
],
sdk_version: "core_platform",
static_libs: [
+ "bouncycastle-repackaged-unbundled",
"framework-internal-utils",
// If MimeMap ever becomes its own APEX, then this dependency would need to be removed
// in favor of an API stubs dependency in java_library "framework" below.
@@ -677,6 +678,7 @@
srcs: [
":ipconnectivity-proto-src",
":libstats_atom_enum_protos",
+ ":libtombstone_proto-src",
"core/proto/**/*.proto",
"libs/incident/**/*.proto",
],
diff --git a/METADATA b/METADATA
index d97975c..95577d8 100644
--- a/METADATA
+++ b/METADATA
@@ -1,3 +1,4 @@
third_party {
- license_type: NOTICE
+ # would be NOTICE save for libs/usb/tests/accessorytest/f_accessory.h
+ license_type: RESTRICTED
}
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 64ee09c..367b427 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -79,12 +79,13 @@
"android.hardware.vibrator-V1.3-java",
"framework-protos",
"stable.core.platform.api.stubs",
- // There are a few classes from modules used as type arguments that
- // need to be resolved by metalava. For now, we can use a previously
- // finalized stub library to resolve them. If a new class gets added,
- // this may be need to be revisited to use a manually maintained stub
- // library with empty classes in order to resolve those references.
- "sdk_system_30_android",
+ // There are a few classes from modules used by the core that
+ // need to be resolved by metalava. We use a prebuilt stub of the
+ // full sdk to ensure we can resolve them. If a new class gets added,
+ // the prebuilts/sdk/current needs to be updated.
+ "sdk_system_current_android",
+ // NOTE: The below can be removed once the prebuilt stub contains IKE.
+ "sdk_system_current_android.net.ipsec.ike",
],
high_mem: true, // Lots of sources => high memory use, see b/170701554
installable: false,
@@ -113,7 +114,7 @@
last_released: {
api_file: ":android-non-updatable.api.public.latest",
removed_api_file: ":android-non-updatable-removed.api.public.latest",
- baseline_file: ":public-api-incompatibilities-with-last-released",
+ baseline_file: ":android-incompatibilities.api.public.latest",
},
api_lint: {
enabled: true,
@@ -165,7 +166,7 @@
last_released: {
api_file: ":android-non-updatable.api.system.latest",
removed_api_file: ":android-non-updatable-removed.api.system.latest",
- baseline_file: ":system-api-incompatibilities-with-last-released"
+ baseline_file: ":android-incompatibilities.api.system.latest"
},
api_lint: {
enabled: true,
@@ -392,7 +393,11 @@
"android_defaults_stubs_current",
"android_stubs_dists_default",
],
- libs: ["sdk_system_29_android"],
+ libs: [
+ "sdk_system_current_android",
+ // NOTE: The below can be removed once the prebuilt stub contains IKE.
+ "sdk_system_current_android.net.ipsec.ike",
+ ],
static_libs: ["art.module.public.api.stubs"],
dist: {
dir: "apistubs/android/module-lib",
diff --git a/apct-tests/perftests/core/OWNERS b/apct-tests/perftests/core/OWNERS
new file mode 100644
index 0000000..18486af
--- /dev/null
+++ b/apct-tests/perftests/core/OWNERS
@@ -0,0 +1 @@
+include /graphics/java/android/graphics/fonts/OWNERS
diff --git a/apct-tests/perftests/core/src/android/app/OWNERS b/apct-tests/perftests/core/src/android/app/OWNERS
new file mode 100644
index 0000000..4f168ce
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/app/OWNERS
@@ -0,0 +1,2 @@
+per-file Overlay* = file:/core/java/android/app/RESOURCES_OWNERS
+per-file Resources* = file:/core/java/android/app/RESOURCES_OWNERS
diff --git a/apct-tests/perftests/core/src/android/os/OWNERS b/apct-tests/perftests/core/src/android/os/OWNERS
new file mode 100644
index 0000000..a1719c9
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/os/OWNERS
@@ -0,0 +1 @@
+per-file PackageParsingPerfTest.kt = file:/services/core/java/com/android/server/pm/OWNERS
\ No newline at end of file
diff --git a/apex/Android.bp b/apex/Android.bp
index 0a535a8..8310ba7 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -12,172 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-mainline_stubs_args =
- "--error UnhiddenSystemApi " +
- "--hide BroadcastBehavior " +
- "--hide DeprecationMismatch " +
- "--hide HiddenSuperclass " +
- "--hide HiddenTypedefConstant " +
- "--hide HiddenTypeParameter " +
- "--hide MissingPermission " +
- "--hide RequiresPermission " +
- "--hide SdkConstant " +
- "--hide Todo " +
- "--hide Typo " +
- "--hide UnavailableSymbol "
-
-// TODO: modularize this so not every module has the same whitelist
-framework_packages_to_document = [
- "android",
- "dalvik",
- "java",
- "javax",
- "junit",
- "org.apache.http",
- "org.json",
- "org.w3c.dom",
- "org.xml.sax",
- "org.xmlpull",
-]
-
-// TODO: remove the hiding when server classes are cleaned up.
-mainline_framework_stubs_args =
- mainline_stubs_args +
- "--hide-package com.android.server "
-
-priv_apps = " " +
- "--show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
- "\\) "
-
-module_libs = " " +
- " --show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES" +
- "\\)" +
- " --show-for-stub-purposes-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
- "\\) "
-
-mainline_service_stubs_args =
- mainline_stubs_args +
- "--show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.SYSTEM_SERVER" +
- "\\) " +
- "--hide-annotation android.annotation.Hide " +
- "--hide InternalClasses " // com.android.* classes are okay in this interface
-
-// Defaults common to all mainline module java_sdk_library instances.
-java_defaults {
- name: "framework-module-common-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",
- },
-
- // Configure framework module specific metalava options.
- droiddoc_options: [mainline_stubs_args],
-
- annotations_enabled: true,
-
- // Allow access to the stubs from anywhere
- visibility: ["//visibility:public"],
- stubs_library_visibility: ["//visibility:public"],
-
- // Hide impl library and stub sources
- impl_library_visibility: [
- ":__pkg__",
- "//frameworks/base", // For framework-all
- ],
- stubs_source_visibility: ["//visibility:private"],
-
- defaults_visibility: ["//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",
-}
-
-// Defaults for mainline module provided java_sdk_library instances.
-java_defaults {
- name: "framework-module-defaults",
- defaults: ["framework-module-common-defaults"],
-
- system: {
- enabled: true,
- sdk_version: "module_current",
- },
- module_lib: {
- enabled: true,
- sdk_version: "module_current",
- },
-}
-
-// Defaults for mainline module system server provided java_sdk_library instances.
-java_defaults {
- name: "framework-system-server-module-defaults",
- defaults: ["framework-module-common-defaults"],
-
- system_server: {
- enabled: true,
- sdk_version: "module_current",
- },
-}
-
-stubs_defaults {
- name: "service-module-stubs-srcs-defaults",
- args: mainline_service_stubs_args,
- installable: false,
- annotations_enabled: true,
- merge_annotations_dirs: [
- "metalava-manual",
- ],
- filter_packages: ["com.android."],
- check_api: {
- current: {
- api_file: "api/current.txt",
- removed_api_file: "api/removed.txt",
- },
- api_lint: {
- enabled: true,
- },
- },
- dist: {
- targets: ["sdk", "win_sdk"],
- dir: "apistubs/android/system-server/api",
- },
-}
-
-// Empty for now, but a convenient place to add rules for all
-// module java_library system_server stub libs.
-java_defaults {
- name: "service-module-stubs-defaults",
- dist: {
- targets: ["sdk", "win_sdk"],
- dir: "apistubs/android/system-server",
- },
+package {
+ default_visibility: [":__subpackages__"],
}
diff --git a/apex/OWNERS b/apex/OWNERS
index bde2bec..b3e81b9 100644
--- a/apex/OWNERS
+++ b/apex/OWNERS
@@ -1,8 +1 @@
-# Mainline modularization team
-
-andreionea@google.com
-dariofreni@google.com
-hansson@google.com
-mathewi@google.com
-pedroql@google.com
-satayev@google.com
+file:platform/packages/modules/common:/OWNERS
diff --git a/apex/blobstore/framework/Android.bp b/apex/blobstore/framework/Android.bp
index 2469351..3499553 100644
--- a/apex/blobstore/framework/Android.bp
+++ b/apex/blobstore/framework/Android.bp
@@ -19,6 +19,7 @@
"java/**/*.aidl"
],
path: "java",
+ visibility: ["//frameworks/base"],
}
java_library {
diff --git a/apex/blobstore/service/Android.bp b/apex/blobstore/service/Android.bp
index 22b0cbe..f6cbac1 100644
--- a/apex/blobstore/service/Android.bp
+++ b/apex/blobstore/service/Android.bp
@@ -25,4 +25,9 @@
"services.core",
"services.usage",
],
+ visibility: [
+ // These are required until blobstore is properly unbundled.
+ "//frameworks/base/services",
+ "//frameworks/base/services/tests/mockingservicestests",
+ ],
}
diff --git a/apex/jobscheduler/framework/Android.bp b/apex/jobscheduler/framework/Android.bp
index ec07426..23f5614f 100644
--- a/apex/jobscheduler/framework/Android.bp
+++ b/apex/jobscheduler/framework/Android.bp
@@ -8,6 +8,7 @@
"java/android/os/IDeviceIdleController.aidl",
],
path: "java",
+ visibility: ["//frameworks/base"],
}
java_library {
diff --git a/apex/jobscheduler/service/Android.bp b/apex/jobscheduler/service/Android.bp
index 69a9fd8..6ddba69 100644
--- a/apex/jobscheduler/service/Android.bp
+++ b/apex/jobscheduler/service/Android.bp
@@ -13,4 +13,12 @@
"framework",
"services.core",
],
+ visibility: [
+ "//frameworks/base/apex/jobscheduler:__subpackages__",
+ // These are required until jobscheduler is properly unbundled.
+ "//frameworks/base/services",
+ "//frameworks/base/services/tests/mockingservicestests",
+ "//frameworks/base/services/tests/servicestests",
+ "//frameworks/base/tests/JobSchedulerPerfTests",
+ ],
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index bb94275..15052f8 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -16,7 +16,6 @@
package com.android.server.job.controllers;
-import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
@@ -325,7 +324,7 @@
if (downloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
final long bandwidth = capabilities.getLinkDownstreamBandwidthKbps();
// If we don't know the bandwidth, all we can do is hope the job finishes in time.
- if (bandwidth != LINK_BANDWIDTH_UNSPECIFIED) {
+ if (bandwidth > 0) {
// Divide by 8 to convert bits to bytes.
final long estimatedMillis = ((downloadBytes * DateUtils.SECOND_IN_MILLIS)
/ (DataUnit.KIBIBYTES.toBytes(bandwidth) / 8));
@@ -343,7 +342,7 @@
if (uploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
final long bandwidth = capabilities.getLinkUpstreamBandwidthKbps();
// If we don't know the bandwidth, all we can do is hope the job finishes in time.
- if (bandwidth != LINK_BANDWIDTH_UNSPECIFIED) {
+ if (bandwidth > 0) {
// Divide by 8 to convert bits to bytes.
final long estimatedMillis = ((uploadBytes * DateUtils.SECOND_IN_MILLIS)
/ (DataUnit.KIBIBYTES.toBytes(bandwidth) / 8));
@@ -373,18 +372,16 @@
private static boolean isStrictSatisfied(JobStatus jobStatus, Network network,
NetworkCapabilities capabilities, Constants constants) {
- final NetworkCapabilities required;
// A restricted job that's out of quota MUST use an unmetered network.
if (jobStatus.getEffectiveStandbyBucket() == RESTRICTED_INDEX
&& !jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)) {
- required = new NetworkCapabilities(
+ final NetworkCapabilities required = new NetworkCapabilities.Builder(
jobStatus.getJob().getRequiredNetwork().networkCapabilities)
- .addCapability(NET_CAPABILITY_NOT_METERED);
+ .addCapability(NET_CAPABILITY_NOT_METERED).build();
+ return required.satisfiedByNetworkCapabilities(capabilities);
} else {
- required = jobStatus.getJob().getRequiredNetwork().networkCapabilities;
+ return jobStatus.getJob().getRequiredNetwork().canBeSatisfiedBy(capabilities);
}
-
- return required.satisfiedByNetworkCapabilities(capabilities);
}
private static boolean isRelaxedSatisfied(JobStatus jobStatus, Network network,
@@ -395,9 +392,9 @@
}
// See if we match after relaxing any unmetered request
- final NetworkCapabilities relaxed = new NetworkCapabilities(
+ final NetworkCapabilities relaxed = new NetworkCapabilities.Builder(
jobStatus.getJob().getRequiredNetwork().networkCapabilities)
- .removeCapability(NET_CAPABILITY_NOT_METERED);
+ .removeCapability(NET_CAPABILITY_NOT_METERED).build();
if (relaxed.satisfiedByNetworkCapabilities(capabilities)) {
// TODO: treat this as "maybe" response; need to check quotas
return jobStatus.getFractionRunTime() > constants.CONN_PREFETCH_RELAX_FRAC;
diff --git a/apex/media/Android.bp b/apex/media/Android.bp
new file mode 100644
index 0000000..5f1bd37
--- /dev/null
+++ b/apex/media/Android.bp
@@ -0,0 +1,20 @@
+// 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 {
+ default_visibility: [
+ ":__subpackages__",
+ "//frameworks/av/apex",
+ "//frameworks/av/apex/testing",
+ ],
+}
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index ce4b030..0ff6d44 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -49,6 +49,10 @@
"test_com.android.media",
],
min_sdk_version: "29",
+ visibility: [
+ "//frameworks/av/apex:__subpackages__",
+ "//frameworks/base", // For framework-all
+ ],
}
filegroup {
@@ -58,6 +62,7 @@
":mediasession2-java-srcs",
":mediasession2-aidl-srcs",
],
+ visibility: ["//frameworks/base"],
}
filegroup {
diff --git a/apex/permission/Android.bp b/apex/permission/Android.bp
deleted file mode 100644
index e30df05..0000000
--- a/apex/permission/Android.bp
+++ /dev/null
@@ -1,43 +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 {
- name: "com.android.permission",
- defaults: ["com.android.permission-defaults"],
- manifest: "apex_manifest.json",
-}
-
-apex_defaults {
- name: "com.android.permission-defaults",
- updatable: true,
- min_sdk_version: "30",
- key: "com.android.permission.key",
- certificate: ":com.android.permission.certificate",
- java_libs: [
- "framework-permission",
- "service-permission",
- ],
- apps: ["PermissionController"],
-}
-
-apex_key {
- name: "com.android.permission.key",
- public_key: "com.android.permission.avbpubkey",
- private_key: "com.android.permission.pem",
-}
-
-android_app_certificate {
- name: "com.android.permission.certificate",
- certificate: "com.android.permission",
-}
diff --git a/apex/permission/OWNERS b/apex/permission/OWNERS
deleted file mode 100644
index 957e10a..0000000
--- a/apex/permission/OWNERS
+++ /dev/null
@@ -1,6 +0,0 @@
-svetoslavganov@google.com
-moltmann@google.com
-eugenesusla@google.com
-zhanghai@google.com
-evanseverson@google.com
-ntmyren@google.com
diff --git a/apex/permission/TEST_MAPPING b/apex/permission/TEST_MAPPING
deleted file mode 100644
index 6e67ce9..0000000
--- a/apex/permission/TEST_MAPPING
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "presubmit" : [
- {
- "name" : "PermissionApexTests"
- }
- ]
-}
diff --git a/apex/permission/apex_manifest.json b/apex/permission/apex_manifest.json
deleted file mode 100644
index 7960598..0000000
--- a/apex/permission/apex_manifest.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "name": "com.android.permission",
- "version": 300000000
-}
diff --git a/apex/permission/com.android.permission.avbpubkey b/apex/permission/com.android.permission.avbpubkey
deleted file mode 100644
index 9eaf852..0000000
--- a/apex/permission/com.android.permission.avbpubkey
+++ /dev/null
Binary files differ
diff --git a/apex/permission/com.android.permission.pem b/apex/permission/com.android.permission.pem
deleted file mode 100644
index 3d584be..0000000
--- a/apex/permission/com.android.permission.pem
+++ /dev/null
@@ -1,51 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIJKgIBAAKCAgEA6snt4eqoz85xiL9Sf6w1S1b9FgSHK05zYTh2JYPvQKQ3yeZp
-E6avJ6FN6XcbmkDzSd658BvUGDBSPhOlzuUO4BsoKBuLMxP6TxIQXFKidzDqY0vQ
-4qkS++bdIhUjwBP3OSZ3Czu0BiihK8GC75Abr//EyCyObGIGGfHEGANiOgrpP4X5
-+OmLzQLCjk4iE1kg+U6cRSRI/XLaoWC0TvIIuzxznrQ6r5GmzgTOwyBWyIB+bj73
-bmsweHTU+w9Y7kGOx4hO3XCLIhoBWEw0EbuW9nZmQ4sZls5Jo/CbyJlCclF11yVo
-SCf2LG/T+9pah5NOmDQ1kPbU+0iKZIV4YFHGTIhyGDE/aPOuUT05ziCGDifgHr0u
-SG1x/RLqsVh/POvNxnvP9cQFMQ08BvbEJaTTgB785iwKsvdqCfmng/SAyxSetmzP
-StXVB3fh1OoZ8vunRbQYxnmUxycVqaA96zmBx2wLvbvzKo7pZFDE6nbhnT5+MRAM
-z/VIK89W26uB4gj8sBFslqZjT0jPqsAZuvDm7swOtMwIcEolyGJuFLqlhN7UwMz2
-9y8+IpYixR+HvD1TZI9NtmuCmv3kPrWgoMZg6yvaBayTIr8RdYzi6FO/C1lLiraz
-48dH3sXWRa8cgw6VcSUwYrEBIc3sotdsupO1iOjcFybIwaee0YTZJfjvbqkCAwEA
-AQKCAgEArRnfdpaJi1xLPGTCMDsIt9kUku0XswgN7PmxsYsKFAB+2S40/jYAIRm9
-1YjpItsMA8RgFfSOdJ77o6TctCMQyo17F8bm4+uwuic5RLfv7Cx2QmsdQF8jDfFx
-y7UGPJD7znjbf76uxXOjEB2FqZX3s9TAgkzHXIUQtoQW7RVhkCWHPjxKxgd5+NY2
-FrDoUpd9xhD9CcTsw1+wbRZdGW88nL6/B50dP2AFORM2VYo8MWr6y9FEn3YLsGOC
-uu7fxBk1aUrHyl81VRkTMMROB1zkuiUk1FtzrEm+5U15rXXBFYOVe9+qeLhtuOlh
-wueDoz0pzvF/JLe24uTik6YL0Ae6SD0pFXQ2KDrdH3cUHLok3r76/yGzaDNTFjS2
-2WbQ8dEJV8veNHk8gjGpFTJIsBUlcZpmUCDHlfvVMb3+2ahQ+28piQUt5t3zqJdZ
-NDqsOHzY6BRPc+Wm85Xii/lWiQceZSee/b1Enu+nchsyXhSenBfC6bIGZReyMI0K
-KKKuVhyR6OSOiR5ZdZ/NyXGqsWy05fn/h0X9hnpETsNaNYNKWvpHLfKll+STJpf7
-AZquJPIclQyiq5NONx6kfPztoCLkKV/zOgIj3Sx5oSZq+5gpO91nXWVwkTbqK1d1
-004q2Mah6UQyAk1XGQc2pHx7ouVcWawjU30vZ4C015Hv2lm/gVkCggEBAPltATYS
-OqOSL1YAtIHPiHxMjNAgUdglq8JiJFXVfkocGU9eNub3Ed3sSWu6GB9Myu/sSKje
-bJ5DZqxJnvB2Fqmu9I9OunLGFSD0aXs4prwsQ1Rm5FcbImtrxcciASdkoo8Pj0z4
-vk2r2NZD3VtER5Uh+YjSDkxcS9gBStXUpCL6gj69UpOxMmWqZVjyHatVB4lEvYJl
-N82uT7N7QVNL1DzcZ9z4C4r7ks1Pm7ka12s5m/oaAlAMdVeofiPJe1xA9zRToSr4
-tIbMkOeXFLVRLuji/7XsOgal5Rl59p+OwLshX5cswPVOMrH6zt+hbsJ5q8M5dqnX
-VAOBK7KNQ/EKZwcCggEBAPD6KVvyCim46n5EbcEqCkO7gevwZkw/9vLwmM5YsxTh
-z9FQkPO0iB7mwbX8w04I91Pre4NdfcgMG0pP1b13Sb4KHBchqW1a+TCs3kSGC6gn
-1SxmXHnA9jRxAkrWlGkoAQEz+aP61cXiiy2tXpQwJ8xQCKprfoqWZwhkCtEVU6CE
-S7v9cscOHIqgNxx4WoceMmq4EoihHAZzHxTcNVbByckMjb2XQJ0iNw3lDP4ddvc+
-a4HzHfHkhzeQ5ZNc8SvWU8z80aSCOKRsSD3aUTZzxhZ4O2tZSW7v7p+FpvVee7bC
-g8YCfszTdpVUMlLRLjScimAcovcFLSvtyupinxWg4M8CggEAN9YGEmOsSte7zwXj
-YrfhtumwEBtcFwX/2Ej+F1Tuq4p0xAa0RaoDjumJWhtTsRYQy/raHSuFpzwxbNoi
-QXQ+CIhI6RfXtz/OlQ0B2/rHoJJMFEXgUfuaDfAXW0eqeHYXyezSyIlamKqipPyW
-Pgsf9yue39keKEv1EorfhNTQVaA8rezV4oglXwrxGyNALw2e3UTNI7ai8mFWKDis
-XAg6n9E7UwUYGGnO6DUtCBgRJ0jDOQ6/e8n+LrxiWIKPIgzNCiK6jpMUXqTGv4Fb
-umdNGAdQ9RnHt5tFmRlrczaSwJFtA7uaCpAR2zPpQbiywchZAiAIB2dTwGEXNiZX
-kksg2wKCAQEA6pNad3qhkgPDoK6T+Jkn7M82paoaqtcJWWwEE7oceZNnbWZz9Agl
-CY+vuawXonrv5+0vCq2Tp4zBdBFLC2h3jFrjBVFrUFxifpOIukOSTVqZFON/2bWQ
-9XOcu6UuSz7522Xw+UNPnZXtzcUacD6AP08ZYGvLfrTyDyTzspyED5k48ALEHCkM
-d5WGkFxII4etpF0TDZVnZo/iDbhe49k4yFFEGO6Ho26PESOLBkNAb2V/2bwDxlij
-l9+g21Z6HiZA5SamHPH2mXgeyrcen1cL2QupK9J6vVcqfnboE6qp2zp2c+Yx8MlY
-gfy4EA44YFaSDQVTTgrn8f9Eq+zc130H2QKCAQEAqOKgv68nIPdDSngNyCVyWego
-boFiDaEJoBBg8FrBjTJ6wFLrNAnXmbvfTtgNmNAzF1cUPJZlIIsHgGrMCfpehbXq
-WQQIw+E+yFbTGLxseGRfsLrV0CsgnAoOVeod+yIHmqc3livaUbrWhL1V2f6Ue+sE
-7YLp/iP43NaMfA4kYk2ep7+ZJoEVkCjHJJaHWgAG3RynPJHkTJlSgu7wLYvGc9uE
-ZsEFUM46lX02t7rrtMfasVGrUy1c2xOxFb4v1vG6iEZ7+YWeq5o3AkxUwEGn+mG4
-/3p+k4AaTXJDXgyZ0Sv6CkGuPHenAYG4cswcUUEf/G4Ag77x6LBNMgycJBxUJA==
------END RSA PRIVATE KEY-----
diff --git a/apex/permission/com.android.permission.pk8 b/apex/permission/com.android.permission.pk8
deleted file mode 100644
index d51673d..0000000
--- a/apex/permission/com.android.permission.pk8
+++ /dev/null
Binary files differ
diff --git a/apex/permission/com.android.permission.x509.pem b/apex/permission/com.android.permission.x509.pem
deleted file mode 100644
index 4b146c9..0000000
--- a/apex/permission/com.android.permission.x509.pem
+++ /dev/null
@@ -1,35 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIGKzCCBBOgAwIBAgIUezo3fQeVZsmLpm/dkpGWJ/G/MN8wDQYJKoZIhvcNAQEL
-BQAwgaMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
-DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy
-b2lkMR8wHQYDVQQDDBZjb20uYW5kcm9pZC5wZXJtaXNzaW9uMSIwIAYJKoZIhvcN
-AQkBFhNhbmRyb2lkQGFuZHJvaWQuY29tMCAXDTE5MTAwOTIxMzExOVoYDzQ3NTcw
-OTA0MjEzMTE5WjCBozELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx
-FjAUBgNVBAcMDU1vdW50YWluIFZpZXcxEDAOBgNVBAoMB0FuZHJvaWQxEDAOBgNV
-BAsMB0FuZHJvaWQxHzAdBgNVBAMMFmNvbS5hbmRyb2lkLnBlcm1pc3Npb24xIjAg
-BgkqhkiG9w0BCQEWE2FuZHJvaWRAYW5kcm9pZC5jb20wggIiMA0GCSqGSIb3DQEB
-AQUAA4ICDwAwggIKAoICAQCxefguRJ7E6tBCTEOeU2HJEGs6AQQapLz9hMed0aaJ
-Qr7aTQiYJEk+sG4+jPYbjpxa8JDDzJHp+4g7DjfSb+dvT9n84A8lWaI/yRXTZTQN
-Hu5m/bgHhi0LbySpiaFyodXBKUAnOhZyGPtYjtBFywFylueub8ryc1Z6UxxU7udH
-1mkIr7sE48Qkq5SyjFROE96iFmYA+vS/JXOfS0NBHiMB4GBxx4V7kXpvrTI7hhZG
-HiyhKvNh7wyHIhO9nDEw1rwtAH6CsL3YkQEVBeAU98m+0Au+qStLYkKHh2l8zT4W
-7sVK1VSqfB+VqOUmeIGdzlBfqMsoXD+FJz6KnIdUHIwjFDjL7Xr+hd+7xve+Q3S+
-U3Blk/U6atY8PM09wNfilG+SvwcKk5IgriDcu3rWKgIFxbUUaxLrDW7pLlu6wt/d
-GGtKK+Bc0jF+9Z901Tl33i5xhc5yOktT0btkKs7lSeE6VzP/Nk5g0SuzixmuRoh9
-f5Ge41N2ZCEHNXx3wZeVZwHIIPfYrL7Yql1Xoxbfs4ETFk6ChzVQcvjfDQQuK58J
-uNc+TOCoI/qflXwGCwpuHl0ier8V5Z4tpMUl5rWyVR/QGRtLPvs2lLuxczDw1OXq
-wEVtCMn9aNnd4y7R9PZ52hi53HAvDjpWefrLYi+Q04J6iGFQ1qAFBClK9DquBvmR
-swIDAQABo1MwUTAdBgNVHQ4EFgQULpfus5s5SrqLkoUKyPXA0D1iHPMwHwYDVR0j
-BBgwFoAULpfus5s5SrqLkoUKyPXA0D1iHPMwDwYDVR0TAQH/BAUwAwEB/zANBgkq
-hkiG9w0BAQsFAAOCAgEAjxQG5EFv8V/9yV2glI53VOmlWMjfEgvUjd39s/XLyPlr
-OzPOKSB0NFo8To3l4l+MsManxPK8y0OyfEVKbWVz9onv0ovo5MVokBmV/2G0jmsV
-B4e9yjOq+DmqIvY/Qh63Ywb97sTgcFI8620MhQDbh2IpEGv4ZNV0H6rgXmgdSCBw
-1EjBoYfFpN5aMgZjeyzZcq+d1IapdWqdhuEJQkMvoYS4WIumNIJlEXPQRoq/F5Ih
-nszdbKI/jVyiGFa2oeZ3rja1Y6GCRU8TYEoKx1pjS8uQDOEDTwsG/QnUe9peEj0V
-SsCkIidJWTomAmq9Tub9vpBe1zuTpuRAwxwR0qwgSxozV1Mvow1dJ19oFtHX0yD6
-ZjCpRn5PW9kMvSWSlrcrFs1NJf0j1Cvf7bHpkEDqLqpMnnh9jaFQq3nzDY+MWcIR
-jDcgQpI+AiE2/qtauZnFEVhbce49nCnk9+5bpTTIZJdzqeaExe5KXHwEtZLaEDh4
-atLY9LuEvPsjmDIMOR6hycD9FvwGXhJOQBjESIWFwigtSb1Yud9n6201jw3MLJ4k
-+WhkbmZgWy+xc+Mdm5H3XyB1lvHaHGkxu+QB9KyQuVQKwbUVcbwZIfTFPN6Zr/dS
-ZXJqAbBhG/dBgF0LazuLaPVpibi+a3Y+tb9b8eXGkz4F97PWZIEDkELQ+9KOvhc=
------END CERTIFICATE-----
diff --git a/apex/permission/framework/Android.bp b/apex/permission/framework/Android.bp
deleted file mode 100644
index 1a2d5d5..0000000
--- a/apex/permission/framework/Android.bp
+++ /dev/null
@@ -1,48 +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.
-
-filegroup {
- name: "framework-permission-sources",
- srcs: [
- "java/**/*.java",
- "java/**/*.aidl",
- ],
- path: "java",
-}
-
-java_sdk_library {
- name: "framework-permission",
- defaults: ["framework-module-defaults"],
-
- // Restrict access to implementation library.
- impl_library_visibility: [
- "//frameworks/base/apex/permission:__subpackages__",
- "//packages/modules/Permission:__subpackages__",
- ],
-
- srcs: [
- ":framework-permission-sources",
- ],
-
- apex_available: [
- "com.android.permission",
- "test_com.android.permission",
- ],
- permitted_packages: [
- "android.permission",
- "android.app.role",
- ],
- hostdex: true,
- installable: true,
-}
diff --git a/apex/permission/framework/api/current.txt b/apex/permission/framework/api/current.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/permission/framework/api/current.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/permission/framework/api/module-lib-current.txt b/apex/permission/framework/api/module-lib-current.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/permission/framework/api/module-lib-current.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/permission/framework/api/module-lib-removed.txt b/apex/permission/framework/api/module-lib-removed.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/permission/framework/api/module-lib-removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/permission/framework/api/removed.txt b/apex/permission/framework/api/removed.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/permission/framework/api/removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/permission/framework/api/system-current.txt b/apex/permission/framework/api/system-current.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/permission/framework/api/system-current.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/permission/framework/api/system-removed.txt b/apex/permission/framework/api/system-removed.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/permission/framework/api/system-removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/permission/service/Android.bp b/apex/permission/service/Android.bp
deleted file mode 100644
index cc6f201..0000000
--- a/apex/permission/service/Android.bp
+++ /dev/null
@@ -1,45 +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.
-
-filegroup {
- name: "service-permission-sources",
- srcs: [
- "java/**/*.java",
- ],
- path: "java",
-}
-
-java_sdk_library {
- name: "service-permission",
- defaults: ["framework-system-server-module-defaults"],
- impl_library_visibility: [
- "//frameworks/base/apex/permission/tests",
- "//frameworks/base/services/tests/mockingservicestests",
- "//frameworks/base/services/tests/servicestests",
- "//packages/modules/Permission/tests",
- ],
- srcs: [
- ":service-permission-sources",
- ],
- libs: [
- "framework-permission",
- ],
- apex_available: [
- "com.android.permission",
- "test_com.android.permission",
- ],
- installable: true,
- // We don't have last-api tracking files for the public part of this jar's API.
- unsafe_ignore_missing_latest_api: true,
-}
diff --git a/apex/permission/service/api/current.txt b/apex/permission/service/api/current.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/permission/service/api/current.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/permission/service/api/removed.txt b/apex/permission/service/api/removed.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/permission/service/api/removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/permission/service/api/system-server-current.txt b/apex/permission/service/api/system-server-current.txt
deleted file mode 100644
index c76cc32..0000000
--- a/apex/permission/service/api/system-server-current.txt
+++ /dev/null
@@ -1,46 +0,0 @@
-// Signature format: 2.0
-package com.android.permission.persistence {
-
- public interface RuntimePermissionsPersistence {
- method @NonNull public static com.android.permission.persistence.RuntimePermissionsPersistence createInstance();
- method public void deleteForUser(@NonNull android.os.UserHandle);
- method @Nullable public com.android.permission.persistence.RuntimePermissionsState readForUser(@NonNull android.os.UserHandle);
- method public void writeForUser(@NonNull com.android.permission.persistence.RuntimePermissionsState, @NonNull android.os.UserHandle);
- }
-
- public final class RuntimePermissionsState {
- ctor public RuntimePermissionsState(int, @Nullable String, @NonNull java.util.Map<java.lang.String,java.util.List<com.android.permission.persistence.RuntimePermissionsState.PermissionState>>, @NonNull java.util.Map<java.lang.String,java.util.List<com.android.permission.persistence.RuntimePermissionsState.PermissionState>>);
- method @Nullable public String getFingerprint();
- method @NonNull public java.util.Map<java.lang.String,java.util.List<com.android.permission.persistence.RuntimePermissionsState.PermissionState>> getPackagePermissions();
- method @NonNull public java.util.Map<java.lang.String,java.util.List<com.android.permission.persistence.RuntimePermissionsState.PermissionState>> getSharedUserPermissions();
- method public int getVersion();
- field public static final int NO_VERSION = -1; // 0xffffffff
- }
-
- public static final class RuntimePermissionsState.PermissionState {
- ctor public RuntimePermissionsState.PermissionState(@NonNull String, boolean, int);
- method public int getFlags();
- method @NonNull public String getName();
- method public boolean isGranted();
- }
-
-}
-
-package com.android.role.persistence {
-
- public interface RolesPersistence {
- method @NonNull public static com.android.role.persistence.RolesPersistence createInstance();
- method public void deleteForUser(@NonNull android.os.UserHandle);
- method @Nullable public com.android.role.persistence.RolesState readForUser(@NonNull android.os.UserHandle);
- method public void writeForUser(@NonNull com.android.role.persistence.RolesState, @NonNull android.os.UserHandle);
- }
-
- public final class RolesState {
- ctor public RolesState(int, @Nullable String, @NonNull java.util.Map<java.lang.String,java.util.Set<java.lang.String>>);
- method @Nullable public String getPackagesHash();
- method @NonNull public java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getRoles();
- method public int getVersion();
- }
-
-}
-
diff --git a/apex/permission/service/api/system-server-removed.txt b/apex/permission/service/api/system-server-removed.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/permission/service/api/system-server-removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/permission/service/java/com/android/permission/persistence/IoUtils.java b/apex/permission/service/java/com/android/permission/persistence/IoUtils.java
deleted file mode 100644
index 569a78c..0000000
--- a/apex/permission/service/java/com/android/permission/persistence/IoUtils.java
+++ /dev/null
@@ -1,40 +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.permission.persistence;
-
-import android.annotation.NonNull;
-
-/**
- * Utility class for IO.
- *
- * @hide
- */
-public class IoUtils {
-
- private IoUtils() {}
-
- /**
- * Close 'closeable' ignoring any exceptions.
- */
- public static void closeQuietly(@NonNull AutoCloseable closeable) {
- try {
- closeable.close();
- } catch (Exception ignored) {
- // Ignored.
- }
- }
-}
diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java
deleted file mode 100644
index aedba29..0000000
--- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java
+++ /dev/null
@@ -1,74 +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.permission.persistence;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.annotation.SystemApi.Client;
-import android.os.UserHandle;
-
-/**
- * Persistence for runtime permissions.
- *
- * TODO(b/147914847): Remove @hide when it becomes the default.
- * @hide
- */
-@SystemApi(client = Client.SYSTEM_SERVER)
-public interface RuntimePermissionsPersistence {
-
- /**
- * Read the runtime permissions from persistence.
- *
- * This will perform I/O operations synchronously.
- *
- * @param user the user to read for
- * @return the runtime permissions read
- */
- @Nullable
- RuntimePermissionsState readForUser(@NonNull UserHandle user);
-
- /**
- * Write the runtime permissions to persistence.
- *
- * This will perform I/O operations synchronously.
- *
- * @param runtimePermissions the runtime permissions to write
- * @param user the user to write for
- */
- void writeForUser(@NonNull RuntimePermissionsState runtimePermissions,
- @NonNull UserHandle user);
-
- /**
- * Delete the runtime permissions from persistence.
- *
- * This will perform I/O operations synchronously.
- *
- * @param user the user to delete for
- */
- void deleteForUser(@NonNull UserHandle user);
-
- /**
- * Create a new instance of {@link RuntimePermissionsPersistence} implementation.
- *
- * @return the new instance.
- */
- @NonNull
- static RuntimePermissionsPersistence createInstance() {
- return new RuntimePermissionsPersistenceImpl();
- }
-}
diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java
deleted file mode 100644
index e43f59a..0000000
--- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java
+++ /dev/null
@@ -1,265 +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.permission.persistence;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ApexEnvironment;
-import android.content.pm.PackageManager;
-import android.os.UserHandle;
-import android.util.ArrayMap;
-import android.util.AtomicFile;
-import android.util.Log;
-import android.util.Xml;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Persistence implementation for runtime permissions.
- *
- * TODO(b/147914847): Remove @hide when it becomes the default.
- * @hide
- */
-public class RuntimePermissionsPersistenceImpl implements RuntimePermissionsPersistence {
-
- private static final String LOG_TAG = RuntimePermissionsPersistenceImpl.class.getSimpleName();
-
- private static final String APEX_MODULE_NAME = "com.android.permission";
-
- private static final String RUNTIME_PERMISSIONS_FILE_NAME = "runtime-permissions.xml";
-
- private static final String TAG_PACKAGE = "package";
- private static final String TAG_PERMISSION = "permission";
- private static final String TAG_RUNTIME_PERMISSIONS = "runtime-permissions";
- private static final String TAG_SHARED_USER = "shared-user";
-
- private static final String ATTRIBUTE_FINGERPRINT = "fingerprint";
- private static final String ATTRIBUTE_FLAGS = "flags";
- private static final String ATTRIBUTE_GRANTED = "granted";
- private static final String ATTRIBUTE_NAME = "name";
- private static final String ATTRIBUTE_VERSION = "version";
-
- @Nullable
- @Override
- public RuntimePermissionsState readForUser(@NonNull UserHandle user) {
- File file = getFile(user);
- try (FileInputStream inputStream = new AtomicFile(file).openRead()) {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(inputStream, null);
- return parseXml(parser);
- } catch (FileNotFoundException e) {
- Log.i(LOG_TAG, "runtime-permissions.xml not found");
- return null;
- } catch (XmlPullParserException | IOException e) {
- throw new IllegalStateException("Failed to read runtime-permissions.xml: " + file , e);
- }
- }
-
- @NonNull
- private static RuntimePermissionsState parseXml(@NonNull XmlPullParser parser)
- throws IOException, XmlPullParserException {
- int type;
- int depth;
- int innerDepth = parser.getDepth() + 1;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
- if (depth > innerDepth || type != XmlPullParser.START_TAG) {
- continue;
- }
-
- if (parser.getName().equals(TAG_RUNTIME_PERMISSIONS)) {
- return parseRuntimePermissions(parser);
- }
- }
- throw new IllegalStateException("Missing <" + TAG_RUNTIME_PERMISSIONS
- + "> in runtime-permissions.xml");
- }
-
- @NonNull
- private static RuntimePermissionsState parseRuntimePermissions(@NonNull XmlPullParser parser)
- throws IOException, XmlPullParserException {
- String versionValue = parser.getAttributeValue(null, ATTRIBUTE_VERSION);
- int version = versionValue != null ? Integer.parseInt(versionValue)
- : RuntimePermissionsState.NO_VERSION;
- String fingerprint = parser.getAttributeValue(null, ATTRIBUTE_FINGERPRINT);
-
- Map<String, List<RuntimePermissionsState.PermissionState>> packagePermissions =
- new ArrayMap<>();
- Map<String, List<RuntimePermissionsState.PermissionState>> sharedUserPermissions =
- new ArrayMap<>();
- int type;
- int depth;
- int innerDepth = parser.getDepth() + 1;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
- if (depth > innerDepth || type != XmlPullParser.START_TAG) {
- continue;
- }
-
- switch (parser.getName()) {
- case TAG_PACKAGE: {
- String packageName = parser.getAttributeValue(null, ATTRIBUTE_NAME);
- List<RuntimePermissionsState.PermissionState> permissions = parsePermissions(
- parser);
- packagePermissions.put(packageName, permissions);
- break;
- }
- case TAG_SHARED_USER: {
- String sharedUserName = parser.getAttributeValue(null, ATTRIBUTE_NAME);
- List<RuntimePermissionsState.PermissionState> permissions = parsePermissions(
- parser);
- sharedUserPermissions.put(sharedUserName, permissions);
- break;
- }
- }
- }
-
- return new RuntimePermissionsState(version, fingerprint, packagePermissions,
- sharedUserPermissions);
- }
-
- @NonNull
- private static List<RuntimePermissionsState.PermissionState> parsePermissions(
- @NonNull XmlPullParser parser) throws IOException, XmlPullParserException {
- List<RuntimePermissionsState.PermissionState> permissions = new ArrayList<>();
- int type;
- int depth;
- int innerDepth = parser.getDepth() + 1;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
- if (depth > innerDepth || type != XmlPullParser.START_TAG) {
- continue;
- }
-
- if (parser.getName().equals(TAG_PERMISSION)) {
- String name = parser.getAttributeValue(null, ATTRIBUTE_NAME);
- boolean granted = Boolean.parseBoolean(parser.getAttributeValue(null,
- ATTRIBUTE_GRANTED));
- int flags = Integer.parseInt(parser.getAttributeValue(null,
- ATTRIBUTE_FLAGS), 16);
- RuntimePermissionsState.PermissionState permission =
- new RuntimePermissionsState.PermissionState(name, granted, flags);
- permissions.add(permission);
- }
- }
- return permissions;
- }
-
- @Override
- public void writeForUser(@NonNull RuntimePermissionsState runtimePermissions,
- @NonNull UserHandle user) {
- File file = getFile(user);
- AtomicFile atomicFile = new AtomicFile(file);
- FileOutputStream outputStream = null;
- try {
- outputStream = atomicFile.startWrite();
-
- XmlSerializer serializer = Xml.newSerializer();
- serializer.setOutput(outputStream, StandardCharsets.UTF_8.name());
- serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
- serializer.startDocument(null, true);
-
- serializeRuntimePermissions(serializer, runtimePermissions);
-
- serializer.endDocument();
- atomicFile.finishWrite(outputStream);
- } catch (Exception e) {
- Log.wtf(LOG_TAG, "Failed to write runtime-permissions.xml, restoring backup: " + file,
- e);
- atomicFile.failWrite(outputStream);
- } finally {
- IoUtils.closeQuietly(outputStream);
- }
- }
-
- private static void serializeRuntimePermissions(@NonNull XmlSerializer serializer,
- @NonNull RuntimePermissionsState runtimePermissions) throws IOException {
- serializer.startTag(null, TAG_RUNTIME_PERMISSIONS);
-
- int version = runtimePermissions.getVersion();
- serializer.attribute(null, ATTRIBUTE_VERSION, Integer.toString(version));
- String fingerprint = runtimePermissions.getFingerprint();
- if (fingerprint != null) {
- serializer.attribute(null, ATTRIBUTE_FINGERPRINT, fingerprint);
- }
-
- for (Map.Entry<String, List<RuntimePermissionsState.PermissionState>> entry
- : runtimePermissions.getPackagePermissions().entrySet()) {
- String packageName = entry.getKey();
- List<RuntimePermissionsState.PermissionState> permissions = entry.getValue();
-
- serializer.startTag(null, TAG_PACKAGE);
- serializer.attribute(null, ATTRIBUTE_NAME, packageName);
- serializePermissions(serializer, permissions);
- serializer.endTag(null, TAG_PACKAGE);
- }
-
- for (Map.Entry<String, List<RuntimePermissionsState.PermissionState>> entry
- : runtimePermissions.getSharedUserPermissions().entrySet()) {
- String sharedUserName = entry.getKey();
- List<RuntimePermissionsState.PermissionState> permissions = entry.getValue();
-
- serializer.startTag(null, TAG_SHARED_USER);
- serializer.attribute(null, ATTRIBUTE_NAME, sharedUserName);
- serializePermissions(serializer, permissions);
- serializer.endTag(null, TAG_SHARED_USER);
- }
-
- serializer.endTag(null, TAG_RUNTIME_PERMISSIONS);
- }
-
- private static void serializePermissions(@NonNull XmlSerializer serializer,
- @NonNull List<RuntimePermissionsState.PermissionState> permissions) throws IOException {
- int permissionsSize = permissions.size();
- for (int i = 0; i < permissionsSize; i++) {
- RuntimePermissionsState.PermissionState permissionState = permissions.get(i);
-
- serializer.startTag(null, TAG_PERMISSION);
- serializer.attribute(null, ATTRIBUTE_NAME, permissionState.getName());
- serializer.attribute(null, ATTRIBUTE_GRANTED, Boolean.toString(
- permissionState.isGranted() && (permissionState.getFlags()
- & PackageManager.FLAG_PERMISSION_ONE_TIME) == 0));
- serializer.attribute(null, ATTRIBUTE_FLAGS, Integer.toHexString(
- permissionState.getFlags()));
- serializer.endTag(null, TAG_PERMISSION);
- }
- }
-
- @Override
- public void deleteForUser(@NonNull UserHandle user) {
- getFile(user).delete();
- }
-
- @NonNull
- private static File getFile(@NonNull UserHandle user) {
- ApexEnvironment apexEnvironment = ApexEnvironment.getApexEnvironment(APEX_MODULE_NAME);
- File dataDirectory = apexEnvironment.getDeviceProtectedDataDirForUser(user);
- return new File(dataDirectory, RUNTIME_PERMISSIONS_FILE_NAME);
- }
-}
diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsState.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsState.java
deleted file mode 100644
index c6bfc6d..0000000
--- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsState.java
+++ /dev/null
@@ -1,222 +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.permission.persistence;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.annotation.SystemApi.Client;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * State of all runtime permissions.
- *
- * TODO(b/147914847): Remove @hide when it becomes the default.
- * @hide
- */
-@SystemApi(client = Client.SYSTEM_SERVER)
-public final class RuntimePermissionsState {
-
- /**
- * Special value for {@link #mVersion} to indicate that no version was read.
- */
- public static final int NO_VERSION = -1;
-
- /**
- * The version of the runtime permissions.
- */
- private final int mVersion;
-
- /**
- * The fingerprint of the runtime permissions.
- */
- @Nullable
- private final String mFingerprint;
-
- /**
- * The runtime permissions by packages.
- */
- @NonNull
- private final Map<String, List<PermissionState>> mPackagePermissions;
-
- /**
- * The runtime permissions by shared users.
- */
- @NonNull
- private final Map<String, List<PermissionState>> mSharedUserPermissions;
-
- /**
- * Create a new instance of this class.
- *
- * @param version the version of the runtime permissions
- * @param fingerprint the fingerprint of the runtime permissions
- * @param packagePermissions the runtime permissions by packages
- * @param sharedUserPermissions the runtime permissions by shared users
- */
- public RuntimePermissionsState(int version, @Nullable String fingerprint,
- @NonNull Map<String, List<PermissionState>> packagePermissions,
- @NonNull Map<String, List<PermissionState>> sharedUserPermissions) {
- mVersion = version;
- mFingerprint = fingerprint;
- mPackagePermissions = packagePermissions;
- mSharedUserPermissions = sharedUserPermissions;
- }
-
- /**
- * Get the version of the runtime permissions.
- *
- * @return the version of the runtime permissions
- */
- public int getVersion() {
- return mVersion;
- }
-
- /**
- * Get the fingerprint of the runtime permissions.
- *
- * @return the fingerprint of the runtime permissions
- */
- @Nullable
- public String getFingerprint() {
- return mFingerprint;
- }
-
- /**
- * Get the runtime permissions by packages.
- *
- * @return the runtime permissions by packages
- */
- @NonNull
- public Map<String, List<PermissionState>> getPackagePermissions() {
- return mPackagePermissions;
- }
-
- /**
- * Get the runtime permissions by shared users.
- *
- * @return the runtime permissions by shared users
- */
- @NonNull
- public Map<String, List<PermissionState>> getSharedUserPermissions() {
- return mSharedUserPermissions;
- }
-
- @Override
- public boolean equals(Object object) {
- if (this == object) {
- return true;
- }
- if (object == null || getClass() != object.getClass()) {
- return false;
- }
- RuntimePermissionsState that = (RuntimePermissionsState) object;
- return mVersion == that.mVersion
- && Objects.equals(mFingerprint, that.mFingerprint)
- && Objects.equals(mPackagePermissions, that.mPackagePermissions)
- && Objects.equals(mSharedUserPermissions, that.mSharedUserPermissions);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mVersion, mFingerprint, mPackagePermissions, mSharedUserPermissions);
- }
-
- /**
- * State of a single permission.
- */
- public static final class PermissionState {
-
- /**
- * The name of the permission.
- */
- @NonNull
- private final String mName;
-
- /**
- * Whether the permission is granted.
- */
- private final boolean mGranted;
-
- /**
- * The flags of the permission.
- */
- private final int mFlags;
-
- /**
- * Create a new instance of this class.
- *
- * @param name the name of the permission
- * @param granted whether the permission is granted
- * @param flags the flags of the permission
- */
- public PermissionState(@NonNull String name, boolean granted, int flags) {
- mName = name;
- mGranted = granted;
- mFlags = flags;
- }
-
- /**
- * Get the name of the permission.
- *
- * @return the name of the permission
- */
- @NonNull
- public String getName() {
- return mName;
- }
-
- /**
- * Get whether the permission is granted.
- *
- * @return whether the permission is granted
- */
- public boolean isGranted() {
- return mGranted;
- }
-
- /**
- * Get the flags of the permission.
- *
- * @return the flags of the permission
- */
- public int getFlags() {
- return mFlags;
- }
-
- @Override
- public boolean equals(Object object) {
- if (this == object) {
- return true;
- }
- if (object == null || getClass() != object.getClass()) {
- return false;
- }
- PermissionState that = (PermissionState) object;
- return mGranted == that.mGranted
- && mFlags == that.mFlags
- && Objects.equals(mName, that.mName);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mName, mGranted, mFlags);
- }
- }
-}
diff --git a/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java b/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java
deleted file mode 100644
index 2e5a28a..0000000
--- a/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java
+++ /dev/null
@@ -1,73 +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.role.persistence;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.annotation.SystemApi.Client;
-import android.os.UserHandle;
-
-/**
- * Persistence for roles.
- *
- * TODO(b/147914847): Remove @hide when it becomes the default.
- * @hide
- */
-@SystemApi(client = Client.SYSTEM_SERVER)
-public interface RolesPersistence {
-
- /**
- * Read the roles from persistence.
- *
- * This will perform I/O operations synchronously.
- *
- * @param user the user to read for
- * @return the roles read
- */
- @Nullable
- RolesState readForUser(@NonNull UserHandle user);
-
- /**
- * Write the roles to persistence.
- *
- * This will perform I/O operations synchronously.
- *
- * @param roles the roles to write
- * @param user the user to write for
- */
- void writeForUser(@NonNull RolesState roles, @NonNull UserHandle user);
-
- /**
- * Delete the roles from persistence.
- *
- * This will perform I/O operations synchronously.
- *
- * @param user the user to delete for
- */
- void deleteForUser(@NonNull UserHandle user);
-
- /**
- * Create a new instance of {@link RolesPersistence} implementation.
- *
- * @return the new instance.
- */
- @NonNull
- static RolesPersistence createInstance() {
- return new RolesPersistenceImpl();
- }
-}
diff --git a/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java b/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java
deleted file mode 100644
index f66257f..0000000
--- a/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java
+++ /dev/null
@@ -1,218 +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.role.persistence;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ApexEnvironment;
-import android.os.UserHandle;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.AtomicFile;
-import android.util.Log;
-import android.util.Xml;
-
-import com.android.permission.persistence.IoUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Persistence implementation for roles.
- *
- * TODO(b/147914847): Remove @hide when it becomes the default.
- * @hide
- */
-public class RolesPersistenceImpl implements RolesPersistence {
-
- private static final String LOG_TAG = RolesPersistenceImpl.class.getSimpleName();
-
- private static final String APEX_MODULE_NAME = "com.android.permission";
-
- private static final String ROLES_FILE_NAME = "roles.xml";
-
- private static final String TAG_ROLES = "roles";
- private static final String TAG_ROLE = "role";
- private static final String TAG_HOLDER = "holder";
-
- private static final String ATTRIBUTE_VERSION = "version";
- private static final String ATTRIBUTE_NAME = "name";
- private static final String ATTRIBUTE_PACKAGES_HASH = "packagesHash";
-
- @Nullable
- @Override
- public RolesState readForUser(@NonNull UserHandle user) {
- File file = getFile(user);
- try (FileInputStream inputStream = new AtomicFile(file).openRead()) {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(inputStream, null);
- return parseXml(parser);
- } catch (FileNotFoundException e) {
- Log.i(LOG_TAG, "roles.xml not found");
- return null;
- } catch (XmlPullParserException | IOException e) {
- throw new IllegalStateException("Failed to read roles.xml: " + file , e);
- }
- }
-
- @NonNull
- private static RolesState parseXml(@NonNull XmlPullParser parser)
- throws IOException, XmlPullParserException {
- int type;
- int depth;
- int innerDepth = parser.getDepth() + 1;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
- if (depth > innerDepth || type != XmlPullParser.START_TAG) {
- continue;
- }
-
- if (parser.getName().equals(TAG_ROLES)) {
- return parseRoles(parser);
- }
- }
- throw new IllegalStateException("Missing <" + TAG_ROLES + "> in roles.xml");
- }
-
- @NonNull
- private static RolesState parseRoles(@NonNull XmlPullParser parser)
- throws IOException, XmlPullParserException {
- int version = Integer.parseInt(parser.getAttributeValue(null, ATTRIBUTE_VERSION));
- String packagesHash = parser.getAttributeValue(null, ATTRIBUTE_PACKAGES_HASH);
-
- Map<String, Set<String>> roles = new ArrayMap<>();
- int type;
- int depth;
- int innerDepth = parser.getDepth() + 1;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
- if (depth > innerDepth || type != XmlPullParser.START_TAG) {
- continue;
- }
-
- if (parser.getName().equals(TAG_ROLE)) {
- String roleName = parser.getAttributeValue(null, ATTRIBUTE_NAME);
- Set<String> roleHolders = parseRoleHolders(parser);
- roles.put(roleName, roleHolders);
- }
- }
-
- return new RolesState(version, packagesHash, roles);
- }
-
- @NonNull
- private static Set<String> parseRoleHolders(@NonNull XmlPullParser parser)
- throws IOException, XmlPullParserException {
- Set<String> roleHolders = new ArraySet<>();
- int type;
- int depth;
- int innerDepth = parser.getDepth() + 1;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
- if (depth > innerDepth || type != XmlPullParser.START_TAG) {
- continue;
- }
-
- if (parser.getName().equals(TAG_HOLDER)) {
- String roleHolder = parser.getAttributeValue(null, ATTRIBUTE_NAME);
- roleHolders.add(roleHolder);
- }
- }
- return roleHolders;
- }
-
- @Override
- public void writeForUser(@NonNull RolesState roles, @NonNull UserHandle user) {
- File file = getFile(user);
- AtomicFile atomicFile = new AtomicFile(file);
- FileOutputStream outputStream = null;
- try {
- outputStream = atomicFile.startWrite();
-
- XmlSerializer serializer = Xml.newSerializer();
- serializer.setOutput(outputStream, StandardCharsets.UTF_8.name());
- serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
- serializer.startDocument(null, true);
-
- serializeRoles(serializer, roles);
-
- serializer.endDocument();
- atomicFile.finishWrite(outputStream);
- } catch (Exception e) {
- Log.wtf(LOG_TAG, "Failed to write roles.xml, restoring backup: " + file,
- e);
- atomicFile.failWrite(outputStream);
- } finally {
- IoUtils.closeQuietly(outputStream);
- }
- }
-
- private static void serializeRoles(@NonNull XmlSerializer serializer,
- @NonNull RolesState roles) throws IOException {
- serializer.startTag(null, TAG_ROLES);
-
- int version = roles.getVersion();
- serializer.attribute(null, ATTRIBUTE_VERSION, Integer.toString(version));
- String packagesHash = roles.getPackagesHash();
- if (packagesHash != null) {
- serializer.attribute(null, ATTRIBUTE_PACKAGES_HASH, packagesHash);
- }
-
- for (Map.Entry<String, Set<String>> entry : roles.getRoles().entrySet()) {
- String roleName = entry.getKey();
- Set<String> roleHolders = entry.getValue();
-
- serializer.startTag(null, TAG_ROLE);
- serializer.attribute(null, ATTRIBUTE_NAME, roleName);
- serializeRoleHolders(serializer, roleHolders);
- serializer.endTag(null, TAG_ROLE);
- }
-
- serializer.endTag(null, TAG_ROLES);
- }
-
- private static void serializeRoleHolders(@NonNull XmlSerializer serializer,
- @NonNull Set<String> roleHolders) throws IOException {
- for (String roleHolder : roleHolders) {
- serializer.startTag(null, TAG_HOLDER);
- serializer.attribute(null, ATTRIBUTE_NAME, roleHolder);
- serializer.endTag(null, TAG_HOLDER);
- }
- }
-
- @Override
- public void deleteForUser(@NonNull UserHandle user) {
- getFile(user).delete();
- }
-
- @NonNull
- private static File getFile(@NonNull UserHandle user) {
- ApexEnvironment apexEnvironment = ApexEnvironment.getApexEnvironment(APEX_MODULE_NAME);
- File dataDirectory = apexEnvironment.getDeviceProtectedDataDirForUser(user);
- return new File(dataDirectory, ROLES_FILE_NAME);
- }
-}
diff --git a/apex/permission/service/java/com/android/role/persistence/RolesState.java b/apex/permission/service/java/com/android/role/persistence/RolesState.java
deleted file mode 100644
index f61efa0..0000000
--- a/apex/permission/service/java/com/android/role/persistence/RolesState.java
+++ /dev/null
@@ -1,115 +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.role.persistence;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.annotation.SystemApi.Client;
-
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * State of all roles.
- *
- * TODO(b/147914847): Remove @hide when it becomes the default.
- * @hide
- */
-@SystemApi(client = Client.SYSTEM_SERVER)
-public final class RolesState {
-
- /**
- * The version of the roles.
- */
- private final int mVersion;
-
- /**
- * The hash of all packages in the system.
- */
- @Nullable
- private final String mPackagesHash;
-
- /**
- * The roles.
- */
- @NonNull
- private final Map<String, Set<String>> mRoles;
-
- /**
- * Create a new instance of this class.
- *
- * @param version the version of the roles
- * @param packagesHash the hash of all packages in the system
- * @param roles the roles
- */
- public RolesState(int version, @Nullable String packagesHash,
- @NonNull Map<String, Set<String>> roles) {
- mVersion = version;
- mPackagesHash = packagesHash;
- mRoles = roles;
- }
-
- /**
- * Get the version of the roles.
- *
- * @return the version of the roles
- */
- public int getVersion() {
- return mVersion;
- }
-
- /**
- * Get the hash of all packages in the system.
- *
- * @return the hash of all packages in the system
- */
- @Nullable
- public String getPackagesHash() {
- return mPackagesHash;
- }
-
- /**
- * Get the roles.
- *
- * @return the roles
- */
- @NonNull
- public Map<String, Set<String>> getRoles() {
- return mRoles;
- }
-
- @Override
- public boolean equals(Object object) {
- if (this == object) {
- return true;
- }
- if (object == null || getClass() != object.getClass()) {
- return false;
- }
- RolesState that = (RolesState) object;
- return mVersion == that.mVersion
- && Objects.equals(mPackagesHash, that.mPackagesHash)
- && Objects.equals(mRoles, that.mRoles);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mVersion, mPackagesHash, mRoles);
- }
-}
diff --git a/apex/permission/testing/Android.bp b/apex/permission/testing/Android.bp
deleted file mode 100644
index 63bf0a0..0000000
--- a/apex/permission/testing/Android.bp
+++ /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.
-
-apex_test {
- name: "test_com.android.permission",
- visibility: [
- "//system/apex/tests",
- ],
- defaults: ["com.android.permission-defaults"],
- manifest: "test_manifest.json",
- file_contexts: ":com.android.permission-file_contexts",
- // Test APEX, should never be installed
- installable: false,
-}
diff --git a/apex/permission/testing/test_manifest.json b/apex/permission/testing/test_manifest.json
deleted file mode 100644
index bc19a9e..0000000
--- a/apex/permission/testing/test_manifest.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "name": "com.android.permission",
- "version": 2147483647
-}
diff --git a/apex/permission/tests/Android.bp b/apex/permission/tests/Android.bp
deleted file mode 100644
index 271e328..0000000
--- a/apex/permission/tests/Android.bp
+++ /dev/null
@@ -1,37 +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.
-
-android_test {
- name: "PermissionApexTests",
- sdk_version: "test_current",
- srcs: [
- "java/**/*.kt",
- ],
- static_libs: [
- "service-permission.impl",
- "androidx.test.rules",
- "androidx.test.ext.junit",
- "androidx.test.ext.truth",
- "mockito-target-extended-minus-junit4",
- ],
- jni_libs: [
- "libdexmakerjvmtiagent",
- "libstaticjvmtiagent",
- ],
- compile_multilib: "both",
- test_suites: [
- "general-tests",
- "mts",
- ],
-}
diff --git a/apex/permission/tests/AndroidManifest.xml b/apex/permission/tests/AndroidManifest.xml
deleted file mode 100644
index 57ee641..0000000
--- a/apex/permission/tests/AndroidManifest.xml
+++ /dev/null
@@ -1,32 +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.
- -->
-
-<manifest
- xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.permission.test">
-
- <!-- The application has to be debuggable for static mocking to work. -->
- <application android:debuggable="true">
- <uses-library android:name="android.test.runner" />
- </application>
-
- <instrumentation
- android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.permission.test"
- android:label="Permission APEX Tests" />
-</manifest>
diff --git a/apex/permission/tests/java/com/android/permission/persistence/RuntimePermissionsPersistenceTest.kt b/apex/permission/tests/java/com/android/permission/persistence/RuntimePermissionsPersistenceTest.kt
deleted file mode 100644
index 2987da0..0000000
--- a/apex/permission/tests/java/com/android/permission/persistence/RuntimePermissionsPersistenceTest.kt
+++ /dev/null
@@ -1,110 +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.permission.persistence
-
-import android.content.ApexEnvironment
-import android.content.Context
-import android.os.Process
-import android.os.UserHandle
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
-import com.google.common.truth.Truth.assertThat
-import org.junit.After
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Mock
-import org.mockito.Mockito.`when`
-import org.mockito.MockitoAnnotations.initMocks
-import org.mockito.MockitoSession
-import org.mockito.quality.Strictness
-import java.io.File
-
-@RunWith(AndroidJUnit4::class)
-class RuntimePermissionsPersistenceTest {
- private val context = InstrumentationRegistry.getInstrumentation().context
-
- private lateinit var mockDataDirectory: File
-
- private lateinit var mockitoSession: MockitoSession
- @Mock
- lateinit var apexEnvironment: ApexEnvironment
-
- private val persistence = RuntimePermissionsPersistence.createInstance()
- private val permissionState = RuntimePermissionsState.PermissionState("permission", true, 3)
- private val state = RuntimePermissionsState(
- 1, "fingerprint", mapOf("package" to listOf(permissionState)),
- mapOf("sharedUser" to listOf(permissionState))
- )
- private val user = Process.myUserHandle()
-
- @Before
- fun createMockDataDirectory() {
- mockDataDirectory = context.getDir("mock_data", Context.MODE_PRIVATE)
- mockDataDirectory.listFiles()!!.forEach { assertThat(it.deleteRecursively()).isTrue() }
- }
-
- @Before
- fun mockApexEnvironment() {
- initMocks(this)
- mockitoSession = mockitoSession()
- .mockStatic(ApexEnvironment::class.java)
- .strictness(Strictness.LENIENT)
- .startMocking()
- `when`(ApexEnvironment.getApexEnvironment(eq(APEX_MODULE_NAME))).thenReturn(apexEnvironment)
- `when`(apexEnvironment.getDeviceProtectedDataDirForUser(any(UserHandle::class.java))).then {
- File(mockDataDirectory, it.arguments[0].toString()).also { it.mkdirs() }
- }
- }
-
- @After
- fun finishMockingApexEnvironment() {
- mockitoSession.finishMocking()
- }
-
- @Test
- fun testReadWrite() {
- persistence.writeForUser(state, user)
- val persistedState = persistence.readForUser(user)
-
- assertThat(persistedState).isEqualTo(state)
- assertThat(persistedState!!.version).isEqualTo(state.version)
- assertThat(persistedState.fingerprint).isEqualTo(state.fingerprint)
- assertThat(persistedState.packagePermissions).isEqualTo(state.packagePermissions)
- val persistedPermissionState = persistedState.packagePermissions.values.first().first()
- assertThat(persistedPermissionState.name).isEqualTo(permissionState.name)
- assertThat(persistedPermissionState.isGranted).isEqualTo(permissionState.isGranted)
- assertThat(persistedPermissionState.flags).isEqualTo(permissionState.flags)
- assertThat(persistedState.sharedUserPermissions).isEqualTo(state.sharedUserPermissions)
- }
-
- @Test
- fun testDelete() {
- persistence.writeForUser(state, user)
- persistence.deleteForUser(user)
- val persistedState = persistence.readForUser(user)
-
- assertThat(persistedState).isNull()
- }
-
- companion object {
- private const val APEX_MODULE_NAME = "com.android.permission"
- }
-}
diff --git a/apex/permission/tests/java/com/android/role/persistence/RolesPersistenceTest.kt b/apex/permission/tests/java/com/android/role/persistence/RolesPersistenceTest.kt
deleted file mode 100644
index f9d9d5a..0000000
--- a/apex/permission/tests/java/com/android/role/persistence/RolesPersistenceTest.kt
+++ /dev/null
@@ -1,101 +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.role.persistence
-
-import android.content.ApexEnvironment
-import android.content.Context
-import android.os.Process
-import android.os.UserHandle
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
-import com.google.common.truth.Truth.assertThat
-import org.junit.After
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Mock
-import org.mockito.Mockito.`when`
-import org.mockito.MockitoAnnotations.initMocks
-import org.mockito.MockitoSession
-import org.mockito.quality.Strictness
-import java.io.File
-
-@RunWith(AndroidJUnit4::class)
-class RolesPersistenceTest {
- private val context = InstrumentationRegistry.getInstrumentation().context
-
- private lateinit var mockDataDirectory: File
-
- private lateinit var mockitoSession: MockitoSession
- @Mock
- lateinit var apexEnvironment: ApexEnvironment
-
- private val persistence = RolesPersistence.createInstance()
- private val state = RolesState(1, "packagesHash", mapOf("role" to setOf("holder1", "holder2")))
- private val user = Process.myUserHandle()
-
- @Before
- fun createMockDataDirectory() {
- mockDataDirectory = context.getDir("mock_data", Context.MODE_PRIVATE)
- mockDataDirectory.listFiles()!!.forEach { assertThat(it.deleteRecursively()).isTrue() }
- }
-
- @Before
- fun mockApexEnvironment() {
- initMocks(this)
- mockitoSession = mockitoSession()
- .mockStatic(ApexEnvironment::class.java)
- .strictness(Strictness.LENIENT)
- .startMocking()
- `when`(ApexEnvironment.getApexEnvironment(eq(APEX_MODULE_NAME))).thenReturn(apexEnvironment)
- `when`(apexEnvironment.getDeviceProtectedDataDirForUser(any(UserHandle::class.java))).then {
- File(mockDataDirectory, it.arguments[0].toString()).also { it.mkdirs() }
- }
- }
-
- @After
- fun finishMockingApexEnvironment() {
- mockitoSession.finishMocking()
- }
-
- @Test
- fun testReadWrite() {
- persistence.writeForUser(state, user)
- val persistedState = persistence.readForUser(user)
-
- assertThat(persistedState).isEqualTo(state)
- assertThat(persistedState!!.version).isEqualTo(state.version)
- assertThat(persistedState.packagesHash).isEqualTo(state.packagesHash)
- assertThat(persistedState.roles).isEqualTo(state.roles)
- }
-
- @Test
- fun testDelete() {
- persistence.writeForUser(state, user)
- persistence.deleteForUser(user)
- val persistedState = persistence.readForUser(user)
-
- assertThat(persistedState).isNull()
- }
-
- companion object {
- private const val APEX_MODULE_NAME = "com.android.permission"
- }
-}
diff --git a/boot/Android.bp b/boot/Android.bp
new file mode 100644
index 0000000..dd4066a
--- /dev/null
+++ b/boot/Android.bp
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+boot_image {
+ name: "framework-boot-image",
+ image_name: "boot",
+}
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 2c7ee21..854982f 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -92,6 +92,8 @@
static const char SYSTEM_TIME_DIR_PATH[] = "/data/system/time";
static const char CLOCK_FONT_ASSET[] = "images/clock_font.png";
static const char CLOCK_FONT_ZIP_NAME[] = "clock_font.png";
+static const char PROGRESS_FONT_ASSET[] = "images/progress_font.png";
+static const char PROGRESS_FONT_ZIP_NAME[] = "progress_font.png";
static const char LAST_TIME_CHANGED_FILE_NAME[] = "last_time_change";
static const char LAST_TIME_CHANGED_FILE_PATH[] = "/data/system/time/last_time_change";
static const char ACCURATE_TIME_FLAG_FILE_NAME[] = "time_is_accurate";
@@ -107,6 +109,7 @@
static const int TEXT_CENTER_VALUE = INT_MAX;
static const int TEXT_MISSING_VALUE = INT_MIN;
static const char EXIT_PROP_NAME[] = "service.bootanim.exit";
+static const char PROGRESS_PROP_NAME[] = "service.bootanim.progress";
static const char DISPLAYS_PROP_NAME[] = "persist.service.bootanim.displays";
static const int ANIM_ENTRY_NAME_MAX = ANIM_PATH_MAX + 1;
static constexpr size_t TEXT_POS_LEN_MAX = 16;
@@ -891,6 +894,18 @@
drawText(out, font, false, &x, &y);
}
+void BootAnimation::drawProgress(int percent, const Font& font, const int xPos, const int yPos) {
+ static constexpr int PERCENT_LENGTH = 5;
+
+ char percentBuff[PERCENT_LENGTH];
+ // ';' has the ascii code just after ':', and the font resource contains '%'
+ // for that ascii code.
+ sprintf(percentBuff, "%d;", percent);
+ int x = xPos;
+ int y = yPos;
+ drawText(percentBuff, font, false, &x, &y);
+}
+
bool BootAnimation::parseAnimationDesc(Animation& animation) {
String8 desString;
@@ -910,6 +925,7 @@
int height = 0;
int count = 0;
int pause = 0;
+ int progress = 0;
int framesToFadeCount = 0;
char path[ANIM_ENTRY_NAME_MAX];
char color[7] = "000000"; // default to black if unspecified
@@ -919,11 +935,17 @@
int nextReadPos;
- if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) {
- // SLOGD("> w=%d, h=%d, fps=%d", width, height, fps);
+ int topLineNumbers = sscanf(l, "%d %d %d %d", &width, &height, &fps, &progress);
+ if (topLineNumbers == 3 || topLineNumbers == 4) {
+ // SLOGD("> w=%d, h=%d, fps=%d, progress=%d", width, height, fps, progress);
animation.width = width;
animation.height = height;
animation.fps = fps;
+ if (topLineNumbers == 4) {
+ animation.progressEnabled = (progress != 0);
+ } else {
+ animation.progressEnabled = false;
+ }
} else if (sscanf(l, "%c %d %d %" STRTO(ANIM_PATH_MAX) "s%n",
&pathType, &count, &pause, path, &nextReadPos) >= 4) {
if (pathType == 'f') {
@@ -1000,6 +1022,14 @@
continue;
}
+ if (entryName == PROGRESS_FONT_ZIP_NAME) {
+ FileMap* map = zip->createEntryFileMap(entry);
+ if (map) {
+ animation.progressFont.map = map;
+ }
+ continue;
+ }
+
for (size_t j = 0; j < pcount; j++) {
if (path == animation.parts[j].path) {
uint16_t method;
@@ -1131,6 +1161,8 @@
mClockEnabled = clockFontInitialized;
}
+ initFont(&mAnimation->progressFont, PROGRESS_FONT_ASSET);
+
if (mClockEnabled && !updateIsTimeAccurate()) {
mTimeCheckThread = new TimeCheckThread(this);
mTimeCheckThread->run("BootAnimation::TimeCheckThread", PRIORITY_NORMAL);
@@ -1166,6 +1198,7 @@
elapsedRealtime());
int fadedFramesCount = 0;
+ int lastDisplayedProgress = 0;
for (size_t i=0 ; i<pcount ; i++) {
const Animation::Part& part(animation.parts[i]);
const size_t fcount = part.frames.size();
@@ -1191,6 +1224,12 @@
part.backgroundColor[2],
1.0f);
+ // For the last animation, if we have progress indicator from
+ // the system, display it.
+ int currentProgress = android::base::GetIntProperty(PROGRESS_PROP_NAME, 0);
+ bool displayProgress = animation.progressEnabled &&
+ (i == (pcount -1)) && currentProgress != 0;
+
for (size_t j=0 ; j<fcount ; j++) {
if (shouldStopPlayingPart(part, fadedFramesCount)) break;
@@ -1248,6 +1287,23 @@
drawClock(animation.clockFont, part.clockPosX, part.clockPosY);
}
+ if (displayProgress) {
+ int newProgress = android::base::GetIntProperty(PROGRESS_PROP_NAME, 0);
+ // In case the new progress jumped suddenly, still show an
+ // increment of 1.
+ if (lastDisplayedProgress != 100) {
+ // Artificially sleep 1/10th a second to slow down the animation.
+ usleep(100000);
+ if (lastDisplayedProgress < newProgress) {
+ lastDisplayedProgress++;
+ }
+ }
+ // Put the progress percentage right below the animation.
+ int posY = animation.height / 3;
+ int posX = TEXT_CENTER_VALUE;
+ drawProgress(lastDisplayedProgress, animation.progressFont, posX, posY);
+ }
+
handleViewport(frameDuration);
eglSwapBuffers(mDisplay, mSurface);
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index aee3853..b52222c 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -100,11 +100,13 @@
int fps;
int width;
int height;
+ bool progressEnabled;
Vector<Part> parts;
String8 audioConf;
String8 fileName;
ZipFileRO* zip;
Font clockFont;
+ Font progressFont;
};
// All callbacks will be called from this class's internal thread.
@@ -168,6 +170,7 @@
bool movie();
void drawText(const char* str, const Font& font, bool bold, int* x, int* y);
void drawClock(const Font& font, const int xPos, const int yPos);
+ void drawProgress(int percent, const Font& font, const int xPos, const int yPos);
void fadeFrame(int frameLeft, int frameBottom, int frameWidth, int frameHeight,
const Animation::Part& part, int fadedFramesCount);
bool validClock(const Animation::Part& part);
diff --git a/cmds/bootanimation/FORMAT.md b/cmds/bootanimation/FORMAT.md
index f9b83c9..1678053 100644
--- a/cmds/bootanimation/FORMAT.md
+++ b/cmds/bootanimation/FORMAT.md
@@ -22,11 +22,14 @@
The first line defines the general parameters of the animation:
- WIDTH HEIGHT FPS
+ WIDTH HEIGHT FPS [PROGRESS]
* **WIDTH:** animation width (pixels)
* **HEIGHT:** animation height (pixels)
* **FPS:** frames per second, e.g. 60
+ * **PROGRESS:** whether to show a progress percentage on the last part
+ + The percentage will be displayed with an x-coordinate of 'c', and a
+ y-coordinate set to 1/3 of the animation height.
It is followed by a number of rows of the form:
@@ -77,6 +80,11 @@
* Each row is divided in half: regular weight glyphs on the top half, bold glyphs on the bottom
* For a NxM image each character glyph will be N/16 pixels wide and M/(12*2) pixels high
+## progress_font.png
+
+The file used to draw the boot progress in percentage on top of the boot animation. The font format
+follows the same specification as the one described for clock_font.png.
+
## loading and playing frames
Each part is scanned and loaded directly from the zip archive. Within a part directory, every file
diff --git a/config/preloaded-classes-denylist b/config/preloaded-classes-denylist
index 8ab5273..2360ef12 100644
--- a/config/preloaded-classes-denylist
+++ b/config/preloaded-classes-denylist
@@ -4,4 +4,3 @@
android.os.NullVibrator
android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask
android.widget.Magnifier
-com.android.server.BootReceiver$2
diff --git a/core/api/current.txt b/core/api/current.txt
index b0534a1..976ec56 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1142,6 +1142,7 @@
field public static final int reqNavigation = 16843306; // 0x101022a
field public static final int reqTouchScreen = 16843303; // 0x1010227
field public static final int requestLegacyExternalStorage = 16844291; // 0x1010603
+ field public static final int requireDeviceScreenOn = 16844312; // 0x1010618
field public static final int requireDeviceUnlock = 16843756; // 0x10103ec
field public static final int required = 16843406; // 0x101028e
field public static final int requiredAccountType = 16843734; // 0x10103d6
@@ -9139,7 +9140,7 @@
method public boolean getIncludeTxPowerLevel();
method public android.util.SparseArray<byte[]> getManufacturerSpecificData();
method public java.util.Map<android.os.ParcelUuid,byte[]> getServiceData();
- method @Nullable public java.util.List<android.os.ParcelUuid> getServiceSolicitationUuids();
+ method @NonNull public java.util.List<android.os.ParcelUuid> getServiceSolicitationUuids();
method public java.util.List<android.os.ParcelUuid> getServiceUuids();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.le.AdvertiseData> CREATOR;
@@ -12133,6 +12134,8 @@
field public static final String FEATURE_INPUT_METHODS = "android.software.input_methods";
field public static final String FEATURE_IPSEC_TUNNELS = "android.software.ipsec_tunnels";
field public static final String FEATURE_IRIS = "android.hardware.biometrics.iris";
+ field public static final String FEATURE_KEYSTORE_LIMITED_USE_KEY = "android.hardware.keystore.limited_use_key";
+ field public static final String FEATURE_KEYSTORE_SINGLE_USE_KEY = "android.hardware.keystore.single_use_key";
field public static final String FEATURE_LEANBACK = "android.software.leanback";
field public static final String FEATURE_LEANBACK_ONLY = "android.software.leanback_only";
field public static final String FEATURE_LIVE_TV = "android.software.live_tv";
@@ -25086,8 +25089,6 @@
method public void applyTransportModeTransform(@NonNull java.net.Socket, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
method public void applyTransportModeTransform(@NonNull java.net.DatagramSocket, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
method public void applyTransportModeTransform(@NonNull java.io.FileDescriptor, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
- method @RequiresPermission("android.permission.MANAGE_IPSEC_TUNNELS") public void applyTunnelModeTransform(@NonNull android.net.IpSecManager.IpSecTunnelInterface, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
- method @NonNull @RequiresPermission("android.permission.MANAGE_IPSEC_TUNNELS") public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(@NonNull android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
method @NonNull public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
method @NonNull public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
method public void removeTransportModeTransforms(@NonNull java.net.Socket) throws java.io.IOException;
@@ -25097,12 +25098,6 @@
field public static final int DIRECTION_OUT = 1; // 0x1
}
- public static final class IpSecManager.IpSecTunnelInterface implements java.lang.AutoCloseable {
- method @RequiresPermission("android.permission.MANAGE_IPSEC_TUNNELS") public void addAddress(@NonNull java.net.InetAddress, int) throws java.io.IOException;
- method public void close();
- method @RequiresPermission("android.permission.MANAGE_IPSEC_TUNNELS") public void removeAddress(@NonNull java.net.InetAddress, int) throws java.io.IOException;
- }
-
public static final class IpSecManager.ResourceUnavailableException extends android.util.AndroidException {
}
@@ -29649,6 +29644,8 @@
field @Deprecated public static final String RADIO;
field @Deprecated public static final String SERIAL;
field @NonNull public static final String SKU;
+ field @NonNull public static final String SOC_MANUFACTURER;
+ field @NonNull public static final String SOC_MODEL;
field public static final String[] SUPPORTED_32_BIT_ABIS;
field public static final String[] SUPPORTED_64_BIT_ABIS;
field public static final String[] SUPPORTED_ABIS;
@@ -34328,6 +34325,55 @@
field public static final String PATH_SETTING_INTENT = "intent";
}
+ public final class SimPhonebookContract {
+ field public static final String AUTHORITY = "com.android.simphonebook";
+ field @NonNull public static final android.net.Uri AUTHORITY_URI;
+ }
+
+ public static final class SimPhonebookContract.ElementaryFiles {
+ method @NonNull public static android.net.Uri getItemUri(int, int);
+ field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sim-elementary-file";
+ field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/sim-elementary-file";
+ field @NonNull public static final android.net.Uri CONTENT_URI;
+ field public static final int EF_ADN = 1; // 0x1
+ field public static final int EF_FDN = 2; // 0x2
+ field public static final int EF_SDN = 3; // 0x3
+ field public static final String EF_TYPE = "ef_type";
+ field public static final int EF_UNKNOWN = 0; // 0x0
+ field public static final String MAX_RECORDS = "max_records";
+ field public static final String NAME_MAX_LENGTH = "name_max_length";
+ field public static final String PHONE_NUMBER_MAX_LENGTH = "phone_number_max_length";
+ field public static final String RECORD_COUNT = "record_count";
+ field public static final String SLOT_INDEX = "slot_index";
+ field public static final String SUBSCRIPTION_ID = "subscription_id";
+ }
+
+ public static final class SimPhonebookContract.SimRecords {
+ method @NonNull public static android.net.Uri getContentUri(int, int);
+ method @NonNull public static android.net.Uri getItemUri(int, int, int);
+ method @NonNull @WorkerThread public static android.provider.SimPhonebookContract.SimRecords.NameValidationResult validateName(@NonNull android.content.ContentResolver, int, int, @NonNull String);
+ field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sim-contact_v2";
+ field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/sim-contact_v2";
+ field public static final String ELEMENTARY_FILE_TYPE = "elementary_file_type";
+ field public static final String NAME = "name";
+ field public static final String PHONE_NUMBER = "phone_number";
+ field public static final String RECORD_NUMBER = "record_number";
+ field public static final String SUBSCRIPTION_ID = "subscription_id";
+ }
+
+ public static final class SimPhonebookContract.SimRecords.NameValidationResult implements android.os.Parcelable {
+ ctor public SimPhonebookContract.SimRecords.NameValidationResult(@NonNull String, @NonNull String, int, int);
+ method public int describeContents();
+ method public int getEncodedLength();
+ method public int getMaxEncodedLength();
+ method @NonNull public String getName();
+ method @NonNull public String getSanitizedName();
+ method public boolean isSupportedCharacter(int);
+ method public boolean isValid();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.provider.SimPhonebookContract.SimRecords.NameValidationResult> CREATOR;
+ }
+
public class SyncStateContract {
ctor public SyncStateContract();
}
@@ -36183,9 +36229,10 @@
package android.security.keystore {
public class BackendBusyException extends java.security.ProviderException {
- ctor public BackendBusyException();
- ctor public BackendBusyException(@NonNull String);
- ctor public BackendBusyException(@NonNull String, @NonNull Throwable);
+ ctor public BackendBusyException(long);
+ ctor public BackendBusyException(long, @NonNull String);
+ ctor public BackendBusyException(long, @NonNull String, @NonNull Throwable);
+ method public long getBackOffHintMillis();
}
public class KeyExpiredException extends java.security.InvalidKeyException {
@@ -36209,6 +36256,7 @@
method @Nullable public java.util.Date getKeyValidityForOriginationEnd();
method @Nullable public java.util.Date getKeyValidityStart();
method @NonNull public String getKeystoreAlias();
+ method public int getMaxUsageCount();
method public int getPurposes();
method @NonNull public String[] getSignaturePaddings();
method public int getUserAuthenticationType();
@@ -36245,6 +36293,7 @@
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityForConsumptionEnd(java.util.Date);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityForOriginationEnd(java.util.Date);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityStart(java.util.Date);
+ method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setMaxUsageCount(int);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setRandomizedEncryptionRequired(boolean);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setSignaturePaddings(java.lang.String...);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUnlockedDeviceRequired(boolean);
@@ -36267,6 +36316,7 @@
method public String getKeystoreAlias();
method public int getOrigin();
method public int getPurposes();
+ method public int getRemainingUsageCount();
method public int getSecurityLevel();
method @NonNull public String[] getSignaturePaddings();
method public int getUserAuthenticationType();
@@ -36323,6 +36373,7 @@
field public static final int ORIGIN_IMPORTED = 2; // 0x2
field public static final int ORIGIN_SECURELY_IMPORTED = 8; // 0x8
field public static final int ORIGIN_UNKNOWN = 4; // 0x4
+ field public static final int PURPOSE_AGREE_KEY = 64; // 0x40
field public static final int PURPOSE_DECRYPT = 2; // 0x2
field public static final int PURPOSE_ENCRYPT = 1; // 0x1
field public static final int PURPOSE_SIGN = 4; // 0x4
@@ -36335,6 +36386,7 @@
field public static final int SECURITY_LEVEL_UNKNOWN_SECURE = -1; // 0xffffffff
field public static final String SIGNATURE_PADDING_RSA_PKCS1 = "PKCS1";
field public static final String SIGNATURE_PADDING_RSA_PSS = "PSS";
+ field public static final int UNRESTRICTED_USAGE_COUNT = -1; // 0xffffffff
}
public final class KeyProtection implements java.security.KeyStore.ProtectionParameter {
@@ -36344,6 +36396,7 @@
method @Nullable public java.util.Date getKeyValidityForConsumptionEnd();
method @Nullable public java.util.Date getKeyValidityForOriginationEnd();
method @Nullable public java.util.Date getKeyValidityStart();
+ method public int getMaxUsageCount();
method public int getPurposes();
method @NonNull public String[] getSignaturePaddings();
method public int getUserAuthenticationType();
@@ -36369,6 +36422,7 @@
method @NonNull public android.security.keystore.KeyProtection.Builder setKeyValidityForConsumptionEnd(java.util.Date);
method @NonNull public android.security.keystore.KeyProtection.Builder setKeyValidityForOriginationEnd(java.util.Date);
method @NonNull public android.security.keystore.KeyProtection.Builder setKeyValidityStart(java.util.Date);
+ method @NonNull public android.security.keystore.KeyProtection.Builder setMaxUsageCount(int);
method @NonNull public android.security.keystore.KeyProtection.Builder setRandomizedEncryptionRequired(boolean);
method @NonNull public android.security.keystore.KeyProtection.Builder setSignaturePaddings(java.lang.String...);
method @NonNull public android.security.keystore.KeyProtection.Builder setUnlockedDeviceRequired(boolean);
@@ -38502,7 +38556,9 @@
method public void onStateChanged(int);
method public void onStopDtmfTone();
method public void onStopRtt();
+ method public void onTrackedByNonUiService(boolean);
method public void onUnhold();
+ method public void onUsingAlternativeUi(boolean);
method public static String propertiesToString(int);
method public final void putExtras(@NonNull android.os.Bundle);
method public final void removeExtras(java.util.List<java.lang.String>);
@@ -38840,6 +38896,7 @@
field public static final int CAPABILITY_VIDEO_CALLING = 8; // 0x8
field public static final int CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE = 256; // 0x100
field @NonNull public static final android.os.Parcelable.Creator<android.telecom.PhoneAccount> CREATOR;
+ field public static final String EXTRA_ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE = "android.telecom.extra.ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE";
field public static final String EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE = "android.telecom.extra.ALWAYS_USE_VOIP_AUDIO_MODE";
field public static final String EXTRA_CALL_SUBJECT_CHARACTER_ENCODING = "android.telecom.extra.CALL_SUBJECT_CHARACTER_ENCODING";
field public static final String EXTRA_CALL_SUBJECT_MAX_LENGTH = "android.telecom.extra.CALL_SUBJECT_MAX_LENGTH";
@@ -39679,6 +39736,7 @@
field public static final String KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL = "ims.enable_presence_group_subscribe_bool";
field public static final String KEY_ENABLE_PRESENCE_PUBLISH_BOOL = "ims.enable_presence_publish_bool";
field public static final String KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL = "ims.ims_single_registration_required_bool";
+ field public static final String KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT = "ims.non_rcs_capabilities_cache_expiration_sec_int";
field public static final String KEY_PREFIX = "ims.";
field public static final String KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL = "ims.rcs_bulk_capability_exchange_bool";
field public static final String KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT = "ims.wifi_off_deferring_time_millis_int";
@@ -40272,6 +40330,7 @@
field public static final int SERVICE_OPTION_OUT_OF_ORDER = 34; // 0x22
field public static final int SIGNAL_LOST = -3; // 0xfffffffd
field public static final int SIM_CARD_CHANGED = 2043; // 0x7fb
+ field public static final int SLICE_REJECTED = 2252; // 0x8cc
field public static final int SYNCHRONIZATION_FAILURE = 2184; // 0x888
field public static final int TEST_LOOPBACK_REGULAR_DEACTIVATION = 2196; // 0x894
field public static final int TETHERED_CALL_ACTIVE = -6; // 0xfffffffa
@@ -41154,7 +41213,9 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void removeSubscriptionsFromGroup(@NonNull java.util.List<java.lang.Integer>, @NonNull android.os.ParcelUuid);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunistic(boolean, int);
method public void setSubscriptionOverrideCongested(int, boolean, long);
+ method public void setSubscriptionOverrideCongested(int, boolean, @NonNull int[], long);
method public void setSubscriptionOverrideUnmetered(int, boolean, long);
+ method public void setSubscriptionOverrideUnmetered(int, boolean, @NonNull int[], long);
method public void setSubscriptionPlans(int, @NonNull java.util.List<android.telephony.SubscriptionPlan>);
method @RequiresPermission("android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS") public void switchToSubscription(int, @NonNull android.app.PendingIntent);
field public static final String ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED = "android.telephony.action.DEFAULT_SMS_SUBSCRIPTION_CHANGED";
@@ -41712,6 +41773,14 @@
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.euicc.DownloadableSubscription> CREATOR;
}
+ public static final class DownloadableSubscription.Builder {
+ ctor public DownloadableSubscription.Builder(@NonNull android.telephony.euicc.DownloadableSubscription);
+ ctor public DownloadableSubscription.Builder(@NonNull String);
+ method @NonNull public android.telephony.euicc.DownloadableSubscription build();
+ method @NonNull public android.telephony.euicc.DownloadableSubscription.Builder setConfirmationCode(@NonNull String);
+ method @NonNull public android.telephony.euicc.DownloadableSubscription.Builder setEncodedActivationCode(@NonNull String);
+ }
+
public final class EuiccInfo implements android.os.Parcelable {
ctor public EuiccInfo(@Nullable String);
method public int describeContents();
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index bc4a3ca..ab9799f 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -10,10 +10,27 @@
package android.net {
+ public final class ConnectivityFrameworkInitializer {
+ method public static void registerServiceWrappers();
+ }
+
public class ConnectivityManager {
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @Nullable android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle);
}
+ public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable {
+ method public int getResourceId();
+ }
+
+ public final class NetworkAgentConfig implements android.os.Parcelable {
+ method @Nullable public String getSubscriberId();
+ }
+
+ public static final class NetworkAgentConfig.Builder {
+ method @NonNull public android.net.NetworkAgentConfig.Builder setSubscriberId(@Nullable String);
+ }
+
public final class NetworkCapabilities implements android.os.Parcelable {
field public static final int TRANSPORT_TEST = 7; // 0x7
}
@@ -44,6 +61,16 @@
method public void teardownTestNetwork(@NonNull android.net.Network);
}
+ public final class UnderlyingNetworkInfo implements android.os.Parcelable {
+ ctor public UnderlyingNetworkInfo(int, @NonNull String, @NonNull java.util.List<java.lang.String>);
+ method public int describeContents();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.UnderlyingNetworkInfo> CREATOR;
+ field @NonNull public final String iface;
+ field public final int ownerUid;
+ field @NonNull public final java.util.List<java.lang.String> underlyingIfaces;
+ }
+
}
package android.os {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index ea23709..fa712ba 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -201,6 +201,7 @@
field public static final String REQUEST_NETWORK_SCORES = "android.permission.REQUEST_NETWORK_SCORES";
field public static final String REQUEST_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE";
field public static final String RESET_PASSWORD = "android.permission.RESET_PASSWORD";
+ field public static final String RESTART_WIFI_SUBSYSTEM = "android.permission.RESTART_WIFI_SUBSYSTEM";
field public static final String RESTORE_RUNTIME_PERMISSIONS = "android.permission.RESTORE_RUNTIME_PERMISSIONS";
field public static final String RESTRICTED_VR_ACCESS = "android.permission.RESTRICTED_VR_ACCESS";
field public static final String RETRIEVE_WINDOW_CONTENT = "android.permission.RETRIEVE_WINDOW_CONTENT";
@@ -1414,8 +1415,10 @@
package android.apphibernation {
public final class AppHibernationManager {
- method public boolean isHibernating(@NonNull String);
- method public void setHibernating(@NonNull String, boolean);
+ method public boolean isHibernatingForUser(@NonNull String);
+ method public boolean isHibernatingGlobally(@NonNull String);
+ method public void setHibernatingForUser(@NonNull String, boolean);
+ method public void setHibernatingGlobally(@NonNull String, boolean);
}
}
@@ -2128,6 +2131,7 @@
field public static final String FEATURE_INCREMENTAL_DELIVERY = "android.software.incremental_delivery";
field public static final String FEATURE_REBOOT_ESCROW = "android.hardware.reboot_escrow";
field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock";
+ field public static final String FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION = "android.hardware.telephony.ims.singlereg";
field public static final int FLAGS_PERMISSION_RESERVED_PERMISSION_CONTROLLER = -268435456; // 0xf0000000
field public static final int FLAG_PERMISSION_APPLY_RESTRICTION = 16384; // 0x4000
field public static final int FLAG_PERMISSION_AUTO_REVOKED = 131072; // 0x20000
@@ -6130,11 +6134,15 @@
}
public final class IpSecManager {
- method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(@NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
+ method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void applyTunnelModeTransform(@NonNull android.net.IpSecManager.IpSecTunnelInterface, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
+ method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(@NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
}
public static final class IpSecManager.IpSecTunnelInterface implements java.lang.AutoCloseable {
+ method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void addAddress(@NonNull java.net.InetAddress, int) throws java.io.IOException;
+ method public void close();
method @NonNull public String getInterfaceName();
+ method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void removeAddress(@NonNull java.net.InetAddress, int) throws java.io.IOException;
}
public static class IpSecTransform.Builder {
@@ -6279,6 +6287,7 @@
method @Nullable public String getSsid();
method @NonNull public int[] getTransportTypes();
method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities);
+ field public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28; // 0x1c
field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16
field public static final int NET_CAPABILITY_OEM_PRIVATE = 26; // 0x1a
field public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24; // 0x18
@@ -7584,6 +7593,7 @@
method @NonNull public static String formatUid(int);
method public static int getAppId(int);
method public int getIdentifier();
+ method public static int getUid(@NonNull android.os.UserHandle, int);
method @Deprecated public boolean isOwner();
method public boolean isSystem();
method public static int myUserId();
@@ -8281,6 +8291,24 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE) public static boolean putString(@NonNull android.content.ContentResolver, @NonNull String, @Nullable String, boolean);
}
+ public final class SimPhonebookContract {
+ method @NonNull public static String getEfUriPath(int);
+ field public static final String SUBSCRIPTION_ID_PATH_SEGMENT = "subid";
+ }
+
+ public static final class SimPhonebookContract.ElementaryFiles {
+ field public static final String EF_ADN_PATH_SEGMENT = "adn";
+ field public static final String EF_FDN_PATH_SEGMENT = "fdn";
+ field public static final String EF_SDN_PATH_SEGMENT = "sdn";
+ field public static final String ELEMENTARY_FILES_PATH_SEGMENT = "elementary_files";
+ }
+
+ public static final class SimPhonebookContract.SimRecords {
+ field public static final String EXTRA_NAME_VALIDATION_RESULT = "android.provider.extra.NAME_VALIDATION_RESULT";
+ field public static final String QUERY_ARG_PIN2 = "android:query-arg-pin2";
+ field public static final String VALIDATE_NAME_PATH_SEGMENT = "validate_name";
+ }
+
public static final class Telephony.Carriers implements android.provider.BaseColumns {
field public static final String APN_SET_ID = "apn_set_id";
field public static final int CARRIER_EDITED = 4; // 0x4
@@ -8408,6 +8436,11 @@
method @Deprecated @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUid(int);
}
+ public abstract class KeyProperties {
+ field public static final int NAMESPACE_APPLICATION = -1; // 0xffffffff
+ field public static final int NAMESPACE_WIFI = 102; // 0x66
+ }
+
}
package android.security.keystore.recovery {
@@ -9255,6 +9288,33 @@
field @Deprecated public static final int ROUTE_WIRED_OR_EARPIECE = 5; // 0x5
}
+ public final class BluetoothCallQualityReport implements android.os.Parcelable {
+ method public int describeContents();
+ method @IntRange(from=0) public int getNegativeAcknowledgementCount();
+ method @IntRange(from=0) public int getPacketsNotReceivedCount();
+ method @IntRange(from=0) public int getRetransmittedPacketsCount();
+ method @IntRange(from=0xffffff81, to=20) public int getRssiDbm();
+ method public long getSentTimestampMillis();
+ method public int getSnrDb();
+ method public boolean isChoppyVoice();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telecom.BluetoothCallQualityReport> CREATOR;
+ field public static final String EVENT_BLUETOOTH_CALL_QUALITY_REPORT = "android.telecom.event.BLUETOOTH_CALL_QUALITY_REPORT";
+ field public static final String EXTRA_BLUETOOTH_CALL_QUALITY_REPORT = "android.telecom.extra.BLUETOOTH_CALL_QUALITY_REPORT";
+ }
+
+ public static final class BluetoothCallQualityReport.Builder {
+ ctor public BluetoothCallQualityReport.Builder();
+ method @NonNull public android.telecom.BluetoothCallQualityReport build();
+ method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setChoppyVoice(boolean);
+ method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setNegativeAcknowledgementCount(int);
+ method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setPacketsNotReceivedCount(int);
+ method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setRetransmittedPacketsCount(int);
+ method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setRssiDbm(int);
+ method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setSentTimestampMillis(long);
+ method @NonNull public android.telecom.BluetoothCallQualityReport.Builder setSnrDb(int);
+ }
+
public final class Call {
method @Deprecated public void addListener(android.telecom.Call.Listener);
method public void enterBackgroundAudioProcessing();
@@ -10419,6 +10479,7 @@
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isOpportunisticNetworkEnabled();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isPotentialEmergencyNumber(@NonNull String);
+ method public boolean isRadioInterfaceCapabilitySupported(@NonNull String);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRadioOn();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isTetheringApnRequired();
@@ -10492,6 +10553,7 @@
field public static final int CALL_WAITING_STATUS_ENABLED = 1; // 0x1
field public static final int CALL_WAITING_STATUS_NOT_SUPPORTED = 4; // 0x4
field public static final int CALL_WAITING_STATUS_UNKNOWN_ERROR = 3; // 0x3
+ field public static final String CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE = "CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE";
field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
@@ -10713,6 +10775,7 @@
method public int getPduSessionId();
method public int getProtocolType();
method public long getRetryDurationMillis();
+ method @Nullable public android.telephony.data.SliceInfo getSliceInfo();
method @Deprecated public int getSuggestedRetryTime();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.DataCallResponse> CREATOR;
@@ -10747,6 +10810,7 @@
method @NonNull public android.telephony.data.DataCallResponse.Builder setPduSessionId(int);
method @NonNull public android.telephony.data.DataCallResponse.Builder setProtocolType(int);
method @NonNull public android.telephony.data.DataCallResponse.Builder setRetryDurationMillis(long);
+ method @NonNull public android.telephony.data.DataCallResponse.Builder setSliceInfo(@Nullable android.telephony.data.SliceInfo);
method @Deprecated @NonNull public android.telephony.data.DataCallResponse.Builder setSuggestedRetryTime(int);
}
@@ -10819,7 +10883,7 @@
method public void setDataProfile(@NonNull java.util.List<android.telephony.data.DataProfile>, boolean, @NonNull android.telephony.data.DataServiceCallback);
method public void setInitialAttachApn(@NonNull android.telephony.data.DataProfile, boolean, @NonNull android.telephony.data.DataServiceCallback);
method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @NonNull android.telephony.data.DataServiceCallback);
- method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @IntRange(from=0, to=15) int, @NonNull android.telephony.data.DataServiceCallback);
+ method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @IntRange(from=0, to=15) int, @Nullable android.telephony.data.SliceInfo, @NonNull android.telephony.data.DataServiceCallback);
method public void startHandover(int, @NonNull android.telephony.data.DataServiceCallback);
}
@@ -10867,6 +10931,32 @@
method public final void updateQualifiedNetworkTypes(int, @NonNull java.util.List<java.lang.Integer>);
}
+ public final class SliceInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @IntRange(from=android.telephony.data.SliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.SliceInfo.MAX_SLICE_DIFFERENTIATOR) public int getMappedHplmnSliceDifferentiator();
+ method public int getMappedHplmnSliceServiceType();
+ method @IntRange(from=android.telephony.data.SliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.SliceInfo.MAX_SLICE_DIFFERENTIATOR) public int getSliceDifferentiator();
+ method public int getSliceServiceType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.SliceInfo> CREATOR;
+ field public static final int MAX_SLICE_DIFFERENTIATOR = 16777214; // 0xfffffe
+ field public static final int MIN_SLICE_DIFFERENTIATOR = -1; // 0xffffffff
+ field public static final int SLICE_DIFFERENTIATOR_NO_SLICE = -1; // 0xffffffff
+ field public static final int SLICE_SERVICE_TYPE_EMBB = 1; // 0x1
+ field public static final int SLICE_SERVICE_TYPE_MIOT = 3; // 0x3
+ field public static final int SLICE_SERVICE_TYPE_NONE = 0; // 0x0
+ field public static final int SLICE_SERVICE_TYPE_URLLC = 2; // 0x2
+ }
+
+ public static final class SliceInfo.Builder {
+ ctor public SliceInfo.Builder();
+ method @NonNull public android.telephony.data.SliceInfo build();
+ method @NonNull public android.telephony.data.SliceInfo.Builder setMappedHplmnSliceDifferentiator(@IntRange(from=android.telephony.data.SliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.SliceInfo.MAX_SLICE_DIFFERENTIATOR) int);
+ method @NonNull public android.telephony.data.SliceInfo.Builder setMappedHplmnSliceServiceType(int);
+ method @NonNull public android.telephony.data.SliceInfo.Builder setSliceDifferentiator(@IntRange(from=android.telephony.data.SliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.SliceInfo.MAX_SLICE_DIFFERENTIATOR) int);
+ method @NonNull public android.telephony.data.SliceInfo.Builder setSliceServiceType(int);
+ }
+
}
package android.telephony.euicc {
@@ -10878,12 +10968,8 @@
public static final class DownloadableSubscription.Builder {
ctor public DownloadableSubscription.Builder();
- ctor public DownloadableSubscription.Builder(android.telephony.euicc.DownloadableSubscription);
- method public android.telephony.euicc.DownloadableSubscription build();
- method public android.telephony.euicc.DownloadableSubscription.Builder setAccessRules(java.util.List<android.telephony.UiccAccessRule>);
- method public android.telephony.euicc.DownloadableSubscription.Builder setCarrierName(String);
- method public android.telephony.euicc.DownloadableSubscription.Builder setConfirmationCode(String);
- method public android.telephony.euicc.DownloadableSubscription.Builder setEncodedActivationCode(String);
+ method @NonNull public android.telephony.euicc.DownloadableSubscription.Builder setAccessRules(@NonNull java.util.List<android.telephony.UiccAccessRule>);
+ method @NonNull public android.telephony.euicc.DownloadableSubscription.Builder setCarrierName(@NonNull String);
}
public class EuiccCardManager {
@@ -11753,11 +11839,100 @@
field public static final String RCS_PROFILE_2_3 = "UP_2.3";
}
+ public final class RcsContactPresenceTuple implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.net.Uri getContactUri();
+ method @Nullable public android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities getServiceCapabilities();
+ method @Nullable public String getServiceDescription();
+ method @NonNull public String getServiceId();
+ method @NonNull public String getServiceVersion();
+ method @NonNull public String getStatus();
+ method @Nullable public String getTimestamp();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsContactPresenceTuple> CREATOR;
+ field public static final String SERVICE_ID_CALL_COMPOSER = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.callcomposer";
+ field public static final String SERVICE_ID_CHATBOT = "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.chatbot";
+ field public static final String SERVICE_ID_CHATBOT_ROLE = "org.gsma.rcs.isbot";
+ field public static final String SERVICE_ID_CHATBOT_STANDALONE = " org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.chatbot.sa";
+ field public static final String SERVICE_ID_CHAT_V1 = "org.openmobilealliance:IM-session";
+ field public static final String SERVICE_ID_CHAT_V2 = "org.openmobilealliance:ChatSession";
+ field public static final String SERVICE_ID_FT = "org.openmobilealliance:File-Transfer-HTTP";
+ field public static final String SERVICE_ID_FT_OVER_SMS = "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.ftsms";
+ field public static final String SERVICE_ID_GEO_PUSH = "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.geopush";
+ field public static final String SERVICE_ID_GEO_PUSH_VIA_SMS = "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.geosms";
+ field public static final String SERVICE_ID_MMTEL = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.mmtel";
+ field public static final String SERVICE_ID_POST_CALL = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.callunanswered";
+ field public static final String SERVICE_ID_SHARED_MAP = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.sharedmap";
+ field public static final String SERVICE_ID_SHARED_SKETCH = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.sharedsketch";
+ field public static final String TUPLE_BASIC_STATUS_CLOSED = "closed";
+ field public static final String TUPLE_BASIC_STATUS_OPEN = "open";
+ }
+
+ public static final class RcsContactPresenceTuple.Builder {
+ ctor public RcsContactPresenceTuple.Builder(@NonNull String, @NonNull String, @NonNull String);
+ method @NonNull public android.telephony.ims.RcsContactPresenceTuple build();
+ method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setContactUri(@NonNull android.net.Uri);
+ method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setServiceCapabilities(@NonNull android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities);
+ method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setServiceDescription(@NonNull String);
+ method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setTimestamp(@NonNull String);
+ }
+
+ public static final class RcsContactPresenceTuple.ServiceCapabilities implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.List<java.lang.String> getSupportedDuplexModes();
+ method @NonNull public java.util.List<java.lang.String> getUnsupportedDuplexModes();
+ method public boolean isAudioCapable();
+ method public boolean isVideoCapable();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities> CREATOR;
+ field public static final String DUPLEX_MODE_FULL = "full";
+ field public static final String DUPLEX_MODE_HALF = "half";
+ field public static final String DUPLEX_MODE_RECEIVE_ONLY = "receive-only";
+ field public static final String DUPLEX_MODE_SEND_ONLY = "send-only";
+ }
+
+ public static final class RcsContactPresenceTuple.ServiceCapabilities.Builder {
+ ctor public RcsContactPresenceTuple.ServiceCapabilities.Builder(boolean, boolean);
+ method @NonNull public android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities.Builder addSupportedDuplexMode(@NonNull String);
+ method @NonNull public android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities.Builder addUnsupportedDuplexMode(@NonNull String);
+ method @NonNull public android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities build();
+ }
+
+ public final class RcsContactUceCapability implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getCapabilityMechanism();
+ method @Nullable public android.telephony.ims.RcsContactPresenceTuple getCapabilityTuple(@NonNull String);
+ method @NonNull public java.util.List<android.telephony.ims.RcsContactPresenceTuple> getCapabilityTuples();
+ method @NonNull public android.net.Uri getContactUri();
+ method public int getRequestResult();
+ method public int getSourceType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final int CAPABILITY_MECHANISM_OPTIONS = 2; // 0x2
+ field public static final int CAPABILITY_MECHANISM_PRESENCE = 1; // 0x1
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsContactUceCapability> CREATOR;
+ field public static final int REQUEST_RESULT_FOUND = 3; // 0x3
+ field public static final int REQUEST_RESULT_NOT_FOUND = 2; // 0x2
+ field public static final int REQUEST_RESULT_NOT_ONLINE = 1; // 0x1
+ field public static final int REQUEST_RESULT_UNKNOWN = 0; // 0x0
+ field public static final int SOURCE_TYPE_CACHED = 1; // 0x1
+ field public static final int SOURCE_TYPE_NETWORK = 0; // 0x0
+ }
+
+ public static final class RcsContactUceCapability.PresenceBuilder {
+ ctor public RcsContactUceCapability.PresenceBuilder(@NonNull android.net.Uri, int, int);
+ method @NonNull public android.telephony.ims.RcsContactUceCapability.PresenceBuilder addCapabilityTuple(@NonNull android.telephony.ims.RcsContactPresenceTuple);
+ method @NonNull public android.telephony.ims.RcsContactUceCapability.PresenceBuilder addCapabilityTuples(@NonNull java.util.List<android.telephony.ims.RcsContactPresenceTuple>);
+ method @NonNull public android.telephony.ims.RcsContactUceCapability build();
+ }
+
public class RcsUceAdapter {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void addOnPublishStateChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.OnPublishStateChangedListener) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getUcePublishState() throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void removeOnPublishStateChangedListener(@NonNull android.telephony.ims.RcsUceAdapter.OnPublishStateChangedListener) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestAvailability(@NonNull android.net.Uri, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void requestCapabilities(@NonNull java.util.List<android.net.Uri>, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
+ field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
field public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 1; // 0x1
field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 7; // 0x7
field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 6; // 0x6
@@ -11770,6 +11945,18 @@
field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED = 11; // 0xb
field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN = 8; // 0x8
field public static final int CAPABILITY_UPDATE_TRIGGER_UNKNOWN = 0; // 0x0
+ field public static final int ERROR_FORBIDDEN = 6; // 0x6
+ field public static final int ERROR_GENERIC_FAILURE = 1; // 0x1
+ field public static final int ERROR_INSUFFICIENT_MEMORY = 10; // 0xa
+ field public static final int ERROR_LOST_NETWORK = 11; // 0xb
+ field public static final int ERROR_NOT_AUTHORIZED = 5; // 0x5
+ field public static final int ERROR_NOT_AVAILABLE = 3; // 0x3
+ field public static final int ERROR_NOT_ENABLED = 2; // 0x2
+ field public static final int ERROR_NOT_FOUND = 7; // 0x7
+ field public static final int ERROR_NOT_REGISTERED = 4; // 0x4
+ field public static final int ERROR_REQUEST_TIMEOUT = 9; // 0x9
+ field public static final int ERROR_REQUEST_TOO_LARGE = 8; // 0x8
+ field public static final int ERROR_SERVER_UNAVAILABLE = 12; // 0xc
field public static final int PUBLISH_STATE_NOT_PUBLISHED = 2; // 0x2
field public static final int PUBLISH_STATE_OK = 1; // 0x1
field public static final int PUBLISH_STATE_OTHER_ERROR = 6; // 0x6
@@ -11778,6 +11965,12 @@
field public static final int PUBLISH_STATE_VOICE_PROVISION_ERROR = 3; // 0x3
}
+ public static interface RcsUceAdapter.CapabilitiesCallback {
+ method public void onCapabilitiesReceived(@NonNull java.util.List<android.telephony.ims.RcsContactUceCapability>);
+ method public void onComplete();
+ method public void onError(int, long);
+ }
+
public static interface RcsUceAdapter.OnPublishStateChangedListener {
method public void onPublishStateChange(int);
}
@@ -11821,6 +12014,7 @@
field public static final String IPTYPE_IPV6 = "IPV6";
field public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING = "sip_config_auhentication_header_string";
field public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING = "sip_config_authentication_nonce_string";
+ field public static final String KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING = "sip_config_cellular_network_info_header_string";
field public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string";
field public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string";
field public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string";
@@ -11853,6 +12047,7 @@
field public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT = "sip_config_ue_public_port_with_nat_int";
field public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING = "sip_config_ue_public_user_id_string";
field public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING = "sip_config_uri_user_part_string";
+ field public static final String KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING = "sip_config_sip_user_agent_header_string";
field public static final String SIP_TRANSPORT_TCP = "TCP";
field public static final String SIP_TRANSPORT_UDP = "UDP";
}
@@ -12188,6 +12383,7 @@
public class RcsCapabilityExchangeImplBase {
ctor public RcsCapabilityExchangeImplBase(@NonNull java.util.concurrent.Executor);
method public void publishCapabilities(@NonNull String, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.PublishResponseCallback);
+ method public void subscribeForCapabilities(@NonNull java.util.List<android.net.Uri>, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.SubscribeResponseCallback);
field public static final int COMMAND_CODE_FETCH_ERROR = 3; // 0x3
field public static final int COMMAND_CODE_GENERIC_FAILURE = 1; // 0x1
field public static final int COMMAND_CODE_INSUFFICIENT_MEMORY = 5; // 0x5
@@ -12204,6 +12400,16 @@
public static interface RcsCapabilityExchangeImplBase.PublishResponseCallback {
method public void onCommandError(int) throws android.telephony.ims.ImsException;
method public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException;
+ method public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String, @IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException;
+ }
+
+ public static interface RcsCapabilityExchangeImplBase.SubscribeResponseCallback {
+ method public void onCommandError(int) throws android.telephony.ims.ImsException;
+ method public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException;
+ method public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String, @IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException;
+ method public void onNotifyCapabilitiesUpdate(@NonNull java.util.List<java.lang.String>) throws android.telephony.ims.ImsException;
+ method public void onResourceTerminated(@NonNull java.util.List<android.util.Pair<android.net.Uri,java.lang.String>>) throws android.telephony.ims.ImsException;
+ method public void onTerminated(@NonNull String, long) throws android.telephony.ims.ImsException;
}
public interface SipDelegate {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 546e72b..668b588 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -18,6 +18,7 @@
field public static final String NETWORK_SETTINGS = "android.permission.NETWORK_SETTINGS";
field public static final String NETWORK_STACK = "android.permission.NETWORK_STACK";
field public static final String OVERRIDE_DISPLAY_MODE_REQUESTS = "android.permission.OVERRIDE_DISPLAY_MODE_REQUESTS";
+ field public static final String QUERY_USERS = "android.permission.QUERY_USERS";
field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
field public static final String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE";
field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java
index e7b3e14..69d5c8d 100644
--- a/core/java/android/app/ApplicationExitInfo.java
+++ b/core/java/android/app/ApplicationExitInfo.java
@@ -50,7 +50,7 @@
*
* <p>
* Application process could die for many reasons, for example {@link #REASON_LOW_MEMORY}
- * when it was killed by the ystem because it was running low on memory. Reason
+ * when it was killed by the system because it was running low on memory. Reason
* of the death can be retrieved via {@link #getReason}. Besides the reason, there are a few other
* auxiliary APIs like {@link #getStatus} and {@link #getImportance} to help the caller with
* additional diagnostic information.
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index afa1560..1ff64db 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -47,8 +47,19 @@
per-file *Zen* = file:/packages/SystemUI/OWNERS
per-file *StatusBar* = file:/packages/SystemUI/OWNERS
+# PackageManager
+per-file ApplicationPackageManager.java = file:/services/core/java/com/android/server/pm/OWNERS
+per-file InstantAppResolverService.java = file:/services/core/java/com/android/server/pm/OWNERS
+per-file LoadedApk.java = file:/services/core/java/com/android/server/pm/OWNERS
+per-file PackageDeleteObserver.java = file:/services/core/java/com/android/server/pm/OWNERS
+per-file PackageInstallObserver.java = file:/services/core/java/com/android/server/pm/OWNERS
+per-file EphemeralResolveInfo.aidl = file:/services/core/java/com/android/server/pm/OWNERS
+per-file IEphemeralResolver.aidl = file:/services/core/java/com/android/server/pm/OWNERS
+per-file IInstantAppResolver.aidl = file:/services/core/java/com/android/server/pm/OWNERS
+per-file InstantAppResolveInfo.aidl = file:/services/core/java/com/android/server/pm/OWNERS
+
# ResourcesManager
-per-file ResourcesManager = rtmitchell@google.com, toddke@google.com
+per-file ResourcesManager.java = file:RESOURCES_OWNERS
# VoiceInteraction
per-file *VoiceInteract* = file:/core/java/android/service/voice/OWNERS
diff --git a/core/java/android/app/RESOURCES_OWNERS b/core/java/android/app/RESOURCES_OWNERS
new file mode 100644
index 0000000..21c39a8
--- /dev/null
+++ b/core/java/android/app/RESOURCES_OWNERS
@@ -0,0 +1,2 @@
+rtmitchell@google.com
+toddke@google.com
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 82e48bf..d151526 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -111,21 +111,16 @@
import android.media.tv.TvInputManager;
import android.media.tv.tunerresourcemanager.ITunerResourceManager;
import android.media.tv.tunerresourcemanager.TunerResourceManager;
-import android.net.ConnectivityDiagnosticsManager;
-import android.net.ConnectivityManager;
+import android.net.ConnectivityFrameworkInitializer;
import android.net.EthernetManager;
-import android.net.IConnectivityManager;
import android.net.IEthernetManager;
import android.net.IIpSecService;
import android.net.INetworkPolicyManager;
-import android.net.ITestNetworkManager;
import android.net.IpSecManager;
import android.net.NetworkPolicyManager;
import android.net.NetworkScoreManager;
import android.net.NetworkWatchlistManager;
-import android.net.TestNetworkManager;
import android.net.TetheringManager;
-import android.net.VpnManager;
import android.net.lowpan.ILowpanManager;
import android.net.lowpan.LowpanManager;
import android.net.nsd.INsdManager;
@@ -154,7 +149,6 @@
import android.os.IncidentManager;
import android.os.PowerManager;
import android.os.RecoverySystem;
-import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.os.StatsFrameworkInitializer;
@@ -349,15 +343,6 @@
// (which extends it).
SYSTEM_SERVICE_NAMES.put(android.text.ClipboardManager.class, Context.CLIPBOARD_SERVICE);
- registerService(Context.CONNECTIVITY_SERVICE, ConnectivityManager.class,
- new StaticApplicationContextServiceFetcher<ConnectivityManager>() {
- @Override
- public ConnectivityManager createService(Context context) throws ServiceNotFoundException {
- IBinder b = ServiceManager.getServiceOrThrow(Context.CONNECTIVITY_SERVICE);
- IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
- return new ConnectivityManager(context, service);
- }});
-
registerService(Context.NETD_SERVICE, IBinder.class, new StaticServiceFetcher<IBinder>() {
@Override
public IBinder createService() throws ServiceNotFoundException {
@@ -391,50 +376,6 @@
return new IpSecManager(ctx, service);
}});
- registerService(Context.VPN_MANAGEMENT_SERVICE, VpnManager.class,
- new CachedServiceFetcher<VpnManager>() {
- @Override
- public VpnManager createService(ContextImpl ctx) throws ServiceNotFoundException {
- IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
- IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
- return new VpnManager(ctx, service);
- }});
-
- registerService(Context.CONNECTIVITY_DIAGNOSTICS_SERVICE,
- ConnectivityDiagnosticsManager.class,
- new CachedServiceFetcher<ConnectivityDiagnosticsManager>() {
- @Override
- public ConnectivityDiagnosticsManager createService(ContextImpl ctx)
- throws ServiceNotFoundException {
- // ConnectivityDiagnosticsManager is backed by ConnectivityService
- IBinder b = ServiceManager.getServiceOrThrow(Context.CONNECTIVITY_SERVICE);
- IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
- return new ConnectivityDiagnosticsManager(ctx, service);
- }});
-
- registerService(
- Context.TEST_NETWORK_SERVICE,
- TestNetworkManager.class,
- new StaticApplicationContextServiceFetcher<TestNetworkManager>() {
- @Override
- public TestNetworkManager createService(Context context)
- throws ServiceNotFoundException {
- IBinder csBinder =
- ServiceManager.getServiceOrThrow(Context.CONNECTIVITY_SERVICE);
- IConnectivityManager csMgr =
- IConnectivityManager.Stub.asInterface(csBinder);
-
- final IBinder tnBinder;
- try {
- tnBinder = csMgr.startOrGetTestNetworkService();
- } catch (RemoteException e) {
- throw new ServiceNotFoundException(Context.TEST_NETWORK_SERVICE);
- }
- ITestNetworkManager tnMgr = ITestNetworkManager.Stub.asInterface(tnBinder);
- return new TestNetworkManager(tnMgr);
- }
- });
-
registerService(Context.COUNTRY_DETECTOR, CountryDetector.class,
new StaticServiceFetcher<CountryDetector>() {
@Override
@@ -1355,6 +1296,7 @@
try {
// Note: the following functions need to be @SystemApis, once they become mainline
// modules.
+ ConnectivityFrameworkInitializer.registerServiceWrappers();
JobSchedulerFrameworkInitializer.registerServiceWrappers();
BlobStoreManagerFrameworkInitializer.initialize();
TelephonyFrameworkInitializer.registerServiceWrappers();
diff --git a/core/java/android/app/smartspace/OWNERS b/core/java/android/app/smartspace/OWNERS
new file mode 100644
index 0000000..19ef9d7
--- /dev/null
+++ b/core/java/android/app/smartspace/OWNERS
@@ -0,0 +1,2 @@
+srazdan@google.com
+alexmang@google.com
\ No newline at end of file
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 1ddfe0d..1d5dc1d 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -28,7 +28,6 @@
import android.net.ConnectivityManager;
import android.net.DataUsageRequest;
import android.net.INetworkStatsService;
-import android.net.NetworkIdentity;
import android.net.NetworkStack;
import android.net.NetworkTemplate;
import android.net.netstats.provider.INetworkStatsProviderCallback;
@@ -47,6 +46,7 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.NetworkIdentityUtils;
import java.util.Objects;
@@ -628,7 +628,7 @@
default:
throw new IllegalArgumentException("Cannot create template for network type "
+ networkType + ", subscriberId '"
- + NetworkIdentity.scrubSubscriberId(subscriberId) + "'.");
+ + NetworkIdentityUtils.scrubSubscriberId(subscriberId) + "'.");
}
return template;
}
diff --git a/core/java/android/apphibernation/AppHibernationManager.java b/core/java/android/apphibernation/AppHibernationManager.java
index 8f1934c..7281d50 100644
--- a/core/java/android/apphibernation/AppHibernationManager.java
+++ b/core/java/android/apphibernation/AppHibernationManager.java
@@ -49,31 +49,61 @@
}
/**
- * Returns true if the package is hibernating, false otherwise.
+ * Returns true if the package is hibernating for this context's user, false otherwise.
*
* @hide
*/
@SystemApi
- public boolean isHibernating(@NonNull String packageName) {
+ public boolean isHibernatingForUser(@NonNull String packageName) {
try {
- return mIAppHibernationService.isHibernating(packageName, mContext.getUserId());
+ return mIAppHibernationService.isHibernatingForUser(packageName, mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Set whether the package is hibernating.
+ * Set whether the package is hibernating for this context's user.
*
* @hide
*/
@SystemApi
- public void setHibernating(@NonNull String packageName, boolean isHibernating) {
+ public void setHibernatingForUser(@NonNull String packageName, boolean isHibernating) {
try {
- mIAppHibernationService.setHibernating(packageName, mContext.getUserId(),
+ mIAppHibernationService.setHibernatingForUser(packageName, mContext.getUserId(),
isHibernating);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Returns true if app is hibernating globally / at the package level.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean isHibernatingGlobally(@NonNull String packageName) {
+ try {
+ return mIAppHibernationService.isHibernatingGlobally(packageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set whether a package should be globally hibernating. This hibernates the package at a
+ * package level. User-level hibernation (e.g.. {@link #isHibernatingForUser} is independent
+ * from global hibernation.
+ *
+ * @hide
+ */
+ @SystemApi
+ public void setHibernatingGlobally(@NonNull String packageName, boolean isHibernating) {
+ try {
+ mIAppHibernationService.setHibernatingGlobally(packageName, isHibernating);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/apphibernation/IAppHibernationService.aidl b/core/java/android/apphibernation/IAppHibernationService.aidl
index db57ecb..6a068ee 100644
--- a/core/java/android/apphibernation/IAppHibernationService.aidl
+++ b/core/java/android/apphibernation/IAppHibernationService.aidl
@@ -21,6 +21,8 @@
* @hide
*/
interface IAppHibernationService {
- boolean isHibernating(String packageName, int userId);
- void setHibernating(String packageName, int userId, boolean isHibernating);
+ boolean isHibernatingForUser(String packageName, int userId);
+ void setHibernatingForUser(String packageName, int userId, boolean isHibernating);
+ boolean isHibernatingGlobally(String packageName);
+ void setHibernatingGlobally(String packageName, boolean isHibernating);
}
\ No newline at end of file
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 36076da..4fb5577 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -80,7 +80,7 @@
/**
* Intent used to broadcast the change in the Audio Connection state of the
- * A2DP profile.
+ * HFP profile.
*
* <p>This intent will have 3 extras:
* <ul>
diff --git a/core/java/android/bluetooth/le/AdvertiseData.java b/core/java/android/bluetooth/le/AdvertiseData.java
index 573b932..fa7ac2b 100644
--- a/core/java/android/bluetooth/le/AdvertiseData.java
+++ b/core/java/android/bluetooth/le/AdvertiseData.java
@@ -44,7 +44,7 @@
@Nullable
private final List<ParcelUuid> mServiceUuids;
- @Nullable
+ @NonNull
private final List<ParcelUuid> mServiceSolicitationUuids;
private final SparseArray<byte[]> mManufacturerSpecificData;
@@ -77,7 +77,7 @@
/**
* Returns a list of service solicitation UUIDs within the advertisement that we invite to connect.
*/
- @Nullable
+ @NonNull
public List<ParcelUuid> getServiceSolicitationUuids() {
return mServiceSolicitationUuids;
}
@@ -221,7 +221,7 @@
public static final class Builder {
@Nullable
private List<ParcelUuid> mServiceUuids = new ArrayList<ParcelUuid>();
- @Nullable
+ @NonNull
private List<ParcelUuid> mServiceSolicitationUuids = new ArrayList<ParcelUuid>();
private SparseArray<byte[]> mManufacturerSpecificData = new SparseArray<byte[]>();
private Map<ParcelUuid, byte[]> mServiceData = new ArrayMap<ParcelUuid, byte[]>();
diff --git a/core/java/android/content/AutofillOptions.java b/core/java/android/content/AutofillOptions.java
index 80a7b16..0581ed5 100644
--- a/core/java/android/content/AutofillOptions.java
+++ b/core/java/android/content/AutofillOptions.java
@@ -17,6 +17,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.app.ActivityThread;
import android.os.Parcel;
@@ -62,6 +63,7 @@
* List of allowlisted activities.
*/
@Nullable
+ @SuppressLint("NullableCollection")
public ArraySet<ComponentName> whitelistedActivitiesForAugmentedAutofill;
/**
@@ -73,6 +75,7 @@
* The disabled Activities of the package. key is component name string, value is when they
* will be enabled.
*/
+ @SuppressLint("NullableCollection")
@Nullable
public ArrayMap<String, Long> disabledActivities;
diff --git a/core/java/android/content/ContentCaptureOptions.java b/core/java/android/content/ContentCaptureOptions.java
index ef49e02..c296bb5 100644
--- a/core/java/android/content/ContentCaptureOptions.java
+++ b/core/java/android/content/ContentCaptureOptions.java
@@ -17,6 +17,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.app.ActivityThread;
import android.os.Parcel;
@@ -73,6 +74,7 @@
* for all acitivites in the package).
*/
@Nullable
+ @SuppressLint("NullableCollection")
public final ArraySet<ComponentName> whitelistedComponents;
/**
@@ -96,6 +98,7 @@
*/
public ContentCaptureOptions(int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs,
int textChangeFlushingFrequencyMs, int logHistorySize,
+ @SuppressLint("NullableCollection")
@Nullable ArraySet<ComponentName> whitelistedComponents) {
this(/* lite= */ false, loggingLevel, maxBufferSize, idleFlushingFrequencyMs,
textChangeFlushingFrequencyMs, logHistorySize, whitelistedComponents);
diff --git a/core/java/android/content/integrity/OWNERS b/core/java/android/content/integrity/OWNERS
new file mode 100644
index 0000000..20c758a
--- /dev/null
+++ b/core/java/android/content/integrity/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 722021
+
+toddke@android.com
+toddke@google.com
+patb@google.com
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 31beb6e..04e0468 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -59,6 +59,7 @@
import android.graphics.Rect;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.Drawable;
+import android.net.ConnectivityManager;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Bundle;
@@ -71,6 +72,12 @@
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.permission.PermissionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.gba.GbaService;
+import android.telephony.ims.ImsService;
+import android.telephony.ims.ProvisioningManager;
+import android.telephony.ims.RcsUceAdapter;
+import android.telephony.ims.SipDelegateManager;
import android.util.AndroidException;
import android.util.Log;
@@ -2602,6 +2609,37 @@
public static final String FEATURE_TELEPHONY_IMS = "android.hardware.telephony.ims";
/**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device
+ * supports a single IMS registration as defined by carrier networks in the IMS service
+ * implementation using the {@link ImsService} API, {@link GbaService} API, and IRadio 1.6 HAL.
+ * <p>
+ * When set, the device must fully support the following APIs for an application to implement
+ * IMS single registration:
+ * <ul>
+ * <li> Updating RCS provisioning status using the {@link ProvisioningManager} API to supply an
+ * RCC.14 defined XML and notify IMS applications of Auto Configuration Server (ACS) or
+ * proprietary server provisioning updates.</li>
+ * <li>Opening a delegate in the device IMS service to forward SIP traffic to the carrier's
+ * network using the {@link SipDelegateManager} API</li>
+ * <li>Listening to EPS dedicated bearer establishment via the
+ * {@link ConnectivityManager#registerQosCallback}
+ * API to indicate to the application when to start/stop media traffic.</li>
+ * <li>Implementing Generic Bootstrapping Architecture (GBA) and providing the associated
+ * authentication keys to applications
+ * requesting this information via the {@link TelephonyManager#bootstrapAuthenticationRequest}
+ * API</li>
+ * <li>Implementing RCS User Capability Exchange using the {@link RcsUceAdapter} API</li>
+ * </ul>
+ * <p>
+ * This feature should only be defined if {@link #FEATURE_TELEPHONY_IMS} is also defined.
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION =
+ "android.hardware.telephony.ims.singlereg";
+
+ /**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device is capable of communicating with
* other devices via ultra wideband.
@@ -3224,6 +3262,24 @@
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_APP_ENUMERATION = "android.software.app_enumeration";
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has
+ * a Keystore implementation that can only enforce limited use key in hardware with max usage
+ * count equals to 1.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_KEYSTORE_SINGLE_USE_KEY =
+ "android.hardware.keystore.single_use_key";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has
+ * a Keystore implementation that can enforce limited use key in hardware with any max usage
+ * count (including count equals to 1).
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_KEYSTORE_LIMITED_USE_KEY =
+ "android.hardware.keystore.limited_use_key";
+
/** @hide */
public static final boolean APP_ENUMERATION_ENABLED_BY_DEFAULT = true;
@@ -4945,7 +5001,7 @@
*
* @hide
*/
- @SuppressWarnings("HiddenAbstractMethod")
+ @SuppressWarnings({"HiddenAbstractMethod", "NullableCollection"})
@TestApi
public abstract @Nullable String[] getNamesForUids(int[] uids);
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 0ef55f4..3730790 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -954,7 +954,10 @@
/**
* Returns whether the given functions are valid inputs to UsbManager.
- * Currently the empty functions or any of MTP, PTP, RNDIS, MIDI are accepted.
+ * Currently the empty functions or any of MTP, PTP, RNDIS, MIDI, NCM are accepted.
+ *
+ * Only one function may be set at a time, except for RNDIS and NCM, which can be set together
+ * because from a user perspective they are the same function (tethering).
*
* @return Whether the mask is settable.
*
@@ -962,7 +965,9 @@
*/
public static boolean areSettableFunctions(long functions) {
return functions == FUNCTION_NONE
- || ((~SETTABLE_FUNCTIONS & functions) == 0 && Long.bitCount(functions) == 1);
+ || ((~SETTABLE_FUNCTIONS & functions) == 0
+ && ((Long.bitCount(functions) == 1)
+ || (functions == (FUNCTION_RNDIS | FUNCTION_NCM))));
}
/**
diff --git a/core/java/android/inputmethodservice/OWNERS b/core/java/android/inputmethodservice/OWNERS
index e6a04da..d7db7c7 100644
--- a/core/java/android/inputmethodservice/OWNERS
+++ b/core/java/android/inputmethodservice/OWNERS
@@ -2,3 +2,5 @@
set noparent
include /services/core/java/com/android/server/inputmethod/OWNERS
+
+per-file *InlineSuggestion* = file:/core/java/android/service/autofill/OWNERS
diff --git a/core/java/android/net/DataUsageRequest.java b/core/java/android/net/DataUsageRequest.java
index 0ac8f7e7..b06d515 100644
--- a/core/java/android/net/DataUsageRequest.java
+++ b/core/java/android/net/DataUsageRequest.java
@@ -16,6 +16,7 @@
package android.net;
+import android.annotation.Nullable;
import android.net.NetworkTemplate;
import android.os.Parcel;
import android.os.Parcelable;
@@ -95,7 +96,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (obj instanceof DataUsageRequest == false) return false;
DataUsageRequest that = (DataUsageRequest) obj;
return that.requestId == this.requestId
diff --git a/core/java/android/net/DhcpResults.java b/core/java/android/net/DhcpResults.java
index ef0a436..82ba156 100644
--- a/core/java/android/net/DhcpResults.java
+++ b/core/java/android/net/DhcpResults.java
@@ -160,7 +160,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) return true;
if (!(obj instanceof DhcpResults)) return false;
diff --git a/core/java/android/net/INetworkPolicyListener.aidl b/core/java/android/net/INetworkPolicyListener.aidl
index fe9141c..dfb1e99 100644
--- a/core/java/android/net/INetworkPolicyListener.aidl
+++ b/core/java/android/net/INetworkPolicyListener.aidl
@@ -23,6 +23,6 @@
void onMeteredIfacesChanged(in String[] meteredIfaces);
void onRestrictBackgroundChanged(boolean restrictBackground);
void onUidPoliciesChanged(int uid, int uidPolicies);
- void onSubscriptionOverride(int subId, int overrideMask, int overrideValue);
+ void onSubscriptionOverride(int subId, int overrideMask, int overrideValue, in int[] networkTypes);
void onSubscriptionPlansChanged(int subId, in SubscriptionPlan[] plans);
}
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index 29a3fdf..b016ed6 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -76,10 +76,11 @@
SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage);
void setSubscriptionPlans(int subId, in SubscriptionPlan[] plans, String callingPackage);
String getSubscriptionPlansOwner(int subId);
- void setSubscriptionOverride(int subId, int overrideMask, int overrideValue, long timeoutMillis, String callingPackage);
+ void setSubscriptionOverride(int subId, int overrideMask, int overrideValue, in int[] networkTypes, long timeoutMillis, String callingPackage);
void factoryReset(String subscriber);
boolean isUidNetworkingBlocked(int uid, boolean meteredNetwork);
boolean isUidRestrictedOnMeteredNetworks(int uid);
+ boolean checkUidNetworkingBlocked(int uid, int uidRules, boolean isNetworkMetered, boolean isBackgroundRestricted);
}
diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl
index 1a3dc97..0baf11e 100644
--- a/core/java/android/net/INetworkStatsService.aidl
+++ b/core/java/android/net/INetworkStatsService.aidl
@@ -23,11 +23,11 @@
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
+import android.net.UnderlyingNetworkInfo;
import android.net.netstats.provider.INetworkStatsProvider;
import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.os.IBinder;
import android.os.Messenger;
-import com.android.internal.net.VpnInfo;
/** {@hide} */
interface INetworkStatsService {
@@ -70,7 +70,7 @@
in Network[] defaultNetworks,
in NetworkState[] networkStates,
in String activeIface,
- in VpnInfo[] vpnInfos);
+ in UnderlyingNetworkInfo[] underlyingNetworkInfos);
/** Force update of statistics. */
@UnsupportedAppUsage
void forceUpdate();
diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java
index 63fc5f2..183f500 100644
--- a/core/java/android/net/Ikev2VpnProfile.java
+++ b/core/java/android/net/Ikev2VpnProfile.java
@@ -360,7 +360,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (!(obj instanceof Ikev2VpnProfile)) {
return false;
}
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index b6ae7ec..70bca30 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -15,6 +15,8 @@
*/
package android.net;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.NonNull;
@@ -628,7 +630,7 @@
}
/** @hide */
- @VisibleForTesting
+ @SystemApi(client = MODULE_LIBRARIES)
public int getResourceId() {
return mResourceId;
}
@@ -705,7 +707,7 @@
}
/**
- * This class represents an IpSecTunnelInterface.
+ * This class represents an IpSecTunnelInterface
*
* <p>IpSecTunnelInterface objects track tunnel interfaces that serve as
* local endpoints for IPsec tunnels.
@@ -714,7 +716,9 @@
* applied to provide IPsec security to packets sent through the tunnel. While a tunnel
* cannot be used in standalone mode within Android, the higher layers may use the tunnel
* to create Network objects which are accessible to the Android system.
+ * @hide
*/
+ @SystemApi
public static final class IpSecTunnelInterface implements AutoCloseable {
private final String mOpPackageName;
private final IIpSecService mService;
@@ -725,26 +729,23 @@
private String mInterfaceName;
private int mResourceId = INVALID_RESOURCE_ID;
- /**
- * Get the underlying SPI held by this object.
- *
- * @hide
- */
- @SystemApi
+ /** Get the underlying SPI held by this object. */
@NonNull
public String getInterfaceName() {
return mInterfaceName;
}
/**
- * Add an address to the IpSecTunnelInterface.
+ * Add an address to the IpSecTunnelInterface
*
* <p>Add an address which may be used as the local inner address for
* tunneled traffic.
*
* @param address the local address for traffic inside the tunnel
* @param prefixLen length of the InetAddress prefix
+ * @hide
*/
+ @SystemApi
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
@RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
public void addAddress(@NonNull InetAddress address, int prefixLen) throws IOException {
@@ -759,13 +760,15 @@
}
/**
- * Remove an address from the IpSecTunnelInterface.
+ * Remove an address from the IpSecTunnelInterface
*
- * <p>Remove an address which was previously added to the IpSecTunnelInterface.
+ * <p>Remove an address which was previously added to the IpSecTunnelInterface
*
* @param address to be removed
* @param prefixLen length of the InetAddress prefix
+ * @hide
*/
+ @SystemApi
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
@RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
public void removeAddress(@NonNull InetAddress address, int prefixLen) throws IOException {
@@ -816,7 +819,7 @@
}
/**
- * Delete an IpSecTunnelInterface.
+ * Delete an IpSecTunnelInterface
*
* <p>Calling close will deallocate the IpSecTunnelInterface and all of its system
* resources. Any packets bound for this interface either inbound or outbound will
@@ -838,12 +841,7 @@
}
}
-
- /**
- * Check that the Interface was closed properly.
- *
- * @hide
- */
+ /** Check that the Interface was closed properly. */
@Override
protected void finalize() throws Throwable {
if (mCloseGuard != null) {
@@ -875,52 +873,17 @@
* Create a new IpSecTunnelInterface as a local endpoint for tunneled IPsec traffic.
*
* <p>An application that creates tunnels is responsible for cleaning up the tunnel when the
- * underlying network disconnects, and the {@link
- * ConnectivityManager.NetworkCallback#onLost(Network)} callback is received.
+ * underlying network goes away, and the onLost() callback is received.
*
- * @param underlyingNetwork the {@link Network} that will carry traffic for this tunnel. Packets
- * that go through the tunnel will need a underlying network to transit to the IPsec peer.
- * This network should almost certainly be a physical network such as WiFi.
- * @return a new {@link IpSecTunnelInterface} with the specified properties
- * @throws IOException indicating that the tunnel could not be created due to a lower-layer
- * error
- * @throws ResourceUnavailableException indicating that the number of opening tunnels has
- * reached the limit.
- */
- @NonNull
- @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
- @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
- public IpSecTunnelInterface createIpSecTunnelInterface(@NonNull Network underlyingNetwork)
- throws ResourceUnavailableException, IOException {
-
- // TODO: Remove the need for adding two unused addresses with IPsec tunnels when {@link
- // #createIpSecTunnelInterface(localAddress, remoteAddress, underlyingNetwork)} can be
- // safely removed.
- final InetAddress address = InetAddress.getLocalHost();
- return createIpSecTunnelInterface(address, address, underlyingNetwork);
- }
-
- /**
- * Create a new IpSecTunnelInterface as a local endpoint for tunneled IPsec traffic.
- *
- * <p>An application that creates tunnels is responsible for cleaning up the tunnel when the
- * underlying network disconnects, and the {@link
- * ConnectivityManager.NetworkCallback#onLost(Network)} callback is received.
- *
- * @param localAddress The local address of the tunnel
- * @param remoteAddress The local address of the tunnel
- * @param underlyingNetwork the {@link Network} that will carry traffic for this tunnel. Packets
- * that go through the tunnel will need a underlying network to transit to the IPsec peer.
- * This network should almost certainly be a physical network such as WiFi.
- * @return a new {@link IpSecTunnelInterface} with the specified properties
- * @throws IOException indicating that the tunnel could not be created due to a lower-layer
- * error
- * @throws ResourceUnavailableException indicating that the number of opening tunnels has
- * reached the limit.
+ * @param localAddress The local addres of the tunnel
+ * @param remoteAddress The local addres of the tunnel
+ * @param underlyingNetwork the {@link Network} that will carry traffic for this tunnel.
+ * This network should almost certainly be a network such as WiFi with an L2 address.
+ * @return a new {@link IpSecManager#IpSecTunnelInterface} with the specified properties
+ * @throws IOException indicating that the socket could not be opened or bound
+ * @throws ResourceUnavailableException indicating that too many encapsulation sockets are open
* @hide
- * @deprecated Callers should use {@link #createIpSecTunnelInterface(Network)}
*/
- @Deprecated
@SystemApi
@NonNull
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
@@ -944,14 +907,16 @@
* <p>Applications should probably not use this API directly.
*
*
- * @param tunnel The {@link IpSecTunnelInterface} that will use the supplied
+ * @param tunnel The {@link IpSecManager#IpSecTunnelInterface} that will use the supplied
* transform.
- * @param direction the direction, {@link #DIRECTION_OUT} or {@link #DIRECTION_IN} in which
+ * @param direction the direction, {@link DIRECTION_OUT} or {@link #DIRECTION_IN} in which
* the transform will be used.
* @param transform an {@link IpSecTransform} created in tunnel mode
- * @throws IOException indicating that the transform could not be applied due to a lower-layer
- * error
+ * @throws IOException indicating that the transform could not be applied due to a lower
+ * layer failure.
+ * @hide
*/
+ @SystemApi
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
@RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
public void applyTunnelModeTransform(@NonNull IpSecTunnelInterface tunnel,
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index aa7811a..b48c1fd 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
@@ -150,7 +151,7 @@
/**
* Standard equals.
*/
- public boolean equals(Object other) {
+ public boolean equals(@Nullable Object other) {
if (this == other) return true;
if (!(other instanceof IpSecTransform)) return false;
final IpSecTransform rhs = (IpSecTransform) other;
diff --git a/core/java/android/net/MatchAllNetworkSpecifier.java b/core/java/android/net/MatchAllNetworkSpecifier.java
index 70c4a72..9a11be0 100644
--- a/core/java/android/net/MatchAllNetworkSpecifier.java
+++ b/core/java/android/net/MatchAllNetworkSpecifier.java
@@ -17,6 +17,7 @@
package android.net;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -31,17 +32,6 @@
*/
@SystemApi
public final class MatchAllNetworkSpecifier extends NetworkSpecifier implements Parcelable {
- /**
- * Utility method which verifies that the ns argument is not a MatchAllNetworkSpecifier and
- * throws an IllegalArgumentException if it is.
- * @hide
- */
- public static void checkNotMatchAllNetworkSpecifier(NetworkSpecifier ns) {
- if (ns instanceof MatchAllNetworkSpecifier) {
- throw new IllegalArgumentException("A MatchAllNetworkSpecifier is not permitted");
- }
- }
-
/** @hide */
@Override
public boolean canBeSatisfiedBy(NetworkSpecifier other) {
@@ -55,7 +45,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
return o instanceof MatchAllNetworkSpecifier;
}
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index b644ed5..5d8122b 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -18,14 +18,16 @@
import static android.net.ConnectivityManager.TYPE_WIFI;
+import android.annotation.Nullable;
import android.content.Context;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
-import android.os.Build;
import android.service.NetworkIdentityProto;
import android.telephony.Annotation.NetworkType;
import android.util.proto.ProtoOutputStream;
+import com.android.net.module.util.NetworkIdentityUtils;
+
import java.util.Objects;
/**
@@ -66,7 +68,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (obj instanceof NetworkIdentity) {
final NetworkIdentity ident = (NetworkIdentity) obj;
return mType == ident.mType && mSubType == ident.mSubType && mRoaming == ident.mRoaming
@@ -89,7 +91,8 @@
builder.append(mSubType);
}
if (mSubscriberId != null) {
- builder.append(", subscriberId=").append(scrubSubscriberId(mSubscriberId));
+ builder.append(", subscriberId=")
+ .append(NetworkIdentityUtils.scrubSubscriberId(mSubscriberId));
}
if (mNetworkId != null) {
builder.append(", networkId=").append(mNetworkId);
@@ -110,7 +113,8 @@
// Not dumping mSubType, subtypes are no longer supported.
if (mSubscriberId != null) {
- proto.write(NetworkIdentityProto.SUBSCRIBER_ID, scrubSubscriberId(mSubscriberId));
+ proto.write(NetworkIdentityProto.SUBSCRIBER_ID,
+ NetworkIdentityUtils.scrubSubscriberId(mSubscriberId));
}
proto.write(NetworkIdentityProto.NETWORK_ID, mNetworkId);
proto.write(NetworkIdentityProto.ROAMING, mRoaming);
@@ -149,32 +153,6 @@
}
/**
- * Scrub given IMSI on production builds.
- */
- public static String scrubSubscriberId(String subscriberId) {
- if (Build.IS_ENG) {
- return subscriberId;
- } else if (subscriberId != null) {
- // TODO: parse this as MCC+MNC instead of hard-coding
- return subscriberId.substring(0, Math.min(6, subscriberId.length())) + "...";
- } else {
- return "null";
- }
- }
-
- /**
- * Scrub given IMSI on production builds.
- */
- public static String[] scrubSubscriberId(String[] subscriberId) {
- if (subscriberId == null) return null;
- final String[] res = new String[subscriberId.length];
- for (int i = 0; i < res.length; i++) {
- res[i] = NetworkIdentity.scrubSubscriberId(subscriberId[i]);
- }
- return res;
- }
-
- /**
* Build a {@link NetworkIdentity} from the given {@link NetworkState} and {@code subType},
* assuming that any mobile networks are using the current IMSI. The subType if applicable,
* should be set as one of the TelephonyManager.NETWORK_TYPE_* constants, or
diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java
index 4f05c9b..8a0211c 100644
--- a/core/java/android/net/NetworkPolicy.java
+++ b/core/java/android/net/NetworkPolicy.java
@@ -16,6 +16,7 @@
package android.net;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -205,7 +206,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (obj instanceof NetworkPolicy) {
final NetworkPolicy other = (NetworkPolicy) obj;
return warningBytes == other.warningBytes
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 82b035b..11146bd 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -34,6 +34,7 @@
import android.os.Build;
import android.os.Process;
import android.os.RemoteException;
+import android.telephony.Annotation;
import android.telephony.SubscriptionPlan;
import android.util.DebugUtils;
import android.util.Pair;
@@ -377,6 +378,8 @@
* @param overrideMask the bitmask that specifies which of the overrides is being
* set or cleared.
* @param overrideValue the override values to set or clear.
+ * @param networkTypes the network types this override applies to.
+ * {@see TelephonyManager#getAllNetworkTypes()}
* @param timeoutMillis the timeout after which the requested override will
* be automatically cleared, or {@code 0} to leave in the
* requested state until explicitly cleared, or the next reboot,
@@ -385,11 +388,12 @@
* @hide
*/
public void setSubscriptionOverride(int subId, @SubscriptionOverrideMask int overrideMask,
- @SubscriptionOverrideMask int overrideValue, long timeoutMillis,
- @NonNull String callingPackage) {
+ @SubscriptionOverrideMask int overrideValue,
+ @NonNull @Annotation.NetworkType int[] networkTypes, long timeoutMillis,
+ @NonNull String callingPackage) {
try {
- mService.setSubscriptionOverride(subId, overrideMask, overrideValue, timeoutMillis,
- callingPackage);
+ mService.setSubscriptionOverride(subId, overrideMask, overrideValue, networkTypes,
+ timeoutMillis, callingPackage);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -460,6 +464,31 @@
}
/**
+ * Figure out if networking is blocked for a given set of conditions.
+ *
+ * This is used by ConnectivityService via passing stale copies of conditions, so it must not
+ * take any locks.
+ *
+ * @param uid The target uid.
+ * @param uidRules The uid rules which are obtained from NetworkPolicyManagerService.
+ * @param isNetworkMetered True if the network is metered.
+ * @param isBackgroundRestricted True if data saver is enabled.
+ *
+ * @return true if networking is blocked for the UID under the specified conditions.
+ *
+ * @hide
+ */
+ public boolean checkUidNetworkingBlocked(int uid, int uidRules,
+ boolean isNetworkMetered, boolean isBackgroundRestricted) {
+ try {
+ return mService.checkUidNetworkingBlocked(uid, uidRules, isNetworkMetered,
+ isBackgroundRestricted);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Check that the given uid is restricted from doing networking on metered networks.
*
* @param uid The target uid.
@@ -613,9 +642,10 @@
* @param subId the subscriber this override applies to.
* @param overrideMask a bitmask that specifies which of the overrides is set.
* @param overrideValue a bitmask that specifies the override values.
+ * @param networkTypes the network types this override applies to.
*/
public void onSubscriptionOverride(int subId, @SubscriptionOverrideMask int overrideMask,
- @SubscriptionOverrideMask int overrideValue) {}
+ @SubscriptionOverrideMask int overrideValue, int[] networkTypes) {}
/**
* Notify of subscription plans change about a given subscription.
@@ -639,8 +669,8 @@
@Override
public void onSubscriptionOverride(int subId, @SubscriptionOverrideMask int overrideMask,
- @SubscriptionOverrideMask int overrideValue) {
- mCallback.onSubscriptionOverride(subId, overrideMask, overrideValue);
+ @SubscriptionOverrideMask int overrideValue, int[] networkTypes) {
+ mCallback.onSubscriptionOverride(subId, overrideMask, overrideValue, networkTypes);
}
@Override
@@ -656,7 +686,7 @@
@Override public void onRestrictBackgroundChanged(boolean restrictBackground) { }
@Override public void onUidPoliciesChanged(int uid, int uidPolicies) { }
@Override public void onSubscriptionOverride(int subId, int overrideMask,
- int overrideValue) { }
+ int overrideValue, int[] networkTypes) { }
@Override public void onSubscriptionPlansChanged(int subId, SubscriptionPlan[] plans) { }
}
}
diff --git a/core/java/android/net/NetworkScorerAppData.java b/core/java/android/net/NetworkScorerAppData.java
index 116e39e..caa6e45 100644
--- a/core/java/android/net/NetworkScorerAppData.java
+++ b/core/java/android/net/NetworkScorerAppData.java
@@ -110,7 +110,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
NetworkScorerAppData that = (NetworkScorerAppData) o;
diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java
index 713b688..e1ef8b5 100644
--- a/core/java/android/net/NetworkState.java
+++ b/core/java/android/net/NetworkState.java
@@ -16,6 +16,7 @@
package android.net;
+import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
@@ -30,7 +31,8 @@
public class NetworkState implements Parcelable {
private static final boolean VALIDATE_ROAMING_STATE = false;
- public static final NetworkState EMPTY = new NetworkState(null, null, null, null, null, null);
+ // TODO: remove and make members @NonNull.
+ public static final NetworkState EMPTY = new NetworkState();
public final NetworkInfo networkInfo;
public final LinkProperties linkProperties;
@@ -40,9 +42,18 @@
public final String subscriberId;
public final String networkId;
- public NetworkState(NetworkInfo networkInfo, LinkProperties linkProperties,
- NetworkCapabilities networkCapabilities, Network network, String subscriberId,
- String networkId) {
+ private NetworkState() {
+ networkInfo = null;
+ linkProperties = null;
+ networkCapabilities = null;
+ network = null;
+ subscriberId = null;
+ networkId = null;
+ }
+
+ public NetworkState(@NonNull NetworkInfo networkInfo, @NonNull LinkProperties linkProperties,
+ @NonNull NetworkCapabilities networkCapabilities, @NonNull Network network,
+ String subscriberId, String networkId) {
this.networkInfo = networkInfo;
this.linkProperties = linkProperties;
this.networkCapabilities = networkCapabilities;
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index cf40ce5..d42beae 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -412,7 +412,7 @@
/** @hide */
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o instanceof Entry) {
final Entry e = (Entry) o;
return uid == e.uid && set == e.set && tag == e.tag && metered == e.metered
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index 72be835..aa61e03 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -48,6 +48,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
+import com.android.net.module.util.NetworkIdentityUtils;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
@@ -296,11 +297,11 @@
builder.append("matchRule=").append(getMatchRuleName(mMatchRule));
if (mSubscriberId != null) {
builder.append(", subscriberId=").append(
- NetworkIdentity.scrubSubscriberId(mSubscriberId));
+ NetworkIdentityUtils.scrubSubscriberId(mSubscriberId));
}
if (mMatchSubscriberIds != null) {
builder.append(", matchSubscriberIds=").append(
- Arrays.toString(NetworkIdentity.scrubSubscriberId(mMatchSubscriberIds)));
+ Arrays.toString(NetworkIdentityUtils.scrubSubscriberIds(mMatchSubscriberIds)));
}
if (mNetworkId != null) {
builder.append(", networkId=").append(mNetworkId);
@@ -328,7 +329,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (obj instanceof NetworkTemplate) {
final NetworkTemplate other = (NetworkTemplate) obj;
return mMatchRule == other.mMatchRule
diff --git a/core/java/android/net/OemNetworkPreferences.java b/core/java/android/net/OemNetworkPreferences.java
index 6a8e3f9..5e56164 100644
--- a/core/java/android/net/OemNetworkPreferences.java
+++ b/core/java/android/net/OemNetworkPreferences.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,14 +18,14 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.os.Bundle;
import android.os.Parcelable;
-import android.util.SparseArray;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
import java.util.Collections;
-import java.util.List;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Objects;
/** @hide */
@@ -60,16 +60,16 @@
public static final int OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY = 4;
@NonNull
- private final SparseArray<List<String>> mNetworkMappings;
+ private final Bundle mNetworkMappings;
@NonNull
- public SparseArray<List<String>> getNetworkPreferences() {
- return mNetworkMappings.clone();
+ public Map<String, Integer> getNetworkPreferences() {
+ return convertToUnmodifiableMap(mNetworkMappings);
}
- private OemNetworkPreferences(@NonNull SparseArray<List<String>> networkMappings) {
+ private OemNetworkPreferences(@NonNull final Bundle networkMappings) {
Objects.requireNonNull(networkMappings);
- mNetworkMappings = networkMappings.clone();
+ mNetworkMappings = (Bundle) networkMappings.clone();
}
@Override
@@ -99,26 +99,45 @@
* @hide
*/
public static final class Builder {
- private final SparseArray<List<String>> mNetworkMappings;
+ private final Bundle mNetworkMappings;
public Builder() {
- mNetworkMappings = new SparseArray<>();
+ mNetworkMappings = new Bundle();
+ }
+
+ public Builder(@NonNull final OemNetworkPreferences preferences) {
+ Objects.requireNonNull(preferences);
+ mNetworkMappings = (Bundle) preferences.mNetworkMappings.clone();
}
/**
- * Add a network preference for a list of packages.
+ * Add a network preference for a given package. Previously stored values for the given
+ * package will be overwritten.
*
- * @param preference the desired network preference to use
- * @param packages full package names (e.g.: "com.google.apps.contacts") for apps to use
- * the given preference
+ * @param packageName full package name (e.g.: "com.google.apps.contacts") of the app
+ * to use the given preference
+ * @param preference the desired network preference to use
* @return The builder to facilitate chaining.
*/
@NonNull
- public Builder addNetworkPreference(@OemNetworkPreference final int preference,
- @NonNull List<String> packages) {
- Objects.requireNonNull(packages);
- mNetworkMappings.put(preference,
- Collections.unmodifiableList(new ArrayList<>(packages)));
+ public Builder addNetworkPreference(@NonNull final String packageName,
+ @OemNetworkPreference final int preference) {
+ Objects.requireNonNull(packageName);
+ mNetworkMappings.putInt(packageName, preference);
+ return this;
+ }
+
+ /**
+ * Remove a network preference for a given package.
+ *
+ * @param packageName full package name (e.g.: "com.google.apps.contacts") of the app to
+ * remove a preference for.
+ * @return The builder to facilitate chaining.
+ */
+ @NonNull
+ public Builder removeNetworkPreference(@NonNull final String packageName) {
+ Objects.requireNonNull(packageName);
+ mNetworkMappings.remove(packageName);
return this;
}
@@ -131,6 +150,14 @@
}
}
+ private static Map<String, Integer> convertToUnmodifiableMap(@NonNull final Bundle bundle) {
+ final Map<String, Integer> networkPreferences = new HashMap<>();
+ for (final String key : bundle.keySet()) {
+ networkPreferences.put(key, bundle.getInt(key));
+ }
+ return Collections.unmodifiableMap(networkPreferences);
+ }
+
/** @hide */
@IntDef(prefix = "OEM_NETWORK_PREFERENCE_", value = {
OEM_NETWORK_PREFERENCE_DEFAULT,
@@ -168,7 +195,7 @@
@Override
public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
- dest.writeSparseArray(mNetworkMappings);
+ dest.writeBundle(mNetworkMappings);
}
@Override
@@ -187,7 +214,7 @@
@Override
public OemNetworkPreferences createFromParcel(@NonNull android.os.Parcel in) {
return new OemNetworkPreferences(
- in.readSparseArray(getClass().getClassLoader()));
+ in.readBundle(getClass().getClassLoader()));
}
};
}
diff --git a/core/java/android/net/StringNetworkSpecifier.java b/core/java/android/net/StringNetworkSpecifier.java
index 3f2aa17..012410b 100644
--- a/core/java/android/net/StringNetworkSpecifier.java
+++ b/core/java/android/net/StringNetworkSpecifier.java
@@ -17,6 +17,7 @@
package android.net;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -45,7 +46,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (!(o instanceof StringNetworkSpecifier)) return false;
return TextUtils.equals(specifier, ((StringNetworkSpecifier) o).specifier);
}
diff --git a/core/java/android/net/TelephonyNetworkSpecifier.java b/core/java/android/net/TelephonyNetworkSpecifier.java
index 33c71d5..3348233 100644
--- a/core/java/android/net/TelephonyNetworkSpecifier.java
+++ b/core/java/android/net/TelephonyNetworkSpecifier.java
@@ -17,6 +17,7 @@
package android.net;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -74,7 +75,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
diff --git a/core/java/android/net/UidRange.java b/core/java/android/net/UidRange.java
index d75c43d..3bc0f9c 100644
--- a/core/java/android/net/UidRange.java
+++ b/core/java/android/net/UidRange.java
@@ -18,6 +18,7 @@
import static android.os.UserHandle.PER_USER_RANGE;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -81,7 +82,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/com/android/internal/net/VpnInfo.aidl b/core/java/android/net/UnderlyingNetworkInfo.aidl
similarity index 91%
rename from core/java/com/android/internal/net/VpnInfo.aidl
rename to core/java/android/net/UnderlyingNetworkInfo.aidl
index 6fc97be..a56f2f4 100644
--- a/core/java/com/android/internal/net/VpnInfo.aidl
+++ b/core/java/android/net/UnderlyingNetworkInfo.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package com.android.internal.net;
+package android.net;
-parcelable VpnInfo;
+parcelable UnderlyingNetworkInfo;
diff --git a/core/java/android/net/UnderlyingNetworkInfo.java b/core/java/android/net/UnderlyingNetworkInfo.java
new file mode 100644
index 0000000..7bf9231
--- /dev/null
+++ b/core/java/android/net/UnderlyingNetworkInfo.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A lightweight container used to carry information on the networks that underly a given
+ * virtual network.
+ *
+ * @hide
+ */
+@SystemApi(client = MODULE_LIBRARIES)
+public final class UnderlyingNetworkInfo implements Parcelable {
+ /** The owner of this network. */
+ public final int ownerUid;
+ /** The interface name of this network. */
+ @NonNull
+ public final String iface;
+ /** The names of the interfaces underlying this network. */
+ @NonNull
+ public final List<String> underlyingIfaces;
+
+ public UnderlyingNetworkInfo(int ownerUid, @NonNull String iface,
+ @NonNull List<String> underlyingIfaces) {
+ Objects.requireNonNull(iface);
+ Objects.requireNonNull(underlyingIfaces);
+ this.ownerUid = ownerUid;
+ this.iface = iface;
+ this.underlyingIfaces = Collections.unmodifiableList(new ArrayList<>(underlyingIfaces));
+ }
+
+ private UnderlyingNetworkInfo(@NonNull Parcel in) {
+ this.ownerUid = in.readInt();
+ this.iface = in.readString();
+ this.underlyingIfaces = new ArrayList<>();
+ in.readList(this.underlyingIfaces, null /*classLoader*/);
+ }
+
+ @Override
+ public String toString() {
+ return "UnderlyingNetworkInfo{"
+ + "ownerUid=" + ownerUid
+ + ", iface='" + iface + '\''
+ + ", underlyingIfaces='" + underlyingIfaces.toString() + '\''
+ + '}';
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(ownerUid);
+ dest.writeString(iface);
+ dest.writeList(underlyingIfaces);
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<UnderlyingNetworkInfo> CREATOR =
+ new Parcelable.Creator<UnderlyingNetworkInfo>() {
+ @NonNull
+ @Override
+ public UnderlyingNetworkInfo createFromParcel(@NonNull Parcel in) {
+ return new UnderlyingNetworkInfo(in);
+ }
+
+ @NonNull
+ @Override
+ public UnderlyingNetworkInfo[] newArray(int size) {
+ return new UnderlyingNetworkInfo[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof UnderlyingNetworkInfo)) return false;
+ final UnderlyingNetworkInfo that = (UnderlyingNetworkInfo) o;
+ return ownerUid == that.ownerUid
+ && Objects.equals(iface, that.iface)
+ && Objects.equals(underlyingIfaces, that.underlyingIfaces);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(ownerUid, iface, underlyingIfaces);
+ }
+}
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 1cb4fe8..ffe4f3c 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -343,7 +343,7 @@
* default port explicitly and the other leaves it implicit, they will not
* be considered equal.
*/
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (!(o instanceof Uri)) {
return false;
}
diff --git a/core/java/android/net/http/SslCertificate.java b/core/java/android/net/http/SslCertificate.java
index 250cff2..a22d41a 100644
--- a/core/java/android/net/http/SslCertificate.java
+++ b/core/java/android/net/http/SslCertificate.java
@@ -26,7 +26,7 @@
import android.widget.TextView;
import com.android.internal.util.HexDump;
-import com.android.org.bouncycastle.asn1.x509.X509Name;
+import com.android.internal.org.bouncycastle.asn1.x509.X509Name;
import java.io.ByteArrayInputStream;
import java.math.BigInteger;
diff --git a/core/java/android/net/vcn/IVcnManagementService.aidl b/core/java/android/net/vcn/IVcnManagementService.aidl
index 80ac64b..4f293ee 100644
--- a/core/java/android/net/vcn/IVcnManagementService.aidl
+++ b/core/java/android/net/vcn/IVcnManagementService.aidl
@@ -16,8 +16,11 @@
package android.net.vcn;
+import android.net.LinkProperties;
+import android.net.NetworkCapabilities;
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
import android.net.vcn.VcnConfig;
+import android.net.vcn.VcnUnderlyingNetworkPolicy;
import android.os.ParcelUuid;
/**
@@ -29,4 +32,5 @@
void addVcnUnderlyingNetworkPolicyListener(in IVcnUnderlyingNetworkPolicyListener listener);
void removeVcnUnderlyingNetworkPolicyListener(in IVcnUnderlyingNetworkPolicyListener listener);
+ VcnUnderlyingNetworkPolicy getUnderlyingNetworkPolicy(in NetworkCapabilities nc, in LinkProperties lp);
}
diff --git a/core/java/android/net/vcn/VcnConfig.java b/core/java/android/net/vcn/VcnConfig.java
index ede8faa..5eb4ba6 100644
--- a/core/java/android/net/vcn/VcnConfig.java
+++ b/core/java/android/net/vcn/VcnConfig.java
@@ -96,7 +96,11 @@
return mPackageName;
}
- /** Retrieves the set of configured tunnels. */
+ /**
+ * Retrieves the set of configured tunnels.
+ *
+ * @hide
+ */
@NonNull
public Set<VcnGatewayConnectionConfig> getGatewayConnectionConfigs() {
return Collections.unmodifiableSet(mGatewayConnectionConfigs);
@@ -146,7 +150,7 @@
}
@Override
- public void writeToParcel(Parcel out, int flags) {
+ public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeParcelable(toPersistableBundle(), flags);
}
@@ -164,8 +168,12 @@
}
};
- /** This class is used to incrementally build {@link VcnConfig} objects. */
- public static class Builder {
+ /**
+ * This class is used to incrementally build {@link VcnConfig} objects.
+ *
+ * @hide
+ */
+ public static final class Builder {
@NonNull private final String mPackageName;
@NonNull
@@ -182,6 +190,7 @@
*
* @param gatewayConnectionConfig the configuration for an individual gateway connection
* @return this {@link Builder} instance, for chaining
+ * @hide
*/
@NonNull
public Builder addGatewayConnectionConfig(
@@ -196,6 +205,7 @@
* Builds and validates the VcnConfig.
*
* @return an immutable VcnConfig instance
+ * @hide
*/
@NonNull
public VcnConfig build() {
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index d531cdb..cead2f1 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -17,6 +17,7 @@
import static com.android.internal.annotations.VisibleForTesting.Visibility;
+import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -25,14 +26,19 @@
import android.util.ArraySet;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
import com.android.server.vcn.util.PersistableBundleUtils;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Objects;
import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
/**
@@ -97,6 +103,26 @@
ALLOWED_CAPABILITIES = Collections.unmodifiableSet(allowedCaps);
}
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = {"NET_CAPABILITY_"},
+ value = {
+ NetworkCapabilities.NET_CAPABILITY_MMS,
+ NetworkCapabilities.NET_CAPABILITY_SUPL,
+ NetworkCapabilities.NET_CAPABILITY_DUN,
+ NetworkCapabilities.NET_CAPABILITY_FOTA,
+ NetworkCapabilities.NET_CAPABILITY_IMS,
+ NetworkCapabilities.NET_CAPABILITY_CBS,
+ NetworkCapabilities.NET_CAPABILITY_IA,
+ NetworkCapabilities.NET_CAPABILITY_RCS,
+ NetworkCapabilities.NET_CAPABILITY_XCAP,
+ NetworkCapabilities.NET_CAPABILITY_EIMS,
+ NetworkCapabilities.NET_CAPABILITY_INTERNET,
+ NetworkCapabilities.NET_CAPABILITY_MCX,
+ })
+ public @interface VcnSupportedCapability {}
+
private static final int DEFAULT_MAX_MTU = 1500;
/**
@@ -128,10 +154,10 @@
};
private static final String EXPOSED_CAPABILITIES_KEY = "mExposedCapabilities";
- @NonNull private final Set<Integer> mExposedCapabilities;
+ @NonNull private final SortedSet<Integer> mExposedCapabilities;
private static final String UNDERLYING_CAPABILITIES_KEY = "mUnderlyingCapabilities";
- @NonNull private final Set<Integer> mUnderlyingCapabilities;
+ @NonNull private final SortedSet<Integer> mUnderlyingCapabilities;
// TODO: Add Ike/ChildSessionParams as a subclass - maybe VcnIkeGatewayConnectionConfig
@@ -141,14 +167,14 @@
private static final String RETRY_INTERVAL_MS_KEY = "mRetryIntervalsMs";
@NonNull private final long[] mRetryIntervalsMs;
- @VisibleForTesting(visibility = Visibility.PRIVATE)
- public VcnGatewayConnectionConfig(
+ /** Builds a VcnGatewayConnectionConfig with the specified parameters. */
+ private VcnGatewayConnectionConfig(
@NonNull Set<Integer> exposedCapabilities,
@NonNull Set<Integer> underlyingCapabilities,
@NonNull long[] retryIntervalsMs,
@IntRange(from = MIN_MTU_V6) int maxMtu) {
- mExposedCapabilities = exposedCapabilities;
- mUnderlyingCapabilities = underlyingCapabilities;
+ mExposedCapabilities = new TreeSet(exposedCapabilities);
+ mUnderlyingCapabilities = new TreeSet(underlyingCapabilities);
mRetryIntervalsMs = retryIntervalsMs;
mMaxMtu = maxMtu;
@@ -163,9 +189,9 @@
final PersistableBundle underlyingCapsBundle =
in.getPersistableBundle(UNDERLYING_CAPABILITIES_KEY);
- mExposedCapabilities = new ArraySet<>(PersistableBundleUtils.toList(
+ mExposedCapabilities = new TreeSet<>(PersistableBundleUtils.toList(
exposedCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER));
- mUnderlyingCapabilities = new ArraySet<>(PersistableBundleUtils.toList(
+ mUnderlyingCapabilities = new TreeSet<>(PersistableBundleUtils.toList(
underlyingCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER));
mRetryIntervalsMs = in.getLongArray(RETRY_INTERVAL_MS_KEY);
mMaxMtu = in.getInt(MAX_MTU_KEY);
@@ -219,52 +245,93 @@
/**
* Returns all exposed capabilities.
*
+ * <p>The returned integer-value capabilities will not contain duplicates, and will be sorted in
+ * ascending numerical order.
+ *
+ * @see Builder#addExposedCapability(int)
+ * @see Builder#clearExposedCapability(int)
* @hide
*/
@NonNull
+ public int[] getExposedCapabilities() {
+ // Sorted set guarantees ordering
+ return ArrayUtils.convertToIntArray(new ArrayList<>(mExposedCapabilities));
+ }
+
+ /**
+ * Returns all exposed capabilities.
+ *
+ * <p>Left to prevent the need to make major changes while changes are actively in flight.
+ *
+ * @deprecated use getExposedCapabilities() instead
+ * @hide
+ */
+ @Deprecated
+ @NonNull
public Set<Integer> getAllExposedCapabilities() {
return Collections.unmodifiableSet(mExposedCapabilities);
}
/**
- * Checks if this config is configured to support/expose a specific capability.
+ * Returns all capabilities required of underlying networks.
*
- * @param capability the capability to check for
+ * <p>The returned integer-value capabilities will be sorted in ascending numerical order.
+ *
+ * @see Builder#addRequiredUnderlyingCapability(int)
+ * @see Builder#clearRequiredUnderlyingCapability(int)
+ * @hide
*/
- public boolean hasExposedCapability(int capability) {
- checkValidCapability(capability);
-
- return mExposedCapabilities.contains(capability);
+ @NonNull
+ public int[] getRequiredUnderlyingCapabilities() {
+ // Sorted set guarantees ordering
+ return ArrayUtils.convertToIntArray(new ArrayList<>(mUnderlyingCapabilities));
}
/**
* Returns all capabilities required of underlying networks.
*
+ * <p>Left to prevent the need to make major changes while changes are actively in flight.
+ *
+ * @deprecated use getRequiredUnderlyingCapabilities() instead
* @hide
*/
+ @Deprecated
@NonNull
public Set<Integer> getAllUnderlyingCapabilities() {
return Collections.unmodifiableSet(mUnderlyingCapabilities);
}
/**
- * Checks if this config requires an underlying network to have the specified capability.
+ * Retrieves the configured retry intervals.
*
- * @param capability the capability to check for
+ * @see Builder#setRetryInterval(long[])
+ * @hide
*/
- public boolean requiresUnderlyingCapability(int capability) {
- checkValidCapability(capability);
-
- return mUnderlyingCapabilities.contains(capability);
- }
-
- /** Retrieves the configured retry intervals. */
@NonNull
- public long[] getRetryIntervalsMs() {
+ public long[] getRetryInterval() {
return Arrays.copyOf(mRetryIntervalsMs, mRetryIntervalsMs.length);
}
- /** Retrieves the maximum MTU allowed for this Gateway Connection. */
+ /**
+ * Retrieves the configured retry intervals.
+ *
+ * <p>Left to prevent the need to make major changes while changes are actively in flight.
+ *
+ * @deprecated use getRequiredUnderlyingCapabilities() instead
+ * @hide
+ */
+ @Deprecated
+ @NonNull
+ public long[] getRetryIntervalsMs() {
+ return getRetryInterval();
+ }
+
+ /**
+ * Retrieves the maximum MTU allowed for this Gateway Connection.
+ *
+ * @see Builder.setMaxMtu(int)
+ * @hide
+ */
@IntRange(from = MIN_MTU_V6)
public int getMaxMtu() {
return mMaxMtu;
@@ -319,8 +386,12 @@
&& mMaxMtu == rhs.mMaxMtu;
}
- /** This class is used to incrementally build {@link VcnGatewayConnectionConfig} objects. */
- public static class Builder {
+ /**
+ * This class is used to incrementally build {@link VcnGatewayConnectionConfig} objects.
+ *
+ * @hide
+ */
+ public static final class Builder {
@NonNull private final Set<Integer> mExposedCapabilities = new ArraySet();
@NonNull private final Set<Integer> mUnderlyingCapabilities = new ArraySet();
@NonNull private long[] mRetryIntervalsMs = DEFAULT_RETRY_INTERVALS_MS;
@@ -338,8 +409,10 @@
* @return this {@link Builder} instance, for chaining
* @see VcnGatewayConnectionConfig for a list of capabilities may be exposed by a Gateway
* Connection
+ * @hide
*/
- public Builder addExposedCapability(int exposedCapability) {
+ @NonNull
+ public Builder addExposedCapability(@VcnSupportedCapability int exposedCapability) {
checkValidCapability(exposedCapability);
mExposedCapabilities.add(exposedCapability);
@@ -354,8 +427,10 @@
* @return this {@link Builder} instance, for chaining
* @see VcnGatewayConnectionConfig for a list of capabilities may be exposed by a Gateway
* Connection
+ * @hide
*/
- public Builder removeExposedCapability(int exposedCapability) {
+ @NonNull
+ public Builder clearExposedCapability(@VcnSupportedCapability int exposedCapability) {
checkValidCapability(exposedCapability);
mExposedCapabilities.remove(exposedCapability);
@@ -370,8 +445,11 @@
* @return this {@link Builder} instance, for chaining
* @see VcnGatewayConnectionConfig for a list of capabilities may be required of underlying
* networks
+ * @hide
*/
- public Builder addRequiredUnderlyingCapability(int underlyingCapability) {
+ @NonNull
+ public Builder addRequiredUnderlyingCapability(
+ @VcnSupportedCapability int underlyingCapability) {
checkValidCapability(underlyingCapability);
mUnderlyingCapabilities.add(underlyingCapability);
@@ -390,8 +468,11 @@
* @return this {@link Builder} instance, for chaining
* @see VcnGatewayConnectionConfig for a list of capabilities may be required of underlying
* networks
+ * @hide
*/
- public Builder removeRequiredUnderlyingCapability(int underlyingCapability) {
+ @NonNull
+ public Builder clearRequiredUnderlyingCapability(
+ @VcnSupportedCapability int underlyingCapability) {
checkValidCapability(underlyingCapability);
mUnderlyingCapabilities.remove(underlyingCapability);
@@ -420,6 +501,7 @@
* 15m]}
* @return this {@link Builder} instance, for chaining
* @see VcnManager for additional discussion on fail-safe mode
+ * @hide
*/
@NonNull
public Builder setRetryInterval(@NonNull long[] retryIntervalsMs) {
@@ -441,6 +523,7 @@
* @param maxMtu the maximum MTU allowed for this Gateway Connection. Must be greater than
* the IPv6 minimum MTU of 1280. Defaults to 1500.
* @return this {@link Builder} instance, for chaining
+ * @hide
*/
@NonNull
public Builder setMaxMtu(@IntRange(from = MIN_MTU_V6) int maxMtu) {
@@ -455,6 +538,7 @@
* Builds and validates the VcnGatewayConnectionConfig.
*
* @return an immutable VcnGatewayConnectionConfig instance
+ * @hide
*/
@NonNull
public VcnGatewayConnectionConfig build() {
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index 2ccdc26..fa090f5 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -21,6 +21,8 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.content.Context;
+import android.net.LinkProperties;
+import android.net.NetworkCapabilities;
import android.os.ParcelUuid;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
@@ -62,7 +64,7 @@
* @hide
*/
@SystemService(Context.VCN_MANAGEMENT_SERVICE)
-public final class VcnManager {
+public class VcnManager {
@NonNull private static final String TAG = VcnManager.class.getSimpleName();
@VisibleForTesting
@@ -222,6 +224,37 @@
}
/**
+ * Queries the underlying network policy for a network with the given parameters.
+ *
+ * <p>Prior to a new NetworkAgent being registered, or upon notification that Carrier VCN policy
+ * may have changed via {@link VcnUnderlyingNetworkPolicyListener#onPolicyChanged()}, a Network
+ * Provider MUST poll for the updated Network policy based on that Network's capabilities and
+ * properties.
+ *
+ * @param networkCapabilities the NetworkCapabilities to be used in determining the Network
+ * policy for this Network.
+ * @param linkProperties the LinkProperties to be used in determining the Network policy for
+ * this Network.
+ * @throws SecurityException if the caller does not have permission NETWORK_FACTORY
+ * @return the VcnUnderlyingNetworkPolicy to be used for this Network.
+ * @hide
+ */
+ @NonNull
+ @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+ public VcnUnderlyingNetworkPolicy getUnderlyingNetworkPolicy(
+ @NonNull NetworkCapabilities networkCapabilities,
+ @NonNull LinkProperties linkProperties) {
+ requireNonNull(networkCapabilities, "networkCapabilities must not be null");
+ requireNonNull(linkProperties, "linkProperties must not be null");
+
+ try {
+ return mService.getUnderlyingNetworkPolicy(networkCapabilities, linkProperties);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Binder wrapper for added VcnUnderlyingNetworkPolicyListeners to receive signals from System
* Server.
*
diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
index d7c2e05..64ab074 100644
--- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -97,6 +97,11 @@
final boolean mRequiresDeviceUnlock;
/**
+ * Whether this service should only be started when the device is screen on.
+ */
+ final boolean mRequiresDeviceScreenOn;
+
+ /**
* The id of the service banner specified in XML.
*/
final int mBannerResourceId;
@@ -119,6 +124,18 @@
ArrayList<AidGroup> staticAidGroups, ArrayList<AidGroup> dynamicAidGroups,
boolean requiresUnlock, int bannerResource, int uid,
String settingsActivityName, String offHost, String staticOffHost) {
+ this(info, onHost, description, staticAidGroups, dynamicAidGroups,
+ requiresUnlock, onHost ? true : false, bannerResource, uid,
+ settingsActivityName, offHost, staticOffHost);
+ }
+
+ /**
+ * @hide
+ */
+ public ApduServiceInfo(ResolveInfo info, boolean onHost, String description,
+ ArrayList<AidGroup> staticAidGroups, ArrayList<AidGroup> dynamicAidGroups,
+ boolean requiresUnlock, boolean requiresScreenOn, int bannerResource, int uid,
+ String settingsActivityName, String offHost, String staticOffHost) {
this.mService = info;
this.mDescription = description;
this.mStaticAidGroups = new HashMap<String, AidGroup>();
@@ -127,6 +144,7 @@
this.mStaticOffHostName = staticOffHost;
this.mOnHost = onHost;
this.mRequiresDeviceUnlock = requiresUnlock;
+ this.mRequiresDeviceScreenOn = requiresScreenOn;
for (AidGroup aidGroup : staticAidGroups) {
this.mStaticAidGroups.put(aidGroup.category, aidGroup);
}
@@ -183,6 +201,9 @@
mRequiresDeviceUnlock = sa.getBoolean(
com.android.internal.R.styleable.HostApduService_requireDeviceUnlock,
false);
+ mRequiresDeviceScreenOn = sa.getBoolean(
+ com.android.internal.R.styleable.HostApduService_requireDeviceScreenOn,
+ true);
mBannerResourceId = sa.getResourceId(
com.android.internal.R.styleable.HostApduService_apduServiceBanner, -1);
mSettingsActivityName = sa.getString(
@@ -196,7 +217,12 @@
mService = info;
mDescription = sa.getString(
com.android.internal.R.styleable.OffHostApduService_description);
- mRequiresDeviceUnlock = false;
+ mRequiresDeviceUnlock = sa.getBoolean(
+ com.android.internal.R.styleable.OffHostApduService_requireDeviceUnlock,
+ false);
+ mRequiresDeviceScreenOn = sa.getBoolean(
+ com.android.internal.R.styleable.OffHostApduService_requireDeviceScreenOn,
+ false);
mBannerResourceId = sa.getResourceId(
com.android.internal.R.styleable.OffHostApduService_apduServiceBanner, -1);
mSettingsActivityName = sa.getString(
@@ -419,6 +445,13 @@
return mRequiresDeviceUnlock;
}
+ /**
+ * Returns whether this service should only be started when the device is screen on.
+ */
+ public boolean requiresScreenOn() {
+ return mRequiresDeviceScreenOn;
+ }
+
@UnsupportedAppUsage
public String getDescription() {
return mDescription;
@@ -542,6 +575,7 @@
dest.writeTypedList(new ArrayList<AidGroup>(mDynamicAidGroups.values()));
}
dest.writeInt(mRequiresDeviceUnlock ? 1 : 0);
+ dest.writeInt(mRequiresDeviceScreenOn ? 1 : 0);
dest.writeInt(mBannerResourceId);
dest.writeInt(mUid);
dest.writeString(mSettingsActivityName);
@@ -568,11 +602,12 @@
source.readTypedList(dynamicAidGroups, AidGroup.CREATOR);
}
boolean requiresUnlock = source.readInt() != 0;
+ boolean requiresScreenOn = source.readInt() != 0;
int bannerResource = source.readInt();
int uid = source.readInt();
String settingsActivityName = source.readString();
return new ApduServiceInfo(info, onHost, description, staticAidGroups,
- dynamicAidGroups, requiresUnlock, bannerResource, uid,
+ dynamicAidGroups, requiresUnlock, requiresScreenOn, bannerResource, uid,
settingsActivityName, offHostName, staticOffHostName);
}
@@ -607,6 +642,8 @@
}
}
pw.println(" Settings Activity: " + mSettingsActivityName);
+ pw.println(" Requires Device Unlock: " + mRequiresDeviceUnlock);
+ pw.println(" Requires Device ScreenOn: " + mRequiresDeviceScreenOn);
}
/**
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index 0185ba4..16d041a 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -547,7 +547,8 @@
}
try {
- return transactNative(code, data, reply, flags);
+ boolean replyOwnsNative = (reply == null) ? false : reply.ownsNativeParcelObject();
+ return transactNative(code, data, reply, replyOwnsNative, flags);
} finally {
AppOpsManager.resumeNotedAppOpsCollection(prevCollection);
@@ -572,7 +573,7 @@
* Native implementation of transact() for proxies
*/
public native boolean transactNative(int code, Parcel data, Parcel reply,
- int flags) throws RemoteException;
+ boolean replyOwnsNativeParcelObject, int flags) throws RemoteException;
/**
* See {@link IBinder#linkToDeath(DeathRecipient, int)}
*/
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 1076118..5ae53b5 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -26,6 +26,7 @@
import android.app.Application;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.sysprop.SocProperties;
import android.sysprop.TelephonyProperties;
import android.text.TextUtils;
import android.util.Slog;
@@ -87,6 +88,14 @@
/** The end-user-visible name for the end product. */
public static final String MODEL = getString("ro.product.model");
+ /** The manufacturer of the device's primary system-on-chip. */
+ @NonNull
+ public static final String SOC_MANUFACTURER = SocProperties.soc_manufacturer().orElse(UNKNOWN);
+
+ /** The model name of the device's primary system-on-chip. */
+ @NonNull
+ public static final String SOC_MODEL = SocProperties.soc_model().orElse(UNKNOWN);
+
/** The system bootloader version number. */
public static final String BOOTLOADER = getString("ro.bootloader");
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 6acdcc4..f0a99ed 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -3691,4 +3691,9 @@
public long getBlobAshmemSize() {
return nativeGetBlobAshmemSize(mNativePtr);
}
+
+ /** @hide */
+ /*package*/ boolean ownsNativeParcelObject() {
+ return mOwnsNativeParcelObject;
+ }
}
diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java
index 71344f9..f853e67 100644
--- a/core/java/android/os/ServiceManager.java
+++ b/core/java/android/os/ServiceManager.java
@@ -288,6 +288,20 @@
}
/**
+ * Get service debug info.
+ * @return an array of information for each service (like listServices, but with PIDs)
+ * @hide
+ */
+ public static ServiceDebugInfo[] getServiceDebugInfo() {
+ try {
+ return getIServiceManager().getServiceDebugInfo();
+ } catch (RemoteException e) {
+ Log.e(TAG, "error in getServiceDebugInfo", e);
+ return null;
+ }
+ }
+
+ /**
* This is only intended to be called when the process is first being brought
* up and bound by the activity manager. There is only one thread in the process
* at that time, so no locking is done.
diff --git a/core/java/android/os/ServiceManagerNative.java b/core/java/android/os/ServiceManagerNative.java
index b70b6b5..60acc57 100644
--- a/core/java/android/os/ServiceManagerNative.java
+++ b/core/java/android/os/ServiceManagerNative.java
@@ -103,6 +103,10 @@
throw new RemoteException();
}
+ public ServiceDebugInfo[] getServiceDebugInfo() throws RemoteException {
+ return mServiceManager.getServiceDebugInfo();
+ }
+
/**
* Same as mServiceManager but used by apps.
*
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index bef876f9..3d539a6 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -295,6 +295,19 @@
}
/**
+ * Returns the uid that is composed from the userHandle and the appId.
+ *
+ * @param userHandle the UserHandle to compose the uid
+ * @param appId the AppId to compose the uid
+ * @return the uid that is composed from the userHandle and the appId
+ * @hide
+ */
+ @SystemApi
+ public static int getUid(@NonNull UserHandle userHandle, @AppIdInt int appId) {
+ return getUid(userHandle.getIdentifier(), appId);
+ }
+
+ /**
* Returns the app id (or base uid) for a given uid, stripping out the user id from it.
* @hide
*/
diff --git a/core/java/android/provider/OWNERS b/core/java/android/provider/OWNERS
index cb1509a..cb06515 100644
--- a/core/java/android/provider/OWNERS
+++ b/core/java/android/provider/OWNERS
@@ -1,5 +1,6 @@
per-file *BlockedNumber* = file:/telephony/OWNERS
per-file *Telephony* = file:/telephony/OWNERS
+per-file *SimPhonebook* = file:/telephony/OWNERS
per-file *CallLog* = file:platform/packages/providers/ContactsProvider:/OWNERS
per-file *Contacts* = file:platform/packages/providers/ContactsProvider:/OWNERS
diff --git a/core/java/android/provider/SimPhonebookContract.java b/core/java/android/provider/SimPhonebookContract.java
new file mode 100644
index 0000000..2efc212
--- /dev/null
+++ b/core/java/android/provider/SimPhonebookContract.java
@@ -0,0 +1,506 @@
+/*
+ * 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.provider;
+
+import static android.provider.SimPhonebookContract.ElementaryFiles.EF_ADN;
+import static android.provider.SimPhonebookContract.ElementaryFiles.EF_ADN_PATH_SEGMENT;
+import static android.provider.SimPhonebookContract.ElementaryFiles.EF_FDN;
+import static android.provider.SimPhonebookContract.ElementaryFiles.EF_FDN_PATH_SEGMENT;
+import static android.provider.SimPhonebookContract.ElementaryFiles.EF_SDN;
+import static android.provider.SimPhonebookContract.ElementaryFiles.EF_SDN_PATH_SEGMENT;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.WorkerThread;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.SubscriptionInfo;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * The contract between the provider of contact records on the device's SIM cards and applications.
+ * Contains definitions of the supported URIs and columns.
+ *
+ * <p>This content provider does not support any of the QUERY_ARG_SQL* bundle arguments. An
+ * IllegalArgumentException will be thrown if these are included.
+ */
+public final class SimPhonebookContract {
+
+ /** The authority for the SIM phonebook provider. */
+ public static final String AUTHORITY = "com.android.simphonebook";
+ /** The content:// style uri to the authority for the SIM phonebook provider. */
+ @NonNull
+ public static final Uri AUTHORITY_URI = Uri.parse("content://com.android.simphonebook");
+ /**
+ * The Uri path element used to indicate that the following path segment is a subscription ID
+ * for the SIM card that will be operated on.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String SUBSCRIPTION_ID_PATH_SEGMENT = "subid";
+
+ private SimPhonebookContract() {
+ }
+
+ /**
+ * Returns the Uri path segment used to reference the specified elementary file type for Uris
+ * returned by this API.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public static String getEfUriPath(@ElementaryFiles.EfType int efType) {
+ switch (efType) {
+ case EF_ADN:
+ return EF_ADN_PATH_SEGMENT;
+ case EF_FDN:
+ return EF_FDN_PATH_SEGMENT;
+ case EF_SDN:
+ return EF_SDN_PATH_SEGMENT;
+ default:
+ throw new IllegalArgumentException("Unsupported EfType " + efType);
+ }
+ }
+
+ /** Constants for the contact records on a SIM card. */
+ public static final class SimRecords {
+
+ /**
+ * The subscription ID of the SIM the record is from.
+ *
+ * @see SubscriptionInfo#getSubscriptionId()
+ */
+ public static final String SUBSCRIPTION_ID = "subscription_id";
+ /**
+ * The type of the elementary file the record is from.
+ *
+ * @see ElementaryFiles#EF_ADN
+ * @see ElementaryFiles#EF_FDN
+ * @see ElementaryFiles#EF_SDN
+ */
+ public static final String ELEMENTARY_FILE_TYPE = "elementary_file_type";
+ /**
+ * The 1-based offset of the record in the elementary file that contains it.
+ *
+ * <p>This can be used to access individual SIM records by appending it to the
+ * elementary file URIs but it is not like a normal database ID because it is not
+ * auto-incrementing and it is not unique across SIM cards or elementary files. Hence, care
+ * should be taken when using it to ensure that it is applied to the correct SIM and EF.
+ *
+ * @see #getItemUri(int, int, int)
+ */
+ public static final String RECORD_NUMBER = "record_number";
+ /**
+ * The name for this record.
+ *
+ * <p>An {@link IllegalArgumentException} will be thrown by insert and update if this
+ * exceeds the maximum supported length or contains unsupported characters.
+ * {@link #validateName(ContentResolver, int, int, String)} )} can be used to
+ * check whether the name is supported.
+ *
+ * @see ElementaryFiles#NAME_MAX_LENGTH
+ * @see #validateName(ContentResolver, int, int, String) )
+ */
+ public static final String NAME = "name";
+ /**
+ * The phone number for this record.
+ *
+ * <p>Only dialable characters are supported.
+ *
+ * <p>An {@link IllegalArgumentException} will be thrown by insert and update if this
+ * exceeds the maximum supported length or contains unsupported characters.
+ *
+ * @see ElementaryFiles#PHONE_NUMBER_MAX_LENGTH
+ * @see android.telephony.PhoneNumberUtils#isDialable(char)
+ */
+ public static final String PHONE_NUMBER = "phone_number";
+
+ /** The MIME type of a CONTENT_URI subdirectory of a single SIM record. */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sim-contact_v2";
+ /** The MIME type of CONTENT_URI providing a directory of SIM records. */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/sim-contact_v2";
+
+ /**
+ * The path segment that is appended to {@link #getContentUri(int, int)} which indicates
+ * that the following path segment contains a name to be validated.
+ *
+ * @hide
+ * @see #validateName(ContentResolver, int, int, String)
+ */
+ @SystemApi
+ public static final String VALIDATE_NAME_PATH_SEGMENT = "validate_name";
+
+ /**
+ * The key for a cursor extra that contains the result of a validate name query.
+ *
+ * @hide
+ * @see #validateName(ContentResolver, int, int, String)
+ */
+ @SystemApi
+ public static final String EXTRA_NAME_VALIDATION_RESULT =
+ "android.provider.extra.NAME_VALIDATION_RESULT";
+
+
+ /**
+ * Key for the PIN2 needed to modify FDN record that should be passed in the Bundle
+ * passed to {@link ContentResolver#insert(Uri, ContentValues, Bundle)},
+ * {@link ContentResolver#update(Uri, ContentValues, Bundle)}
+ * and {@link ContentResolver#delete(Uri, Bundle)}.
+ *
+ * <p>Modifying FDN records also requires either
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or
+ * {@link TelephonyManager#hasCarrierPrivileges()}
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String QUERY_ARG_PIN2 = "android:query-arg-pin2";
+
+ private SimRecords() {
+ }
+
+ /**
+ * Returns the content Uri for the specified elementary file on the specified SIM.
+ *
+ * <p>When queried this Uri will return all of the contact records in the specified
+ * elementary file on the specified SIM. The available subscriptionIds and efTypes can
+ * be discovered by querying {@link ElementaryFiles#CONTENT_URI}.
+ *
+ * <p>If a SIM with the provided subscription ID does not exist or the SIM with the provided
+ * subscription ID doesn't support the specified entity file then queries will return
+ * and empty cursor and inserts will throw an {@link IllegalArgumentException}
+ *
+ * @param subscriptionId the subscriptionId of the SIM card that this Uri will reference
+ * @param efType the elementary file on the SIM that this Uri will reference
+ * @see ElementaryFiles#EF_ADN
+ * @see ElementaryFiles#EF_FDN
+ * @see ElementaryFiles#EF_SDN
+ */
+ @NonNull
+ public static Uri getContentUri(int subscriptionId, @ElementaryFiles.EfType int efType) {
+ return buildContentUri(subscriptionId, efType).build();
+ }
+
+ /**
+ * Content Uri for the specific SIM record with the provided {@link #RECORD_NUMBER}.
+ *
+ * <p>When queried this will return the record identified by the provided arguments.
+ *
+ * <p>For a non-existent record:
+ * <ul>
+ * <li>query will return an empty cursor</li>
+ * <li>update will return 0</li>
+ * <li>delete will return 0</li>
+ * </ul>
+ *
+ * @param subscriptionId the subscription ID of the SIM containing the record. If no SIM
+ * with this subscription ID exists then it will be treated as a
+ * non-existent record
+ * @param efType the elementary file type containing the record. If the specified
+ * SIM doesn't support this elementary file then it will be treated
+ * as a non-existent record.
+ * @param recordNumber the record number of the record this Uri should reference. This
+ * must be greater than 0. If there is no record with this record
+ * number in the specified entity file then it will be treated as a
+ * non-existent record.
+ */
+ @NonNull
+ public static Uri getItemUri(
+ int subscriptionId, @ElementaryFiles.EfType int efType, int recordNumber) {
+ // Elementary file record indices are 1-based.
+ Preconditions.checkArgument(recordNumber > 0, "Invalid recordNumber");
+
+ return buildContentUri(subscriptionId, efType)
+ .appendPath(String.valueOf(recordNumber))
+ .build();
+ }
+
+ /**
+ * Validates a value that is being provided for the {@link #NAME} column.
+ *
+ * <p>The return value can be used to check if the name is valid. If it is not valid then
+ * inserts and updates to the specified elementary file that use the provided name value
+ * will throw an {@link IllegalArgumentException}.
+ *
+ * <p>If the specified SIM or elementary file don't exist then
+ * {@link NameValidationResult#getMaxEncodedLength()} will be zero and
+ * {@link NameValidationResult#isValid()} will return false.
+ */
+ @NonNull
+ @WorkerThread
+ public static NameValidationResult validateName(
+ @NonNull ContentResolver resolver, int subscriptionId,
+ @ElementaryFiles.EfType int efType,
+ @NonNull String name) {
+ Bundle queryArgs = new Bundle();
+ queryArgs.putString(SimRecords.NAME, name);
+ try (Cursor cursor =
+ resolver.query(buildContentUri(subscriptionId, efType)
+ .appendPath(VALIDATE_NAME_PATH_SEGMENT)
+ .build(), null, queryArgs, null)) {
+ NameValidationResult result = cursor.getExtras()
+ .getParcelable(EXTRA_NAME_VALIDATION_RESULT);
+ return result != null ? result : new NameValidationResult(name, "", 0, 0);
+ }
+ }
+
+ private static Uri.Builder buildContentUri(
+ int subscriptionId, @ElementaryFiles.EfType int efType) {
+ return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(AUTHORITY)
+ .appendPath(SUBSCRIPTION_ID_PATH_SEGMENT)
+ .appendPath(String.valueOf(subscriptionId))
+ .appendPath(getEfUriPath(efType));
+ }
+
+ /** Contains details about the validity of a value provided for the {@link #NAME} column. */
+ public static final class NameValidationResult implements Parcelable {
+
+ @NonNull
+ public static final Creator<NameValidationResult> CREATOR =
+ new Creator<NameValidationResult>() {
+
+ @Override
+ public NameValidationResult createFromParcel(@NonNull Parcel in) {
+ return new NameValidationResult(in);
+ }
+
+ @NonNull
+ @Override
+ public NameValidationResult[] newArray(int size) {
+ return new NameValidationResult[size];
+ }
+ };
+
+ private final String mName;
+ private final String mSanitizedName;
+ private final int mEncodedLength;
+ private final int mMaxEncodedLength;
+
+ /** Creates a new instance from the provided values. */
+ public NameValidationResult(@NonNull String name, @NonNull String sanitizedName,
+ int encodedLength, int maxEncodedLength) {
+ this.mName = Objects.requireNonNull(name);
+ this.mSanitizedName = Objects.requireNonNull(sanitizedName);
+ this.mEncodedLength = encodedLength;
+ this.mMaxEncodedLength = maxEncodedLength;
+ }
+
+ private NameValidationResult(Parcel in) {
+ this(in.readString(), in.readString(), in.readInt(), in.readInt());
+ }
+
+ /** Returns the original name that is being validated. */
+ @NonNull
+ public String getName() {
+ return mName;
+ }
+
+ /**
+ * Returns a sanitized copy of the original name with all unsupported characters
+ * replaced with spaces.
+ */
+ @NonNull
+ public String getSanitizedName() {
+ return mSanitizedName;
+ }
+
+ /**
+ * Returns whether the original name isValid.
+ *
+ * <p>If this returns false then inserts and updates using the name will throw an
+ * {@link IllegalArgumentException}
+ */
+ public boolean isValid() {
+ return mMaxEncodedLength > 0 && mEncodedLength <= mMaxEncodedLength
+ && Objects.equals(
+ mName, mSanitizedName);
+ }
+
+ /** Returns whether the character at the specified position is supported by the SIM. */
+ public boolean isSupportedCharacter(int position) {
+ return mName.charAt(position) == mSanitizedName.charAt(position);
+ }
+
+ /**
+ * Returns the number of bytes required to save the name.
+ *
+ * <p>This may be more than the number of characters in the name.
+ */
+ public int getEncodedLength() {
+ return mEncodedLength;
+ }
+
+ /**
+ * Returns the maximum number of bytes that are supported for the name.
+ *
+ * @see ElementaryFiles#NAME_MAX_LENGTH
+ */
+ public int getMaxEncodedLength() {
+ return mMaxEncodedLength;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mName);
+ dest.writeString(mSanitizedName);
+ dest.writeInt(mEncodedLength);
+ dest.writeInt(mMaxEncodedLength);
+ }
+ }
+ }
+
+ /** Constants for metadata about the elementary files of the SIM cards in the phone. */
+ public static final class ElementaryFiles {
+
+ /** {@link SubscriptionInfo#getSimSlotIndex()} of the SIM for this row. */
+ public static final String SLOT_INDEX = "slot_index";
+ /** {@link SubscriptionInfo#getSubscriptionId()} of the SIM for this row. */
+ public static final String SUBSCRIPTION_ID = "subscription_id";
+ /**
+ * The elementary file type for this row.
+ *
+ * @see ElementaryFiles#EF_ADN
+ * @see ElementaryFiles#EF_FDN
+ * @see ElementaryFiles#EF_SDN
+ */
+ public static final String EF_TYPE = "ef_type";
+ /** The maximum number of records supported by the elementary file. */
+ public static final String MAX_RECORDS = "max_records";
+ /** Count of the number of records that are currently stored in the elementary file. */
+ public static final String RECORD_COUNT = "record_count";
+ /** The maximum length supported for the name of a record in the elementary file. */
+ public static final String NAME_MAX_LENGTH = "name_max_length";
+ /**
+ * The maximum length supported for the phone number of a record in the elementary file.
+ */
+ public static final String PHONE_NUMBER_MAX_LENGTH = "phone_number_max_length";
+
+ /**
+ * A value for an elementary file that is not recognized.
+ *
+ * <p>Generally this should be ignored. If new values are added then this will be used
+ * for apps that target SDKs where they aren't defined.
+ */
+ public static final int EF_UNKNOWN = 0;
+ /**
+ * Type for accessing records in the "abbreviated dialing number" (ADN) elementary file on
+ * the SIM.
+ *
+ * <p>ADN records are typically user created.
+ */
+ public static final int EF_ADN = 1;
+ /**
+ * Type for accessing records in the "fixed dialing number" (FDN) elementary file on the
+ * SIM.
+ *
+ * <p>FDN numbers are the numbers that are allowed to dialed for outbound calls when FDN is
+ * enabled.
+ *
+ * <p>FDN records cannot be modified by applications. Hence, insert, update and
+ * delete methods operating on this Uri will throw UnsupportedOperationException
+ */
+ public static final int EF_FDN = 2;
+ /**
+ * Type for accessing records in the "service dialing number" (SDN) elementary file on the
+ * SIM.
+ *
+ * <p>Typically SDNs are preset numbers provided by the carrier for common operations (e.g.
+ * voicemail, check balance, etc).
+ *
+ * <p>SDN records cannot be modified by applications. Hence, insert, update and delete
+ * methods operating on this Uri will throw UnsupportedOperationException
+ */
+ public static final int EF_SDN = 3;
+ /** @hide */
+ @SystemApi
+ public static final String EF_ADN_PATH_SEGMENT = "adn";
+ /** @hide */
+ @SystemApi
+ public static final String EF_FDN_PATH_SEGMENT = "fdn";
+ /** @hide */
+ @SystemApi
+ public static final String EF_SDN_PATH_SEGMENT = "sdn";
+ /** The MIME type of CONTENT_URI providing a directory of ADN-like elementary files. */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/sim-elementary-file";
+ /** The MIME type of a CONTENT_URI subdirectory of a single ADN-like elementary file. */
+ public static final String CONTENT_ITEM_TYPE =
+ "vnd.android.cursor.item/sim-elementary-file";
+ /**
+ * The Uri path segment used to construct Uris for the metadata defined in this class.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String ELEMENTARY_FILES_PATH_SEGMENT = "elementary_files";
+
+ /** Content URI for the ADN-like elementary files available on the device. */
+ @NonNull
+ public static final Uri CONTENT_URI = AUTHORITY_URI
+ .buildUpon()
+ .appendPath(ELEMENTARY_FILES_PATH_SEGMENT).build();
+
+ private ElementaryFiles() {
+ }
+
+ /**
+ * Returns a content uri for a specific elementary file.
+ *
+ * <p>If a SIM with the specified subscriptionId is not present an exception will be thrown.
+ * If the SIM doesn't support the specified elementary file it will have a zero value for
+ * {@link #MAX_RECORDS}.
+ */
+ @NonNull
+ public static Uri getItemUri(int subscriptionId, @EfType int efType) {
+ return CONTENT_URI.buildUpon().appendPath(SUBSCRIPTION_ID_PATH_SEGMENT)
+ .appendPath(String.valueOf(subscriptionId))
+ .appendPath(getEfUriPath(efType))
+ .build();
+ }
+
+ /**
+ * Annotation for the valid elementary file types.
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = {"EF"},
+ value = {EF_UNKNOWN, EF_ADN, EF_FDN, EF_SDN})
+ public @interface EfType {
+ }
+ }
+}
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index 017f405..a79b197 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -80,6 +80,7 @@
public static final int KM_TAG_MIN_SECONDS_BETWEEN_OPS =
Tag.MIN_SECONDS_BETWEEN_OPS; // KM_UINT | 403;
public static final int KM_TAG_MAX_USES_PER_BOOT = Tag.MAX_USES_PER_BOOT; // KM_UINT | 404;
+ public static final int KM_TAG_USAGE_COUNT_LIMIT = Tag.USAGE_COUNT_LIMIT; // KM_UINT | 405;
public static final int KM_TAG_USER_ID = Tag.USER_ID; // KM_UINT | 501;
public static final int KM_TAG_USER_SECURE_ID = Tag.USER_SECURE_ID; // KM_ULONG_REP | 502;
@@ -129,6 +130,15 @@
public static final int KM_TAG_ASSOCIATED_DATA = Tag.ASSOCIATED_DATA; // KM_BYTES | 1000;
public static final int KM_TAG_NONCE = Tag.NONCE; // KM_BYTES | 1001;
public static final int KM_TAG_MAC_LENGTH = Tag.MAC_LENGTH; // KM_UINT | 1003;
+ public static final int KM_TAG_RESET_SINCE_ID_ROTATION =
+ Tag.RESET_SINCE_ID_ROTATION; // KM_BOOL | 1004
+ public static final int KM_TAG_CONFIRMATION_TOKEN = Tag.CONFIRMATION_TOKEN; // KM_BYTES | 1005;
+ public static final int KM_TAG_CERTIFICATE_SERIAL = Tag.CERTIFICATE_SERIAL; // KM_UINT | 1006;
+ public static final int KM_TAG_CERTIFICATE_SUBJECT = Tag.CERTIFICATE_SUBJECT; // KM_UINT | 1007;
+ public static final int KM_TAG_CERTIFICATE_NOT_BEFORE =
+ Tag.CERTIFICATE_NOT_BEFORE; // KM_DATE | 1008;
+ public static final int KM_TAG_CERTIFICATE_NOT_AFTER =
+ Tag.CERTIFICATE_NOT_AFTER; // KM_DATE | 1009;
// Algorithm values.
public static final int KM_ALGORITHM_RSA = Algorithm.RSA;
@@ -177,6 +187,7 @@
public static final int KM_PURPOSE_SIGN = KeyPurpose.SIGN;
public static final int KM_PURPOSE_VERIFY = KeyPurpose.VERIFY;
public static final int KM_PURPOSE_WRAP = KeyPurpose.WRAP_KEY;
+ public static final int KM_PURPOSE_AGREE_KEY = KeyPurpose.AGREE_KEY;
// Key formats.
public static final int KM_KEY_FORMAT_X509 = KeyFormat.X509;
@@ -315,6 +326,10 @@
ErrorCode.HARDWARE_TYPE_UNAVAILABLE; // -68;
public static final int KM_ERROR_DEVICE_LOCKED =
ErrorCode.DEVICE_LOCKED; // -72;
+ public static final int KM_ERROR_MISSING_NOT_BEFORE =
+ ErrorCode.MISSING_NOT_BEFORE; // -80;
+ public static final int KM_ERROR_MISSING_NOT_AFTER =
+ ErrorCode.MISSING_NOT_AFTER; // -80;
public static final int KM_ERROR_UNIMPLEMENTED =
ErrorCode.UNIMPLEMENTED; // -100;
public static final int KM_ERROR_VERSION_MISMATCH =
diff --git a/core/java/android/service/dataloader/OWNERS b/core/java/android/service/dataloader/OWNERS
new file mode 100644
index 0000000..7f3906b
--- /dev/null
+++ b/core/java/android/service/dataloader/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/os/incremental/OWNERS
\ No newline at end of file
diff --git a/core/java/android/service/resumeonreboot/ResumeOnRebootService.java b/core/java/android/service/resumeonreboot/ResumeOnRebootService.java
index 4ebaa96..ad49ffd 100644
--- a/core/java/android/service/resumeonreboot/ResumeOnRebootService.java
+++ b/core/java/android/service/resumeonreboot/ResumeOnRebootService.java
@@ -87,7 +87,9 @@
* Implementation for wrapping the opaque blob used for resume-on-reboot prior to
* reboot. The service should not assume any structure of the blob to be wrapped. The
* implementation should wrap the opaque blob in a reasonable time or throw {@link IOException}
- * if it's unable to complete the action.
+ * if it's unable to complete the action due to retry-able errors (e.g network errors)
+ * and {@link IllegalArgumentException} if {@code wrapBlob} fails due to fatal errors
+ * (e.g corrupted blob).
*
* @param blob The opaque blob with size on the order of 100 bytes.
* @param lifeTimeInMillis The life time of the blob. This must be strictly enforced by the
@@ -95,7 +97,8 @@
* this function after expiration should
* fail.
* @return Wrapped blob to be persisted across reboot with size on the order of 100 bytes.
- * @throws IOException if the implementation is unable to wrap the blob successfully.
+ * @throws IOException if the implementation is unable to wrap the blob successfully due to
+ * retry-able errors.
*/
@NonNull
public abstract byte[] onWrap(@NonNull byte[] blob, @DurationMillisLong long lifeTimeInMillis)
@@ -106,12 +109,13 @@
* operation would happen after reboot during direct boot mode (i.e before device is unlocked
* for the first time). The implementation should unwrap the wrapped blob in a reasonable time
* and returns the result or throw {@link IOException} if it's unable to complete the action
- * and {@link IllegalArgumentException} if {@code unwrapBlob} fails because the wrappedBlob is
- * stale.
+ * due to retry-able errors (e.g network error) and {@link IllegalArgumentException}
+ * if {@code unwrapBlob} fails due to fatal errors (e.g stale or corrupted blob).
*
* @param wrappedBlob The wrapped blob with size on the order of 100 bytes.
* @return Unwrapped blob used for resume-on-reboot with the size on the order of 100 bytes.
- * @throws IOException if the implementation is unable to unwrap the wrapped blob successfully.
+ * @throws IOException if the implementation is unable to unwrap the wrapped blob successfully
+ * due to retry-able errors.
*/
@NonNull
public abstract byte[] onUnwrap(@NonNull byte[] wrappedBlob) throws IOException;
diff --git a/core/java/android/service/smartspace/OWNERS b/core/java/android/service/smartspace/OWNERS
new file mode 100644
index 0000000..19ef9d7
--- /dev/null
+++ b/core/java/android/service/smartspace/OWNERS
@@ -0,0 +1,2 @@
+srazdan@google.com
+alexmang@google.com
\ No newline at end of file
diff --git a/core/java/android/util/OWNERS b/core/java/android/util/OWNERS
index 8f3d9f6..14aa386 100644
--- a/core/java/android/util/OWNERS
+++ b/core/java/android/util/OWNERS
@@ -1,3 +1,6 @@
per-file FeatureFlagUtils.java = sbasi@google.com
per-file FeatureFlagUtils.java = tmfang@google.com
per-file FeatureFlagUtils.java = asapperstein@google.com
+
+per-file TypedValue.java = file:/core/java/android/content/res/OWNERS
+per-file AttributeSet.java = file:/core/java/android/content/res/OWNERS
diff --git a/core/java/android/uwb/RangingManager.java b/core/java/android/uwb/RangingManager.java
index 5ac95d4..c0d8187 100644
--- a/core/java/android/uwb/RangingManager.java
+++ b/core/java/android/uwb/RangingManager.java
@@ -94,7 +94,8 @@
synchronized (this) {
if (!hasSession(sessionHandle)) {
Log.w(TAG,
- "onRangingOpened - received unexpected SessionHandle: " + sessionHandle);
+ "onRangingOpenedFailed - received unexpected SessionHandle: "
+ + sessionHandle);
return;
}
@@ -124,7 +125,7 @@
@RangingChangeReason int reason, PersistableBundle params) {
synchronized (this) {
if (!hasSession(sessionHandle)) {
- Log.w(TAG, "onRangingStartFailed - received unexpected SessionHandle: "
+ Log.w(TAG, "onRangingReconfigureFailed - received unexpected SessionHandle: "
+ sessionHandle);
return;
}
diff --git a/core/java/android/view/FrameMetrics.java b/core/java/android/view/FrameMetrics.java
index f8500fa..6511a94 100644
--- a/core/java/android/view/FrameMetrics.java
+++ b/core/java/android/view/FrameMetrics.java
@@ -149,7 +149,7 @@
* <p>
* The time value that was used in all the vsync listeners and drawing for
* the frame (Choreographer frame callbacks, animations,
- * {@link View#getDrawingTime()}, etc…)
+ * {@link View#getDrawingTime()}, etc.)
* </p>
*/
public static final int VSYNC_TIMESTAMP = 11;
diff --git a/core/java/android/view/inputmethod/InlineSuggestionInfo.java b/core/java/android/view/inputmethod/InlineSuggestionInfo.java
index 1c703ec..a17c012 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionInfo.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionInfo.java
@@ -82,6 +82,7 @@
public static InlineSuggestionInfo newInlineSuggestionInfo(
@NonNull InlinePresentationSpec presentationSpec,
@NonNull @Source String source,
+ @SuppressLint("NullableCollection")
@Nullable String[] autofillHints, @NonNull @Type String type, boolean isPinned) {
return new InlineSuggestionInfo(presentationSpec, source, autofillHints, type, isPinned);
}
diff --git a/core/java/android/view/inputmethod/OWNERS b/core/java/android/view/inputmethod/OWNERS
index e6a04da..d7db7c7 100644
--- a/core/java/android/view/inputmethod/OWNERS
+++ b/core/java/android/view/inputmethod/OWNERS
@@ -2,3 +2,5 @@
set noparent
include /services/core/java/com/android/server/inputmethod/OWNERS
+
+per-file *InlineSuggestion* = file:/core/java/android/service/autofill/OWNERS
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index 023d9ff2..20230e7 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -98,9 +98,17 @@
public abstract boolean acceptThirdPartyCookies(WebView webview);
/**
- * Sets a cookie for the given URL. Any existing cookie with the same host,
- * path and name will be replaced with the new cookie. The cookie being set
- * will be ignored if it is expired.
+ * Sets a single cookie (key-value pair) for the given URL. Any existing cookie with the same
+ * host, path and name will be replaced with the new cookie. The cookie being set
+ * will be ignored if it is expired. To set multiple cookies, your application should invoke
+ * this method multiple times.
+ *
+ * <p>The {@code value} parameter must follow the format of the {@code Set-Cookie} HTTP
+ * response header defined by
+ * <a href="https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03">RFC6265bis</a>.
+ * This is a key-value pair of the form {@code "key=value"}, optionally followed by a list of
+ * cookie attributes delimited with semicolons (ex. {@code "key=value; Max-Age=123"}). Please
+ * consult the RFC specification for a list of valid attributes.
*
* <p class="note"><b>Note:</b> if specifying a {@code value} containing the {@code "Secure"}
* attribute, {@code url} must use the {@code "https://"} scheme.
@@ -112,13 +120,20 @@
public abstract void setCookie(String url, String value);
/**
- * Sets a cookie for the given URL. Any existing cookie with the same host,
- * path and name will be replaced with the new cookie. The cookie being set
- * will be ignored if it is expired.
- * <p>
- * This method is asynchronous.
- * If a {@link ValueCallback} is provided,
- * {@link ValueCallback#onReceiveValue(T) onReceiveValue()} will be called on the current
+ * Sets a single cookie (key-value pair) for the given URL. Any existing cookie with the same
+ * host, path and name will be replaced with the new cookie. The cookie being set
+ * will be ignored if it is expired. To set multiple cookies, your application should invoke
+ * this method multiple times.
+ *
+ * <p>The {@code value} parameter must follow the format of the {@code Set-Cookie} HTTP
+ * response header defined by
+ * <a href="https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03">RFC6265bis</a>.
+ * This is a key-value pair of the form {@code "key=value"}, optionally followed by a list of
+ * cookie attributes delimited with semicolons (ex. {@code "key=value; Max-Age=123"}). Please
+ * consult the RFC specification for a list of valid attributes.
+ *
+ * <p>This method is asynchronous. If a {@link ValueCallback} is provided,
+ * {@link ValueCallback#onReceiveValue} will be called on the current
* thread's {@link android.os.Looper} once the operation is complete.
* The value provided to the callback indicates whether the cookie was set successfully.
* You can pass {@code null} as the callback if you don't need to know when the operation
@@ -137,7 +152,10 @@
callback);
/**
- * Gets the cookies for the given URL.
+ * Gets all the cookies for the given URL. This may return multiple key-value pairs if multiple
+ * cookies are associated with this URL, in which case each cookie will be delimited by {@code
+ * "; "} characters (semicolon followed by a space). Each key-value pair will be of the form
+ * {@code "key=value"}.
*
* @param url the URL for which the cookies are requested
* @return value the cookies as a string, using the format of the 'Cookie'
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index 502680d..7b4fcbf 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.app.ActivityManager;
import android.os.RemoteException;
@@ -101,6 +102,7 @@
/** Gets direct child tasks (ordered from top-to-bottom) */
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
@Nullable
+ @SuppressLint("NullableCollection")
public static List<ActivityManager.RunningTaskInfo> getChildTasks(
@NonNull WindowContainerToken parent, @NonNull int[] activityTypes) {
try {
@@ -113,6 +115,7 @@
/** Gets all root tasks on a display (ordered from top-to-bottom) */
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
@Nullable
+ @SuppressLint("NullableCollection")
public static List<ActivityManager.RunningTaskInfo> getRootTasks(
int displayId, @NonNull int[] activityTypes) {
try {
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index e6a1661..beef982 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -129,7 +129,7 @@
void noteWifiBatchedScanStartedFromSource(in WorkSource ws, int csph);
void noteWifiBatchedScanStoppedFromSource(in WorkSource ws);
void noteWifiRadioPowerState(int powerState, long timestampNs, int uid);
- void noteNetworkInterfaceType(String iface, int type);
+ void noteNetworkInterfaceForTransports(String iface, in int[] transportTypes);
void noteNetworkStatsEnabled();
void noteDeviceIdleMode(int mode, String activeReason, int activeUid);
void setBatteryState(int status, int health, int plugType, int level, int temp, int volt,
diff --git a/core/java/com/android/internal/app/OWNERS b/core/java/com/android/internal/app/OWNERS
index 99692d0..7ade05c 100644
--- a/core/java/com/android/internal/app/OWNERS
+++ b/core/java/com/android/internal/app/OWNERS
@@ -5,3 +5,4 @@
per-file NetInitiatedActivity.java = file:/location/java/android/location/OWNERS
per-file IVoice* = file:/core/java/android/service/voice/OWNERS
per-file *Hotword* = file:/core/java/android/service/voice/OWNERS
+per-file *BatteryStats* = file:/BATTERY_STATS_OWNERS
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 16991b4..1270185 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1210,25 +1210,6 @@
if (TextUtils.isEmpty(packageName)) {
pm.setDefaultBrowserPackageNameAsUser(ri.activityInfo.packageName, userId);
}
- } else {
- // Update Domain Verification status
- ComponentName cn = intent.getComponent();
- String packageName = cn.getPackageName();
- String dataScheme = (data != null) ? data.getScheme() : null;
-
- boolean isHttpOrHttps = (dataScheme != null) &&
- (dataScheme.equals(IntentFilter.SCHEME_HTTP) ||
- dataScheme.equals(IntentFilter.SCHEME_HTTPS));
-
- boolean isViewAction = (action != null) && action.equals(Intent.ACTION_VIEW);
- boolean hasCategoryBrowsable = (categories != null) &&
- categories.contains(Intent.CATEGORY_BROWSABLE);
-
- if (isHttpOrHttps && isViewAction && hasCategoryBrowsable) {
- pm.updateIntentVerificationStatusAsUser(packageName,
- PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS,
- userId);
- }
}
} else {
try {
diff --git a/core/java/com/android/internal/net/VpnInfo.java b/core/java/com/android/internal/net/VpnInfo.java
deleted file mode 100644
index e74af5e..0000000
--- a/core/java/com/android/internal/net/VpnInfo.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2015 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.net;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.Arrays;
-
-/**
- * A lightweight container used to carry information of the ongoing VPN.
- * Internal use only..
- *
- * @hide
- */
-public class VpnInfo implements Parcelable {
- public int ownerUid;
- public String vpnIface;
- public String[] underlyingIfaces;
-
- @Override
- public String toString() {
- return "VpnInfo{"
- + "ownerUid=" + ownerUid
- + ", vpnIface='" + vpnIface + '\''
- + ", underlyingIfaces='" + Arrays.toString(underlyingIfaces) + '\''
- + '}';
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(ownerUid);
- dest.writeString(vpnIface);
- dest.writeStringArray(underlyingIfaces);
- }
-
- public static final Parcelable.Creator<VpnInfo> CREATOR = new Parcelable.Creator<VpnInfo>() {
- @Override
- public VpnInfo createFromParcel(Parcel source) {
- VpnInfo info = new VpnInfo();
- info.ownerUid = source.readInt();
- info.vpnIface = source.readString();
- info.underlyingIfaces = source.readStringArray();
- return info;
- }
-
- @Override
- public VpnInfo[] newArray(int size) {
- return new VpnInfo[size];
- }
- };
-}
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index 8bfc28e..ae680e0 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -23,7 +23,6 @@
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.hardware.SensorManager;
-import android.net.ConnectivityManager;
import android.os.BatteryStats;
import android.os.BatteryStats.Uid;
import android.os.Build;
@@ -37,6 +36,7 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.telephony.TelephonyManager;
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -148,12 +148,11 @@
boolean mHasBluetoothPowerReporting = false;
public static boolean checkWifiOnly(Context context) {
- ConnectivityManager cm = (ConnectivityManager) context.getSystemService(
- Context.CONNECTIVITY_SERVICE);
- if (cm == null) {
+ final TelephonyManager tm = context.getSystemService(TelephonyManager.class);
+ if (tm == null) {
return false;
}
- return !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+ return !tm.isDataCapable();
}
public static boolean checkHasWifiPowerReporting(BatteryStats stats, PowerProfile profile) {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 1071d9f..2d75b70 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -16,6 +16,8 @@
package com.android.internal.os;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.os.BatteryStatsManager.NUM_WIFI_STATES;
import static android.os.BatteryStatsManager.NUM_WIFI_SUPPL_STATES;
@@ -32,7 +34,6 @@
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.hardware.usb.UsbManager;
-import android.net.ConnectivityManager;
import android.net.INetworkStatsService;
import android.net.NetworkStats;
import android.net.Uri;
@@ -101,6 +102,7 @@
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.XmlUtils;
+import com.android.net.module.util.NetworkCapabilitiesUtils;
import libcore.util.EmptyArray;
@@ -6099,11 +6101,12 @@
}
/** @hide */
- public void noteNetworkInterfaceType(String iface, int networkType) {
+ public void noteNetworkInterfaceForTransports(String iface, int[] transportTypes) {
if (TextUtils.isEmpty(iface)) return;
+ final int displayTransport = NetworkCapabilitiesUtils.getDisplayTransport(transportTypes);
synchronized (mModemNetworkLock) {
- if (ConnectivityManager.isNetworkTypeMobile(networkType)) {
+ if (displayTransport == TRANSPORT_CELLULAR) {
mModemIfaces = includeInStringArray(mModemIfaces, iface);
if (DEBUG) Slog.d(TAG, "Note mobile iface " + iface + ": " + mModemIfaces);
} else {
@@ -6113,7 +6116,7 @@
}
synchronized (mWifiNetworkLock) {
- if (ConnectivityManager.isNetworkTypeWifi(networkType)) {
+ if (displayTransport == TRANSPORT_WIFI) {
mWifiIfaces = includeInStringArray(mWifiIfaces, iface);
if (DEBUG) Slog.d(TAG, "Note wifi iface " + iface + ": " + mWifiIfaces);
} else {
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 0381a75..527d10f 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -103,7 +103,7 @@
*/
public static final int PROFILE_FROM_SHELL = 1 << 15;
- /*
+ /**
* Enable using the ART app image startup cache
*/
public static final int USE_APP_IMAGE_STARTUP_CACHE = 1 << 16;
@@ -116,13 +116,6 @@
*/
public static final int DEBUG_IGNORE_APP_SIGNAL_HANDLER = 1 << 17;
- /**
- * Disable runtime access to {@link android.annotation.TestApi} annotated members.
- *
- * <p>This only takes effect if Hidden API access restrictions are enabled as well.
- */
- public static final int DISABLE_TEST_API_ENFORCEMENT_POLICY = 1 << 18;
-
public static final int MEMORY_TAG_LEVEL_MASK = (1 << 19) | (1 << 20);
/**
* Enable pointer tagging in this process.
diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java
index 0dd8b59..db7cbbc 100644
--- a/core/java/com/android/internal/os/ZygoteServer.java
+++ b/core/java/com/android/internal/os/ZygoteServer.java
@@ -492,10 +492,12 @@
long elapsedTimeMs = System.currentTimeMillis() - mUsapPoolRefillTriggerTimestamp;
if (elapsedTimeMs >= mUsapPoolRefillDelayMs) {
- // Normalize the poll timeout value when the time between one poll event and the
- // next pushes us over the delay value. This prevents poll receiving a 0
- // timeout value, which would result in it returning immediately.
- pollTimeoutMs = -1;
+ // The refill delay has elapsed during the period between poll invocations.
+ // We will now check for any currently ready file descriptors before refilling
+ // the USAP pool.
+ pollTimeoutMs = 0;
+ mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
+ mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED;
} else if (elapsedTimeMs <= 0) {
// This can occur if the clock used by currentTimeMillis is reset, which is
@@ -517,9 +519,11 @@
}
if (pollReturnValue == 0) {
- // The poll timeout has been exceeded. This only occurs when we have finished the
- // USAP pool refill delay period.
-
+ // The poll returned zero results either when the timeout value has been exceeded
+ // or when a non-blocking poll is issued and no FDs are ready. In either case it
+ // is time to refill the pool. This will result in a duplicate assignment when
+ // the non-blocking poll returns zero results, but it avoids an additional
+ // conditional in the else branch.
mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED;
diff --git a/core/java/com/android/internal/widget/OWNERS b/core/java/com/android/internal/widget/OWNERS
index ae566c3..d284d51 100644
--- a/core/java/com/android/internal/widget/OWNERS
+++ b/core/java/com/android/internal/widget/OWNERS
@@ -5,3 +5,17 @@
per-file *LockScreen* = file:/services/core/java/com/android/server/locksettings/OWNERS
per-file *Lockscreen* = file:/services/core/java/com/android/server/locksettings/OWNERS
per-file *LockSettings* = file:/services/core/java/com/android/server/locksettings/OWNERS
+
+# Notification related
+per-file *Notification* = file:/services/core/java/com/android/server/notification/OWNERS
+per-file *Messaging* = file:/services/core/java/com/android/server/notification/OWNERS
+per-file *Message* = file:/services/core/java/com/android/server/notification/OWNERS
+per-file *Conversation* = file:/services/core/java/com/android/server/notification/OWNERS
+per-file *People* = file:/services/core/java/com/android/server/notification/OWNERS
+per-file *ImageResolver* = file:/services/core/java/com/android/server/notification/OWNERS
+per-file CallLayout.java = file:/services/core/java/com/android/server/notification/OWNERS
+per-file CachingIconView.java = file:/services/core/java/com/android/server/notification/OWNERS
+per-file ImageFloatingTextView.java = file:/services/core/java/com/android/server/notification/OWNERS
+per-file ObservableTextView.java = file:/services/core/java/com/android/server/notification/OWNERS
+per-file RemeasuringLinearLayout.java = file:/services/core/java/com/android/server/notification/OWNERS
+per-file ViewClippingUtil.java = file:/services/core/java/com/android/server/notification/OWNERS
diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java
index ac2361d..c46d708 100644
--- a/core/java/com/android/server/BootReceiver.java
+++ b/core/java/com/android/server/BootReceiver.java
@@ -23,7 +23,6 @@
import android.os.Build;
import android.os.DropBoxManager;
import android.os.Environment;
-import android.os.FileObserver;
import android.os.FileUtils;
import android.os.RecoverySystem;
import android.os.RemoteException;
@@ -75,7 +74,6 @@
SystemProperties.getInt("ro.debuggable", 0) == 1 ? 196608 : 65536;
private static final int GMSCORE_LASTK_LOG_SIZE = 196608;
- private static final File TOMBSTONE_DIR = new File("/data/tombstones");
private static final String TAG_TOMBSTONE = "SYSTEM_TOMBSTONE";
// The pre-froyo package and class of the system updater, which
@@ -86,9 +84,6 @@
private static final String OLD_UPDATER_CLASS =
"com.google.android.systemupdater.SystemUpdateReceiver";
- // Keep a reference to the observer so the finalizer doesn't disable it.
- private static FileObserver sTombstoneObserver = null;
-
private static final String LOG_FILES_FILE = "log-files.xml";
private static final AtomicFile sFile = new AtomicFile(new File(
Environment.getDataSystemDirectory(), LOG_FILES_FILE), "log-files");
@@ -154,7 +149,7 @@
Downloads.removeAllDownloadsByPackage(context, OLD_UPDATER_PACKAGE, OLD_UPDATER_CLASS);
}
- private String getPreviousBootHeaders() {
+ private static String getPreviousBootHeaders() {
try {
return FileUtils.readTextFile(lastHeaderFile, 0, null);
} catch (IOException e) {
@@ -162,7 +157,7 @@
}
}
- private String getCurrentBootHeaders() throws IOException {
+ private static String getCurrentBootHeaders() throws IOException {
return new StringBuilder(512)
.append("Build: ").append(Build.FINGERPRINT).append("\n")
.append("Hardware: ").append(Build.BOARD).append("\n")
@@ -176,7 +171,7 @@
}
- private String getBootHeadersToLogAndUpdate() throws IOException {
+ private static String getBootHeadersToLogAndUpdate() throws IOException {
final String oldHeaders = getPreviousBootHeaders();
final String newHeaders = getCurrentBootHeaders();
@@ -248,38 +243,27 @@
logFsMountTime();
addFsckErrorsToDropBoxAndLogFsStat(db, timestamps, headers, -LOG_SIZE, "SYSTEM_FSCK");
logSystemServerShutdownTimeMetrics();
-
- // Scan existing tombstones (in case any new ones appeared)
- File[] tombstoneFiles = TOMBSTONE_DIR.listFiles();
- for (int i = 0; tombstoneFiles != null && i < tombstoneFiles.length; i++) {
- if (tombstoneFiles[i].isFile()) {
- addFileToDropBox(db, timestamps, headers, tombstoneFiles[i].getPath(),
- LOG_SIZE, "SYSTEM_TOMBSTONE");
- }
- }
-
writeTimestamps(timestamps);
+ }
- // Start watching for new tombstone files; will record them as they occur.
- // This gets registered with the singleton file observer thread.
- sTombstoneObserver = new FileObserver(TOMBSTONE_DIR.getPath(), FileObserver.CREATE) {
- @Override
- public void onEvent(int event, String path) {
- HashMap<String, Long> timestamps = readTimestamps();
- try {
- File file = new File(TOMBSTONE_DIR, path);
- if (file.isFile() && file.getName().startsWith("tombstone_")) {
- addFileToDropBox(db, timestamps, headers, file.getPath(), LOG_SIZE,
- TAG_TOMBSTONE);
- }
- } catch (IOException e) {
- Slog.e(TAG, "Can't log tombstone", e);
- }
- writeTimestamps(timestamps);
- }
- };
-
- sTombstoneObserver.startWatching();
+ /**
+ * Add a tombstone to the DropBox.
+ *
+ * @param ctx Context
+ * @param tombstone path to the tombstone
+ */
+ public static void addTombstoneToDropBox(Context ctx, File tombstone) {
+ final DropBoxManager db = ctx.getSystemService(DropBoxManager.class);
+ final String bootReason = SystemProperties.get("ro.boot.bootreason", null);
+ HashMap<String, Long> timestamps = readTimestamps();
+ try {
+ final String headers = getBootHeadersToLogAndUpdate();
+ addFileToDropBox(db, timestamps, headers, tombstone.getPath(), LOG_SIZE,
+ TAG_TOMBSTONE);
+ } catch (IOException e) {
+ Slog.e(TAG, "Can't log tombstone", e);
+ }
+ writeTimestamps(timestamps);
}
private static void addLastkToDropBox(
@@ -764,7 +748,7 @@
}
}
- private void writeTimestamps(HashMap<String, Long> timestamps) {
+ private static void writeTimestamps(HashMap<String, Long> timestamps) {
synchronized (sFile) {
final FileOutputStream stream;
try {
diff --git a/core/jni/android_media_AudioErrors.h b/core/jni/android_media_AudioErrors.h
index c17a020..13c9115 100644
--- a/core/jni/android_media_AudioErrors.h
+++ b/core/jni/android_media_AudioErrors.h
@@ -35,7 +35,7 @@
AUDIO_JAVA_WOULD_BLOCK = -7,
};
-static inline jint nativeToJavaStatus(status_t status) {
+static constexpr inline jint nativeToJavaStatus(status_t status) {
switch (status) {
case NO_ERROR:
return AUDIO_JAVA_SUCCESS;
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 7a5c383..065c79b 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -263,18 +263,7 @@
return (jint) AUDIO_JAVA_ERROR;
}
- // TODO: replace when we land matching AudioTrack::set() in frameworks/av in r or r-tv-dev.
- if (tunerConfiguration != nullptr) {
- const TunerConfigurationHelper tunerHelper(env, tunerConfiguration);
- ALOGE("Error creating AudioTrack: unsupported tuner contentId:%d syncId:%d",
- tunerHelper.getContentId(), tunerHelper.getSyncId());
- return (jint)AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
- }
- // TODO: replace when we land matching AudioTrack::set() in frameworks/av in r or r-tv-dev.
- if (encapsulationMode != 0 /* ENCAPSULATION_MODE_NONE */) {
- ALOGE("Error creating AudioTrack: unsupported encapsulationMode %d", encapsulationMode);
- return (jint)AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
- }
+ const TunerConfigurationHelper tunerHelper(env, tunerConfiguration);
jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
if (nSession == NULL) {
@@ -369,6 +358,18 @@
offloadInfo.stream_type = AUDIO_STREAM_MUSIC; //required for offload
}
+ if (encapsulationMode != 0) {
+ offloadInfo = AUDIO_INFO_INITIALIZER;
+ offloadInfo.format = format;
+ offloadInfo.sample_rate = sampleRateInHertz;
+ offloadInfo.channel_mask = nativeChannelMask;
+ offloadInfo.stream_type = AUDIO_STREAM_MUSIC;
+ offloadInfo.encapsulation_mode =
+ static_cast<audio_encapsulation_mode_t>(encapsulationMode);
+ offloadInfo.content_id = tunerHelper.getContentId();
+ offloadInfo.sync_id = tunerHelper.getSyncId();
+ }
+
// initialize the native AudioTrack object
status_t status = NO_ERROR;
switch (memoryMode) {
@@ -389,7 +390,8 @@
sessionId, // audio session ID
offload ? AudioTrack::TRANSFER_SYNC_NOTIF_CALLBACK
: AudioTrack::TRANSFER_SYNC,
- offload ? &offloadInfo : NULL, -1, -1, // default uid, pid values
+ (offload || encapsulationMode) ? &offloadInfo : NULL, -1,
+ -1, // default uid, pid values
paa.get());
break;
@@ -1364,8 +1366,7 @@
return (jint)AUDIO_JAVA_ERROR;
}
- // TODO: replace in r-dev or r-tv-dev with code if HW is able to set audio mix level.
- return (jint)AUDIO_JAVA_ERROR;
+ return nativeToJavaStatus(lpTrack->setAudioDescriptionMixLevel(level));
}
static jint android_media_AudioTrack_getAudioDescriptionMixLeveldB(JNIEnv *env, jobject thiz,
@@ -1381,12 +1382,10 @@
return (jint)AUDIO_JAVA_ERROR;
}
- // TODO: replace in r-dev or r-tv-dev with code if HW is able to set audio mix level.
- // By contract we can return -infinity if unsupported.
- *nativeLevel = -std::numeric_limits<float>::infinity();
+ status_t status = lpTrack->getAudioDescriptionMixLevel(reinterpret_cast<float *>(nativeLevel));
env->ReleasePrimitiveArrayCritical(level, nativeLevel, 0 /* mode */);
- nativeLevel = nullptr;
- return (jint)AUDIO_JAVA_SUCCESS;
+
+ return nativeToJavaStatus(status);
}
static jint android_media_AudioTrack_setDualMonoMode(JNIEnv *env, jobject thiz, jint dualMonoMode) {
@@ -1396,8 +1395,8 @@
return (jint)AUDIO_JAVA_ERROR;
}
- // TODO: replace in r-dev or r-tv-dev with code if HW is able to set audio mix level.
- return (jint)AUDIO_JAVA_ERROR;
+ return nativeToJavaStatus(
+ lpTrack->setDualMonoMode(static_cast<audio_dual_mono_mode_t>(dualMonoMode)));
}
static jint android_media_AudioTrack_getDualMonoMode(JNIEnv *env, jobject thiz,
@@ -1407,18 +1406,17 @@
ALOGE("%s: AudioTrack not initialized", __func__);
return (jint)AUDIO_JAVA_ERROR;
}
- jfloat *nativeDualMonoMode = (jfloat *)env->GetPrimitiveArrayCritical(dualMonoMode, NULL);
+ jint *nativeDualMonoMode = (jint *)env->GetPrimitiveArrayCritical(dualMonoMode, NULL);
if (nativeDualMonoMode == nullptr) {
ALOGE("%s: Cannot retrieve dualMonoMode pointer", __func__);
return (jint)AUDIO_JAVA_ERROR;
}
- // TODO: replace in r-dev or r-tv-dev with code if HW is able to select dual mono mode.
- // By contract we can return DUAL_MONO_MODE_OFF if unsupported.
- *nativeDualMonoMode = 0; // DUAL_MONO_MODE_OFF for now.
+ status_t status = lpTrack->getDualMonoMode(
+ reinterpret_cast<audio_dual_mono_mode_t *>(nativeDualMonoMode));
env->ReleasePrimitiveArrayCritical(dualMonoMode, nativeDualMonoMode, 0 /* mode */);
- nativeDualMonoMode = nullptr;
- return (jint)AUDIO_JAVA_SUCCESS;
+
+ return nativeToJavaStatus(status);
}
// ----------------------------------------------------------------------------
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 2155246..e2af87e 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -18,6 +18,7 @@
#include <vector>
+#include <android/file_descriptor_jni.h>
#include <arpa/inet.h>
#include <linux/filter.h>
#include <linux/if_arp.h>
@@ -83,7 +84,7 @@
filter_code,
};
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ int fd = AFileDescriptor_getFD(env, javaFd);
if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
jniThrowExceptionFmt(env, "java/net/SocketException",
"setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
@@ -93,7 +94,7 @@
static void android_net_utils_detachBPFFilter(JNIEnv *env, jobject clazz, jobject javaFd)
{
int optval_ignored = 0;
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ int fd = AFileDescriptor_getFD(env, javaFd);
if (setsockopt(fd, SOL_SOCKET, SO_DETACH_FILTER, &optval_ignored, sizeof(optval_ignored)) !=
0) {
jniThrowExceptionFmt(env, "java/net/SocketException",
@@ -117,10 +118,9 @@
return (jboolean) !setNetworkForResolv(netId);
}
-static jint android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz, jint socket,
- jint netId)
-{
- return setNetworkForSocket(netId, socket);
+static jint android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz, jobject javaFd,
+ jint netId) {
+ return setNetworkForSocket(netId, AFileDescriptor_getFD(env, javaFd));
}
static jboolean android_net_utils_protectFromVpn(JNIEnv *env, jobject thiz, jint socket)
@@ -128,6 +128,10 @@
return (jboolean) !protectFromVpn(socket);
}
+static jboolean android_net_utils_protectFromVpnWithFd(JNIEnv *env, jobject thiz, jobject javaFd) {
+ return android_net_utils_protectFromVpn(env, thiz, AFileDescriptor_getFD(env, javaFd));
+}
+
static jboolean android_net_utils_queryUserAccess(JNIEnv *env, jobject thiz, jint uid, jint netId)
{
return (jboolean) !queryUserAccess(uid, netId);
@@ -178,7 +182,7 @@
}
static jobject android_net_utils_resNetworkResult(JNIEnv *env, jobject thiz, jobject javaFd) {
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ int fd = AFileDescriptor_getFD(env, javaFd);
int rcode;
std::vector<uint8_t> buf(MAXPACKETSIZE, 0);
@@ -205,7 +209,7 @@
}
static void android_net_utils_resNetworkCancel(JNIEnv *env, jobject thiz, jobject javaFd) {
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ int fd = AFileDescriptor_getFD(env, javaFd);
resNetworkCancel(fd);
jniSetFileDescriptorOfFD(env, javaFd, -1);
}
@@ -231,7 +235,7 @@
return NULL;
}
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ int fd = AFileDescriptor_getFD(env, javaFd);
struct tcp_repair_window trw = {};
socklen_t size = sizeof(trw);
@@ -271,8 +275,9 @@
{ "bindProcessToNetwork", "(I)Z", (void*) android_net_utils_bindProcessToNetwork },
{ "getBoundNetworkForProcess", "()I", (void*) android_net_utils_getBoundNetworkForProcess },
{ "bindProcessToNetworkForHostResolution", "(I)Z", (void*) android_net_utils_bindProcessToNetworkForHostResolution },
- { "bindSocketToNetwork", "(II)I", (void*) android_net_utils_bindSocketToNetwork },
- { "protectFromVpn", "(I)Z", (void*)android_net_utils_protectFromVpn },
+ { "bindSocketToNetwork", "(Ljava/io/FileDescriptor;I)I", (void*) android_net_utils_bindSocketToNetwork },
+ { "protectFromVpn", "(I)Z", (void*) android_net_utils_protectFromVpn },
+ { "protectFromVpn", "(Ljava/io/FileDescriptor;)Z", (void*) android_net_utils_protectFromVpnWithFd },
{ "queryUserAccess", "(II)Z", (void*)android_net_utils_queryUserAccess },
{ "attachDropAllBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDropAllBPFFilter },
{ "detachBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_detachBPFFilter },
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index 241570a..0fdab72 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -36,6 +36,7 @@
#include <utils/List.h>
#include <utils/KeyedVector.h>
#include <binder/Parcel.h>
+#include <binder/ParcelRef.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <utils/threads.h>
@@ -529,8 +530,9 @@
static jlong android_os_Parcel_create(JNIEnv* env, jclass clazz)
{
- Parcel* parcel = new Parcel();
- return reinterpret_cast<jlong>(parcel);
+ sp<ParcelRef> parcelRef = ParcelRef::create();
+ parcelRef->incStrong(reinterpret_cast<const void*>(android_os_Parcel_create));
+ return reinterpret_cast<jlong>(static_cast<Parcel *>(parcelRef.get()));
}
static jlong android_os_Parcel_freeBuffer(JNIEnv* env, jclass clazz, jlong nativePtr)
@@ -545,8 +547,8 @@
static void android_os_Parcel_destroy(JNIEnv* env, jclass clazz, jlong nativePtr)
{
- Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
- delete parcel;
+ ParcelRef* derivative = static_cast<ParcelRef*>(reinterpret_cast<Parcel*>(nativePtr));
+ derivative->decStrong(reinterpret_cast<const void*>(android_os_Parcel_create));
}
static jbyteArray android_os_Parcel_marshall(JNIEnv* env, jclass clazz, jlong nativePtr)
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 581dc08..f71b42c 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -35,6 +35,7 @@
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/Parcel.h>
+#include <binder/ParcelRef.h>
#include <binder/ProcessState.h>
#include <binder/Stability.h>
#include <binderthreadstate/CallerUtils.h>
@@ -1374,7 +1375,8 @@
}
static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
- jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
+ jint code, jobject dataObj, jobject replyObj, jboolean replyObjOwnsNativeParcel,
+ jint flags) // throws RemoteException
{
if (dataObj == NULL) {
jniThrowNullPointerException(env, NULL);
@@ -1416,6 +1418,21 @@
status_t err = target->transact(code, *data, reply, flags);
//if (reply) printf("Transact from Java code to %p received: ", target); reply->print();
+ if (reply) {
+ if (replyObjOwnsNativeParcel) {
+ // as per Parcel java class constructor, here, "reply" MUST be a "ParcelRef"
+ // only for Parcel that contained Binder objects
+ if (reply->objectsCount() > 0) {
+ IPCThreadState::self()->createTransactionReference(static_cast<ParcelRef*>(reply));
+ }
+ } else {
+ // as per Parcel.java, if Parcel java object NOT owning native Parcel object, it will
+ // NOT destroy the native Parcel object upon GC(finalize()), so, there will be no race
+ // condtion in this case. Please refer to the java class methods: Parcel.finalize(),
+ // Parcel.destroy().
+ }
+ }
+
if (kEnableBinderSample) {
if (time_binder_calls) {
conditionally_log_binder_call(start_millis, target, code);
@@ -1542,7 +1559,7 @@
{"pingBinder", "()Z", (void*)android_os_BinderProxy_pingBinder},
{"isBinderAlive", "()Z", (void*)android_os_BinderProxy_isBinderAlive},
{"getInterfaceDescriptor", "()Ljava/lang/String;", (void*)android_os_BinderProxy_getInterfaceDescriptor},
- {"transactNative", "(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z", (void*)android_os_BinderProxy_transact},
+ {"transactNative", "(ILandroid/os/Parcel;Landroid/os/Parcel;ZI)Z", (void*)android_os_BinderProxy_transact},
{"linkToDeath", "(Landroid/os/IBinder$DeathRecipient;I)V", (void*)android_os_BinderProxy_linkToDeath},
{"unlinkToDeath", "(Landroid/os/IBinder$DeathRecipient;I)Z", (void*)android_os_BinderProxy_unlinkToDeath},
{"getNativeFinalizer", "()J", (void*)android_os_BinderProxy_getNativeFinalizer},
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index b2e562c..d7001d8 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -70,7 +70,6 @@
#include <bionic/malloc.h>
#include <bionic/mte.h>
#include <cutils/fs.h>
-#include <cutils/memory.h>
#include <cutils/multiuser.h>
#include <cutils/sockets.h>
#include <private/android_filesystem_config.h>
@@ -638,13 +637,6 @@
// Set the jemalloc decay time to 1.
mallopt(M_DECAY_TIME, 1);
-
- // Avoid potentially expensive memory mitigations, mostly meant for system
- // processes, in apps. These may cause app compat problems, use more memory,
- // or reduce performance. While it would be nice to have them for apps,
- // we will have to wait until they are proven out, have more efficient
- // hardware, and/or apply them only to new applications.
- process_disable_memory_mitigations();
}
static void SetUpSeccompFilter(uid_t uid, bool is_child_zygote) {
@@ -1811,6 +1803,14 @@
break;
}
mallopt(M_BIONIC_SET_HEAP_TAGGING_LEVEL, heap_tagging_level);
+
+ // Avoid heap zero initialization for applications without MTE. Zero init may
+ // cause app compat problems, use more memory, or reduce performance. While it
+ // would be nice to have them for apps, we will have to wait until they are
+ // proven out, have more efficient hardware, and/or apply them only to new
+ // applications.
+ mallopt(M_BIONIC_ZERO_INIT, 0);
+
// Now that we've used the flag, clear it so that we don't pass unknown flags to the ART runtime.
runtime_flags &= ~RuntimeFlags::MEMORY_TAG_LEVEL_MASK;
diff --git a/core/proto/OWNERS b/core/proto/OWNERS
index 748b4b4..99fd215 100644
--- a/core/proto/OWNERS
+++ b/core/proto/OWNERS
@@ -16,6 +16,7 @@
jjaggi@google.com
roosa@google.com
per-file usagestatsservice.proto, usagestatsservice_v2.proto = mwachens@google.com
+per-file apphibernationservice.proto = file:/core/java/android/apphibernation/OWNERS
# Biometrics
kchyn@google.com
diff --git a/core/proto/android/server/apphibernationservice.proto b/core/proto/android/server/apphibernationservice.proto
new file mode 100644
index 0000000..d341c4b
--- /dev/null
+++ b/core/proto/android/server/apphibernationservice.proto
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package com.android.server.apphibernation;
+
+option java_multiple_files = true;
+
+// Proto for hibernation states for all packages for a user.
+message UserLevelHibernationStatesProto {
+ repeated UserLevelHibernationStateProto hibernation_state = 1;
+}
+
+// Proto for com.android.server.apphibernation.UserLevelState.
+message UserLevelHibernationStateProto {
+ optional string package_name = 1;
+ optional bool hibernated = 2;
+}
+
+// Proto for global hibernation states for all packages.
+message GlobalLevelHibernationStatesProto {
+ repeated GlobalLevelHibernationStateProto hibernation_state = 1;
+}
+
+// Proto for com.android.server.apphibernation.GlobalLevelState
+message GlobalLevelHibernationStateProto {
+ optional string package_name = 1;
+ optional bool hibernated = 2;
+}
\ No newline at end of file
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 3183ed3..af6b973 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1669,6 +1669,12 @@
<permission android:name="android.permission.REQUEST_NETWORK_SCORES"
android:protectionLevel="signature|setup" />
+ <!-- Allows applications to restart the Wi-Fi subsystem.
+ @SystemApi
+ <p>Not for use by third-party applications. @hide -->
+ <permission android:name="android.permission.RESTART_WIFI_SUBSYSTEM"
+ android:protectionLevel="signature|privileged" />
+
<!-- @SystemApi @hide Allows applications to toggle airplane mode.
<p>Not for use by third-party or privileged applications.
-->
@@ -2493,6 +2499,10 @@
<permission android:name="android.permission.CREATE_USERS"
android:protectionLevel="signature" />
+ <!-- @TestApi @hide Allows an application to query user info for all users on the device. -->
+ <permission android:name="android.permission.QUERY_USERS"
+ android:protectionLevel="signature" />
+
<!-- @hide Allows an application to set the profile owners and the device owner.
This permission is not available to third party applications.-->
<permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"
@@ -4501,7 +4511,7 @@
<!-- Allows access to keyguard secure storage. Only allowed for system processes.
@hide -->
<permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|setup" />
<!-- Allows applications to set the initial lockscreen state.
<p>Not for use by third-party applications. @hide -->
diff --git a/core/res/OWNERS b/core/res/OWNERS
index a30111b..9d739b9 100644
--- a/core/res/OWNERS
+++ b/core/res/OWNERS
@@ -6,9 +6,12 @@
dupin@google.com
hackbod@android.com
hackbod@google.com
+ilyamaty@google.com
+jaggies@google.com
jsharkey@android.com
jsharkey@google.com
juliacr@google.com
+kchyn@google.com
michaelwr@google.com
nandana@google.com
narayan@google.com
diff --git a/core/res/assets/images/progress_font.png b/core/res/assets/images/progress_font.png
new file mode 100644
index 0000000..78c3ed9
--- /dev/null
+++ b/core/res/assets/images/progress_font.png
Binary files differ
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 4b3d82a..9cc0690 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3856,6 +3856,9 @@
<!-- Component name of an activity that allows the user to modify
the settings for this service. -->
<attr name="settingsActivity"/>
+ <!-- Whether the device must be screen on before routing data to this service.
+ The default is true.-->
+ <attr name="requireDeviceScreenOn" format="boolean"/>
</declare-styleable>
<!-- Use <code>offhost-apdu-service</code> as the root tag of the XML resource that
@@ -3874,6 +3877,12 @@
<attr name="settingsActivity"/>
<!-- Secure Element which the AIDs should be routed to -->
<attr name="secureElementName" format="string"/>
+ <!-- Whether the device must be unlocked before routing data to this service.
+ The default is false.-->
+ <attr name="requireDeviceUnlock"/>
+ <!-- Whether the device must be screen on before routing data to this service.
+ The default is false.-->
+ <attr name="requireDeviceScreenOn"/>
</declare-styleable>
<!-- Specify one or more <code>aid-group</code> elements inside a
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d30efa9..20cb270 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4411,4 +4411,7 @@
<!-- Component names of the services which will keep critical code path warm -->
<string-array name="config_keep_warming_services" translatable="false" />
+
+ <!-- Whether to select voice/data/sms preference without user confirmation -->
+ <bool name="config_voice_data_sms_auto_fallback">false</bool>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index a0be068..0874a77 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3045,6 +3045,7 @@
<public-group type="attr" first-id="0x01010617">
<public name="canPauseRecording" />
<!-- attribute definitions go here -->
+ <public name="requireDeviceScreenOn" />
</public-group>
<public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 937716d..4509b4e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4057,4 +4057,6 @@
<java-symbol type="array" name="config_notificationMsgPkgsAllowedAsConvos" />
<java-symbol type="array" name="config_keep_warming_services" />
+
+ <java-symbol type="bool" name="config_voice_data_sms_auto_fallback" />
</resources>
diff --git a/core/sysprop/WatchdogProperties.sysprop b/core/sysprop/WatchdogProperties.sysprop
index 1bcc773..93e8b78 100644
--- a/core/sysprop/WatchdogProperties.sysprop
+++ b/core/sysprop/WatchdogProperties.sysprop
@@ -16,7 +16,7 @@
owner: Platform
# To escape the watchdog timeout loop, fatal reboot the system when
-# watchdog timed out 'fatal_count' times in 'fatal_window_second'
+# watchdog timed out 'fatal_count' times in 'fatal_window_seconds'
# seconds, if both values are not 0. Default value of both is 0.
prop {
api_name: "fatal_count"
@@ -26,8 +26,9 @@
access: Readonly
}
+# See 'fatal_count' for documentation.
prop {
- api_name: "fatal_window_second"
+ api_name: "fatal_window_seconds"
type: Integer
prop_name: "framework_watchdog.fatal_window.second"
scope: Internal
@@ -35,9 +36,9 @@
}
# The fatal counting can be disabled by setting property
-# 'is_fatal_ignore' to true.
+# 'should_ignore_fatal_count' to true.
prop {
- api_name: "is_fatal_ignore"
+ api_name: "should_ignore_fatal_count"
type: Boolean
prop_name: "persist.debug.framework_watchdog.fatal_ignore"
scope: Internal
diff --git a/core/sysprop/api/com.android.sysprop.watchdog-latest.txt b/core/sysprop/api/com.android.sysprop.watchdog-latest.txt
index d901aef..c846211 100644
--- a/core/sysprop/api/com.android.sysprop.watchdog-latest.txt
+++ b/core/sysprop/api/com.android.sysprop.watchdog-latest.txt
@@ -7,13 +7,13 @@
prop_name: "framework_watchdog.fatal_count"
}
prop {
- api_name: "fatal_window_second"
+ api_name: "fatal_window_seconds"
type: Integer
scope: Internal
prop_name: "framework_watchdog.fatal_window.second"
}
prop {
- api_name: "is_fatal_ignore"
+ api_name: "should_ignore_fatal_count"
scope: Internal
prop_name: "persist.debug.framework_watchdog.fatal_ignore"
}
diff --git a/core/tests/coretests/assets/fonts/OWNERS b/core/tests/coretests/assets/fonts/OWNERS
new file mode 100644
index 0000000..b0e0b9d
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/text/OWNERS
diff --git a/core/tests/coretests/assets/fonts_for_family_selection/OWNERS b/core/tests/coretests/assets/fonts_for_family_selection/OWNERS
new file mode 100644
index 0000000..b0e0b9d
--- /dev/null
+++ b/core/tests/coretests/assets/fonts_for_family_selection/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/text/OWNERS
diff --git a/core/tests/coretests/res/font/OWNERS b/core/tests/coretests/res/font/OWNERS
new file mode 100644
index 0000000..b0e0b9d
--- /dev/null
+++ b/core/tests/coretests/res/font/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/text/OWNERS
diff --git a/core/tests/coretests/src/android/content/OWNERS b/core/tests/coretests/src/android/content/OWNERS
index 912db1e..c61a4b5 100644
--- a/core/tests/coretests/src/android/content/OWNERS
+++ b/core/tests/coretests/src/android/content/OWNERS
@@ -1,3 +1,4 @@
+per-file AssetTest.java = file:/core/java/android/content/res/OWNERS
per-file ContextTest.java = file:/services/core/java/com/android/server/wm/OWNERS
-per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS
per-file *Launcher* = file:/core/java/android/content/pm/LAUNCHER_OWNERS
+per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS
diff --git a/core/tests/coretests/src/android/content/integrity/OWNERS b/core/tests/coretests/src/android/content/integrity/OWNERS
new file mode 100644
index 0000000..4ffc704
--- /dev/null
+++ b/core/tests/coretests/src/android/content/integrity/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/content/integrity/OWNERS
diff --git a/core/tests/coretests/src/android/content/pm/OWNERS b/core/tests/coretests/src/android/content/pm/OWNERS
index 7b76706..8673365 100644
--- a/core/tests/coretests/src/android/content/pm/OWNERS
+++ b/core/tests/coretests/src/android/content/pm/OWNERS
@@ -1,3 +1,5 @@
+include /core/java/android/content/pm/OWNERS
+
per-file AppSearchPersonTest.java = file:/core/java/android/content/pm/SHORTCUT_OWNERS
-per-file SigningDetailsTest.java = mpgroover@google.com
per-file SigningDetailsTest.java = cbrubaker@google.com
+per-file SigningDetailsTest.java = mpgroover@google.com
diff --git a/core/tests/coretests/src/android/content/res/OWNERS b/core/tests/coretests/src/android/content/res/OWNERS
new file mode 100644
index 0000000..3e79d8f
--- /dev/null
+++ b/core/tests/coretests/src/android/content/res/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/content/res/OWNERS
diff --git a/core/tests/coretests/src/android/provider/SimPhonebookContractTest.java b/core/tests/coretests/src/android/provider/SimPhonebookContractTest.java
new file mode 100644
index 0000000..be38260
--- /dev/null
+++ b/core/tests/coretests/src/android/provider/SimPhonebookContractTest.java
@@ -0,0 +1,120 @@
+/*
+ * 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.provider;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.content.ContentValues;
+import android.os.Parcel;
+import android.provider.SimPhonebookContract.SimRecords.NameValidationResult;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class SimPhonebookContractTest {
+
+ @Test
+ public void getContentUri_invalidEfType_throwsIllegalArgumentException() {
+ assertThrows(IllegalArgumentException.class, () ->
+ SimPhonebookContract.SimRecords.getContentUri(1, 100)
+ );
+ assertThrows(IllegalArgumentException.class, () ->
+ SimPhonebookContract.SimRecords.getContentUri(1, -1)
+ );
+ assertThrows(IllegalArgumentException.class, () ->
+ SimPhonebookContract.SimRecords.getContentUri(1,
+ SimPhonebookContract.ElementaryFiles.EF_UNKNOWN)
+ );
+ }
+
+ @Test
+ public void getItemUri_invalidEfType_throwsIllegalArgumentException() {
+ assertThrows(IllegalArgumentException.class, () ->
+ SimPhonebookContract.SimRecords.getItemUri(1, 100, 1)
+ );
+ assertThrows(IllegalArgumentException.class, () ->
+ SimPhonebookContract.SimRecords.getItemUri(1, -1, 1)
+ );
+ assertThrows(IllegalArgumentException.class, () ->
+ SimPhonebookContract.SimRecords.getItemUri(1,
+ SimPhonebookContract.ElementaryFiles.EF_UNKNOWN, 1)
+ );
+ }
+
+ @Test
+ public void getItemUri_invalidRecordIndex_throwsIllegalArgumentException() {
+ assertThrows(IllegalArgumentException.class, () ->
+ SimPhonebookContract.SimRecords.getItemUri(1,
+ SimPhonebookContract.ElementaryFiles.EF_ADN, 0)
+ );
+ assertThrows(IllegalArgumentException.class, () ->
+ SimPhonebookContract.SimRecords.getItemUri(1,
+ SimPhonebookContract.ElementaryFiles.EF_ADN, -1)
+ );
+ }
+
+ @Test
+ public void nameValidationResult_isValid_validNames() {
+ assertThat(new NameValidationResult("", "", 0, 1).isValid()).isTrue();
+ assertThat(new NameValidationResult("a", "a", 1, 1).isValid()).isTrue();
+ assertThat(new NameValidationResult("First Last", "First Last", 10, 10).isValid()).isTrue();
+ assertThat(
+ new NameValidationResult("First Last", "First Last", 10, 100).isValid()).isTrue();
+ }
+
+ @Test
+ public void nameValidationResult_isValid_invalidNames() {
+ assertThat(new NameValidationResult("", "", 0, 0).isValid()).isFalse();
+ assertThat(new NameValidationResult("ab", "ab", 2, 1).isValid()).isFalse();
+ NameValidationResult unsupportedCharactersResult = new NameValidationResult("A_b_c",
+ "A b c", 5, 5);
+ assertThat(unsupportedCharactersResult.isValid()).isFalse();
+ assertThat(unsupportedCharactersResult.isSupportedCharacter(0)).isTrue();
+ assertThat(unsupportedCharactersResult.isSupportedCharacter(1)).isFalse();
+ assertThat(unsupportedCharactersResult.isSupportedCharacter(2)).isTrue();
+ assertThat(unsupportedCharactersResult.isSupportedCharacter(3)).isFalse();
+ assertThat(unsupportedCharactersResult.isSupportedCharacter(4)).isTrue();
+ }
+
+ @Test
+ public void nameValidationResult_parcel() {
+ ContentValues values = new ContentValues();
+ values.put("name", "Name");
+ values.put("phone_number", "123");
+
+ NameValidationResult result;
+ Parcel parcel = Parcel.obtain();
+ try {
+ parcel.writeParcelable(new NameValidationResult("name", "sanitized name", 1, 2), 0);
+ parcel.setDataPosition(0);
+ result = parcel.readParcelable(NameValidationResult.class.getClassLoader());
+ } finally {
+ parcel.recycle();
+ }
+
+ assertThat(result.getName()).isEqualTo("name");
+ assertThat(result.getSanitizedName()).isEqualTo("sanitized name");
+ assertThat(result.getEncodedLength()).isEqualTo(1);
+ assertThat(result.getMaxEncodedLength()).isEqualTo(2);
+ }
+}
+
diff --git a/core/tests/mockingcoretests/src/android/view/OWNERS b/core/tests/mockingcoretests/src/android/view/OWNERS
new file mode 100644
index 0000000..9c9f824
--- /dev/null
+++ b/core/tests/mockingcoretests/src/android/view/OWNERS
@@ -0,0 +1,2 @@
+# Display
+per-file Display*.java = file:/services/core/java/com/android/server/display/OWNERS
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 92f2c00..5b32641 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -344,7 +344,7 @@
<permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<permission name="android.permission.MOVE_PACKAGE"/>
<!-- Needed for test only -->
- <permission name="android.permission.NETWORK_AIRPLANE_MODE"/>
+ <permission name="android.permission.RESTART_WIFI_SUBSYSTEM"/>
<permission name="android.permission.OBSERVE_APP_USAGE"/>
<permission name="android.permission.NETWORK_SCAN"/>
<permission name="android.permission.PACKAGE_USAGE_STATS" />
@@ -443,6 +443,10 @@
<!-- Permission needed for CTS test - WifiManagerTest -->
<permission name="android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS" />
<permission name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS" />
+ <!-- Permission required for GTS test - GtsAssistIntentTestCases -->
+ <permission name="android.permission.MANAGE_SOUND_TRIGGER" />
+ <permission name="android.permission.CAPTURE_AUDIO_HOTWORD" />
+ <permission name="android.permission.MODIFY_QUIET_MODE" />
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/keystore/java/android/security/Authorization.java b/keystore/java/android/security/Authorization.java
index fcc518c..21d23b1 100644
--- a/keystore/java/android/security/Authorization.java
+++ b/keystore/java/android/security/Authorization.java
@@ -82,7 +82,7 @@
*
* @param locked - whether it is a lock (true) or unlock (false) event
* @param syntheticPassword - if it is an unlock event with the password, pass the synthetic
- * password provided by the LockSettingService
+ * password provided by the LockSettingService
*
* @return 0 if successful or a {@code ResponseCode}.
*/
diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java
index 7abcfdc..9e1fb54 100644
--- a/keystore/java/android/security/Credentials.java
+++ b/keystore/java/android/security/Credentials.java
@@ -19,9 +19,9 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
-import com.android.org.bouncycastle.util.io.pem.PemObject;
-import com.android.org.bouncycastle.util.io.pem.PemReader;
-import com.android.org.bouncycastle.util.io.pem.PemWriter;
+import com.android.internal.org.bouncycastle.util.io.pem.PemObject;
+import com.android.internal.org.bouncycastle.util.io.pem.PemReader;
+import com.android.internal.org.bouncycastle.util.io.pem.PemWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index 97da3cc..1ae6a63 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -46,6 +46,7 @@
boolean installKeyPair(
in byte[] privateKey, in byte[] userCert, in byte[] certChain, String alias, int uid);
boolean removeKeyPair(String alias);
+ boolean containsKeyPair(String alias);
// APIs used by Settings
boolean deleteCaCertificate(String alias);
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 4a67135..e19d88c 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -45,8 +45,8 @@
import android.security.keystore.UserNotAuthenticatedException;
import android.util.Log;
-import com.android.org.bouncycastle.asn1.ASN1InputStream;
-import com.android.org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import com.android.internal.org.bouncycastle.asn1.ASN1InputStream;
+import com.android.internal.org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import java.io.ByteArrayInputStream;
import java.io.IOException;
diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java
index 92d87aa..f7477bf 100644
--- a/keystore/java/android/security/KeyStore2.java
+++ b/keystore/java/android/security/KeyStore2.java
@@ -23,6 +23,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
+import android.security.keymaster.KeymasterDefs;
import android.system.keystore2.IKeystoreService;
import android.system.keystore2.KeyDescriptor;
import android.system.keystore2.KeyEntryResponse;
@@ -107,7 +108,7 @@
return request.execute(service);
} catch (ServiceSpecificException e) {
Log.e(TAG, "KeyStore exception", e);
- throw new KeyStoreException(e.errorCode, "");
+ throw getKeyStoreException(e.errorCode);
} catch (RemoteException e) {
if (firstTry) {
Log.w(TAG, "Looks like we may have lost connection to the Keystore "
@@ -274,4 +275,40 @@
}
}
+ static KeyStoreException getKeyStoreException(int errorCode) {
+ if (errorCode > 0) {
+ // KeyStore layer error
+ switch (errorCode) {
+ case ResponseCode.LOCKED:
+ return new KeyStoreException(errorCode, "User authentication required");
+ case ResponseCode.UNINITIALIZED:
+ return new KeyStoreException(errorCode, "Keystore not initialized");
+ case ResponseCode.SYSTEM_ERROR:
+ return new KeyStoreException(errorCode, "System error");
+ case ResponseCode.PERMISSION_DENIED:
+ return new KeyStoreException(errorCode, "Permission denied");
+ case ResponseCode.KEY_NOT_FOUND:
+ return new KeyStoreException(errorCode, "Key not found");
+ case ResponseCode.VALUE_CORRUPTED:
+ return new KeyStoreException(errorCode, "Key blob corrupted");
+ case ResponseCode.KEY_PERMANENTLY_INVALIDATED:
+ return new KeyStoreException(errorCode, "Key permanently invalidated");
+ default:
+ return new KeyStoreException(errorCode, String.valueOf(errorCode));
+ }
+ } else {
+ // Keymaster layer error
+ switch (errorCode) {
+ case KeymasterDefs.KM_ERROR_INVALID_AUTHORIZATION_TIMEOUT:
+ // The name of this parameter significantly differs between Keymaster and
+ // framework APIs. Use the framework wording to make life easier for developers.
+ return new KeyStoreException(errorCode,
+ "Invalid user authentication validity duration");
+ default:
+ return new KeyStoreException(errorCode,
+ KeymasterDefs.getErrorMessage(errorCode));
+ }
+ }
+ }
+
}
diff --git a/keystore/java/android/security/KeyStoreOperation.java b/keystore/java/android/security/KeyStoreOperation.java
index 7ea9e14..a6552dd 100644
--- a/keystore/java/android/security/KeyStoreOperation.java
+++ b/keystore/java/android/security/KeyStoreOperation.java
@@ -73,8 +73,7 @@
);
}
default:
- // TODO Human readable string. Use something like KeyStore.getKeyStoreException
- throw new KeyStoreException(e.errorCode, "");
+ throw KeyStore2.getKeyStoreException(e.errorCode);
}
} catch (RemoteException e) {
// Log exception and report invalid operation handle.
diff --git a/keystore/java/android/security/KeyStoreSecurityLevel.java b/keystore/java/android/security/KeyStoreSecurityLevel.java
index 3ef4aa5..372add9 100644
--- a/keystore/java/android/security/KeyStoreSecurityLevel.java
+++ b/keystore/java/android/security/KeyStoreSecurityLevel.java
@@ -52,7 +52,7 @@
try {
return request.execute();
} catch (ServiceSpecificException e) {
- throw new KeyStoreException(e.errorCode, "");
+ throw KeyStore2.getKeyStoreException(e.errorCode);
} catch (RemoteException e) {
// Log exception and report invalid operation handle.
// This should prompt the caller drop the reference to this operation and retry.
@@ -96,25 +96,26 @@
} catch (ServiceSpecificException e) {
switch (e.errorCode) {
case ResponseCode.BACKEND_BUSY: {
+ long backOffHint = (long) (Math.random() * 80 + 20);
if (CompatChanges.isChangeEnabled(
KeyStore2.KEYSTORE_OPERATION_CREATION_MAY_FAIL)) {
// Starting with Android S we inform the caller about the
// backend being busy.
- throw new BackendBusyException();
+ throw new BackendBusyException(backOffHint);
} else {
// Before Android S operation creation must always succeed. So we
// just have to retry. We do so with a randomized back-off between
- // 50 and 250ms.
+ // 20 and 100ms.
// It is a little awkward that we cannot break out of this loop
// by interrupting this thread. But that is the expected behavior.
// There is some comfort in the fact that interrupting a thread
// also does not unblock a thread waiting for a binder transaction.
- interruptedPreservingSleep((long) (Math.random() * 200 + 50));
+ interruptedPreservingSleep(backOffHint);
}
break;
}
default:
- throw new KeyStoreException(e.errorCode, "");
+ throw KeyStore2.getKeyStoreException(e.errorCode);
}
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
index 6ad8d2c..334b111 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -26,24 +26,24 @@
import android.security.keymaster.KeymasterCertificateChain;
import android.security.keymaster.KeymasterDefs;
-import com.android.org.bouncycastle.asn1.ASN1EncodableVector;
-import com.android.org.bouncycastle.asn1.ASN1InputStream;
-import com.android.org.bouncycastle.asn1.ASN1Integer;
-import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import com.android.org.bouncycastle.asn1.DERBitString;
-import com.android.org.bouncycastle.asn1.DERNull;
-import com.android.org.bouncycastle.asn1.DERSequence;
-import com.android.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
-import com.android.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import com.android.org.bouncycastle.asn1.x509.Certificate;
-import com.android.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-import com.android.org.bouncycastle.asn1.x509.TBSCertificate;
-import com.android.org.bouncycastle.asn1.x509.Time;
-import com.android.org.bouncycastle.asn1.x509.V3TBSCertificateGenerator;
-import com.android.org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
-import com.android.org.bouncycastle.jce.X509Principal;
-import com.android.org.bouncycastle.jce.provider.X509CertificateObject;
-import com.android.org.bouncycastle.x509.X509V3CertificateGenerator;
+import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
+import com.android.internal.org.bouncycastle.asn1.ASN1InputStream;
+import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
+import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import com.android.internal.org.bouncycastle.asn1.DERBitString;
+import com.android.internal.org.bouncycastle.asn1.DERNull;
+import com.android.internal.org.bouncycastle.asn1.DERSequence;
+import com.android.internal.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import com.android.internal.org.bouncycastle.asn1.x509.Certificate;
+import com.android.internal.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import com.android.internal.org.bouncycastle.asn1.x509.TBSCertificate;
+import com.android.internal.org.bouncycastle.asn1.x509.Time;
+import com.android.internal.org.bouncycastle.asn1.x509.V3TBSCertificateGenerator;
+import com.android.internal.org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+import com.android.internal.org.bouncycastle.jce.X509Principal;
+import com.android.internal.org.bouncycastle.jce.provider.X509CertificateObject;
+import com.android.internal.org.bouncycastle.x509.X509V3CertificateGenerator;
import libcore.util.EmptyArray;
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
index b1b6306..16bf546 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
@@ -23,7 +23,6 @@
import android.security.keymaster.ExportResult;
import android.security.keymaster.KeyCharacteristics;
import android.security.keymaster.KeymasterDefs;
-import android.sysprop.Keystore2Properties;
import java.io.IOException;
import java.security.KeyFactory;
@@ -117,8 +116,6 @@
putSecretKeyFactoryImpl("HmacSHA512");
}
- private static boolean sKeystore2Enabled;
-
/**
* This function indicates whether or not Keystore 2.0 is enabled. Some parts of the
* Keystore SPI must behave subtly differently when Keystore 2.0 is enabled. However,
@@ -133,10 +130,9 @@
* @hide
*/
public static boolean isKeystore2Enabled() {
- return sKeystore2Enabled;
+ return android.security.keystore2.AndroidKeyStoreProvider.isInstalled();
}
-
/**
* Installs a new instance of this provider (and the
* {@link AndroidKeyStoreBCWorkaroundProvider}).
@@ -164,11 +160,6 @@
// priority.
Security.addProvider(workaroundProvider);
}
-
- // {@code install()} is run by zygote when this property is still accessible. We store its
- // value so that the Keystore SPI can act accordingly without having to access an internal
- // property.
- sKeystore2Enabled = Keystore2Properties.keystore2_enabled().orElse(false);
}
private void putSecretKeyFactoryImpl(String algorithm) {
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
index 3694d63..d2678c7 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -215,7 +215,8 @@
// Keystore 1.0 does not tell us the exact security level of the key
// so we report an unknown but secure security level.
insideSecureHardware ? KeyProperties.SECURITY_LEVEL_UNKNOWN_SECURE
- : KeyProperties.SECURITY_LEVEL_SOFTWARE);
+ : KeyProperties.SECURITY_LEVEL_SOFTWARE,
+ KeyProperties.UNRESTRICTED_USAGE_COUNT);
}
private static BigInteger getGateKeeperSecureUserId() throws ProviderException {
diff --git a/keystore/java/android/security/keystore/BackendBusyException.java b/keystore/java/android/security/keystore/BackendBusyException.java
index 1a88469..a813e93 100644
--- a/keystore/java/android/security/keystore/BackendBusyException.java
+++ b/keystore/java/android/security/keystore/BackendBusyException.java
@@ -16,37 +16,66 @@
package android.security.keystore;
+import android.annotation.DurationMillisLong;
import android.annotation.NonNull;
import java.security.ProviderException;
/**
* Indicates a transient error that prevented a key operation from being created.
- * Callers should try again with a back-off period of 10-30 milliseconds.
+ * Callers should try again with a back-off period of {@link #getBackOffHintMillis()}
+ * milliseconds.
*/
public class BackendBusyException extends ProviderException {
+ private final long mBackOffHintMillis;
+
/**
* Constructs a new {@code BackendBusyException} without detail message and cause.
+ *
*/
- public BackendBusyException() {
+ public BackendBusyException(@DurationMillisLong long backOffHintMillis) {
super("The keystore backend has no operation slots available. Retry later.");
+ if (backOffHintMillis < 0) {
+ throw new IllegalArgumentException("Back-off hint cannot be negative.");
+ }
+ mBackOffHintMillis = backOffHintMillis;
}
/**
* Constructs a new {@code BackendBusyException} with the provided detail message and
* no cause.
*/
- public BackendBusyException(@NonNull String message) {
+ public BackendBusyException(@DurationMillisLong long backOffHintMillis,
+ @NonNull String message) {
super(message);
+ if (backOffHintMillis < 0) {
+ throw new IllegalArgumentException("Back-off hint cannot be negative.");
+ }
+ mBackOffHintMillis = backOffHintMillis;
}
/**
* Constructs a new {@code BackendBusyException} with the provided detail message and
* cause.
*/
- public BackendBusyException(@NonNull String message, @NonNull Throwable cause) {
+ public BackendBusyException(@DurationMillisLong long backOffHintMillis,
+ @NonNull String message, @NonNull Throwable cause) {
super(message, cause);
+ if (backOffHintMillis < 0) {
+ throw new IllegalArgumentException("Back-off hint cannot be negative.");
+ }
+ mBackOffHintMillis = backOffHintMillis;
}
+ /**
+ * When retrying to start a Keystore operation after receiving this exception, this can be
+ * used to determine how long to wait before retrying. It is not guaranteed that the operation
+ * will succeeds after this time. Multiple retries may be necessary if the system is congested.
+ *
+ * @return Number of milliseconds to back off before retrying.
+ */
+ public @DurationMillisLong long getBackOffHintMillis() {
+ return mBackOffHintMillis;
+ }
}
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index e9aac7d..c2a7b2e 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -274,6 +274,7 @@
private final boolean mUserConfirmationRequired;
private final boolean mUnlockedDeviceRequired;
private final boolean mCriticalToDeviceEncryption;
+ private final int mMaxUsageCount;
/*
* ***NOTE***: All new fields MUST also be added to the following:
* ParcelableKeyGenParameterSpec class.
@@ -313,7 +314,8 @@
boolean isStrongBoxBacked,
boolean userConfirmationRequired,
boolean unlockedDeviceRequired,
- boolean criticalToDeviceEncryption) {
+ boolean criticalToDeviceEncryption,
+ int maxUsageCount) {
if (TextUtils.isEmpty(keyStoreAlias)) {
throw new IllegalArgumentException("keyStoreAlias must not be empty");
}
@@ -366,6 +368,7 @@
mUserConfirmationRequired = userConfirmationRequired;
mUnlockedDeviceRequired = unlockedDeviceRequired;
mCriticalToDeviceEncryption = criticalToDeviceEncryption;
+ mMaxUsageCount = maxUsageCount;
}
/**
@@ -782,7 +785,7 @@
}
/**
- * Return whether this key is critical to the device encryption flow.
+ * Returns whether this key is critical to the device encryption flow.
*
* @see android.security.KeyStore#FLAG_CRITICAL_TO_DEVICE_ENCRYPTION
* @hide
@@ -792,6 +795,17 @@
}
/**
+ * Returns the maximum number of times the limited use key is allowed to be used or
+ * {@link KeyProperties#UNRESTRICTED_USAGE_COUNT} if there’s no restriction on the number of
+ * times the key can be used.
+ *
+ * @see Builder#setMaxUsageCount(int)
+ */
+ public int getMaxUsageCount() {
+ return mMaxUsageCount;
+ }
+
+ /**
* Builder of {@link KeyGenParameterSpec} instances.
*/
public final static class Builder {
@@ -827,6 +841,7 @@
private boolean mUserConfirmationRequired;
private boolean mUnlockedDeviceRequired = false;
private boolean mCriticalToDeviceEncryption = false;
+ private int mMaxUsageCount = KeyProperties.UNRESTRICTED_USAGE_COUNT;
/**
* Creates a new instance of the {@code Builder}.
@@ -894,6 +909,7 @@
mUserConfirmationRequired = sourceSpec.isUserConfirmationRequired();
mUnlockedDeviceRequired = sourceSpec.isUnlockedDeviceRequired();
mCriticalToDeviceEncryption = sourceSpec.isCriticalToDeviceEncryption();
+ mMaxUsageCount = sourceSpec.getMaxUsageCount();
}
/**
@@ -1553,6 +1569,47 @@
}
/**
+ * Sets the maximum number of times the key is allowed to be used. After every use of the
+ * key, the use counter will decrease. This authorization applies only to secret key and
+ * private key operations. Public key operations are not restricted. For example, after
+ * successfully encrypting and decrypting data using methods such as
+ * {@link Cipher#doFinal()}, the use counter of the secret key will decrease. After
+ * successfully signing data using methods such as {@link Signature#sign()}, the use
+ * counter of the private key will decrease.
+ *
+ * When the use counter is depleted, the key will be marked for deletion by Android
+ * Keystore and any subsequent attempt to use the key will throw
+ * {@link KeyPermanentlyInvalidatedException}. There is no key to be loaded from the
+ * Android Keystore once the exhausted key is permanently deleted, as if the key never
+ * existed before.
+ *
+ * <p>By default, there is no restriction on the usage of key.
+ *
+ * <p>Some secure hardware may not support this feature at all, in which case it will
+ * be enforced in software, some secure hardware may support it but only with
+ * maxUsageCount = 1, and some secure hardware may support it with larger value
+ * of maxUsageCount.
+ *
+ * <p>The PackageManger feature flags:
+ * {@link android.content.pm.PackageManager#FEATURE_KEYSTORE_SINGLE_USE_KEY} and
+ * {@link android.content.pm.PackageManager#FEATURE_KEYSTORE_LIMITED_USE_KEY} can be used
+ * to check whether the secure hardware cannot enforce this feature, can only enforce it
+ * with maxUsageCount = 1, or can enforce it with larger value of maxUsageCount.
+ *
+ * @param maxUsageCount maximum number of times the key is allowed to be used or
+ * {@link KeyProperties#UNRESTRICTED_USAGE_COUNT} if there is no restriction on the
+ * usage.
+ */
+ @NonNull
+ public Builder setMaxUsageCount(int maxUsageCount) {
+ if (maxUsageCount == KeyProperties.UNRESTRICTED_USAGE_COUNT || maxUsageCount > 0) {
+ mMaxUsageCount = maxUsageCount;
+ return this;
+ }
+ throw new IllegalArgumentException("maxUsageCount is not valid");
+ }
+
+ /**
* Builds an instance of {@code KeyGenParameterSpec}.
*/
@NonNull
@@ -1587,7 +1644,8 @@
mIsStrongBoxBacked,
mUserConfirmationRequired,
mUnlockedDeviceRequired,
- mCriticalToDeviceEncryption);
+ mCriticalToDeviceEncryption,
+ mMaxUsageCount);
}
}
}
diff --git a/keystore/java/android/security/keystore/KeyInfo.java b/keystore/java/android/security/keystore/KeyInfo.java
index 7158d0c..f50efd2 100644
--- a/keystore/java/android/security/keystore/KeyInfo.java
+++ b/keystore/java/android/security/keystore/KeyInfo.java
@@ -85,6 +85,7 @@
private final boolean mInvalidatedByBiometricEnrollment;
private final boolean mUserConfirmationRequired;
private final @KeyProperties.SecurityLevelEnum int mSecurityLevel;
+ private final int mRemainingUsageCount;
/**
* @hide
@@ -109,7 +110,8 @@
boolean trustedUserPresenceRequired,
boolean invalidatedByBiometricEnrollment,
boolean userConfirmationRequired,
- @KeyProperties.SecurityLevelEnum int securityLevel) {
+ @KeyProperties.SecurityLevelEnum int securityLevel,
+ int remainingUsageCount) {
mKeystoreAlias = keystoreKeyAlias;
mInsideSecureHardware = insideSecureHardware;
mOrigin = origin;
@@ -134,6 +136,7 @@
mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
mUserConfirmationRequired = userConfirmationRequired;
mSecurityLevel = securityLevel;
+ mRemainingUsageCount = remainingUsageCount;
}
/**
@@ -374,4 +377,15 @@
public @KeyProperties.SecurityLevelEnum int getSecurityLevel() {
return mSecurityLevel;
}
+
+ /**
+ * Returns the remaining number of times the key is allowed to be used or
+ * {@link KeyProperties#UNRESTRICTED_USAGE_COUNT} if there's no restriction on the number of
+ * times the key can be used. Note that this gives a best effort count and need not be
+ * accurate (as there might be usages happening in parallel and the count maintained here need
+ * not be in sync with the usage).
+ */
+ public int getRemainingUsageCount() {
+ return mRemainingUsageCount;
+ }
}
diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index 5928540..3ebca6a 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -20,6 +20,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringDef;
+import android.annotation.SystemApi;
+import android.os.Process;
import android.security.KeyStore;
import android.security.keymaster.KeymasterDefs;
@@ -67,6 +69,7 @@
PURPOSE_SIGN,
PURPOSE_VERIFY,
PURPOSE_WRAP_KEY,
+ PURPOSE_AGREE_KEY,
})
public @interface PurposeEnum {}
@@ -96,6 +99,11 @@
public static final int PURPOSE_WRAP_KEY = 1 << 5;
/**
+ * Purpose of key: creating a shared ECDH secret through key agreement.
+ */
+ public static final int PURPOSE_AGREE_KEY = 1 << 6;
+
+ /**
* @hide
*/
public static abstract class Purpose {
@@ -113,6 +121,8 @@
return KeymasterDefs.KM_PURPOSE_VERIFY;
case PURPOSE_WRAP_KEY:
return KeymasterDefs.KM_PURPOSE_WRAP;
+ case PURPOSE_AGREE_KEY:
+ return KeymasterDefs.KM_PURPOSE_AGREE_KEY;
default:
throw new IllegalArgumentException("Unknown purpose: " + purpose);
}
@@ -130,6 +140,8 @@
return PURPOSE_VERIFY;
case KeymasterDefs.KM_PURPOSE_WRAP:
return PURPOSE_WRAP_KEY;
+ case KeymasterDefs.KM_PURPOSE_AGREE_KEY:
+ return PURPOSE_AGREE_KEY;
default:
throw new IllegalArgumentException("Unknown purpose: " + purpose);
}
@@ -864,9 +876,18 @@
* which it must be configured in SEPolicy.
* @hide
*/
+ @SystemApi
public static final int NAMESPACE_APPLICATION = -1;
/**
+ * The namespace identifier for the WIFI Keystore namespace.
+ * This must be kept in sync with system/sepolicy/private/keystore2_key_contexts
+ * @hide
+ */
+ @SystemApi
+ public static final int NAMESPACE_WIFI = 102;
+
+ /**
* For legacy support, translate namespaces into known UIDs.
* @hide
*/
@@ -874,6 +895,8 @@
switch (namespace) {
case NAMESPACE_APPLICATION:
return KeyStore.UID_SELF;
+ case NAMESPACE_WIFI:
+ return Process.WIFI_UID;
// TODO Translate WIFI and VPN UIDs once the namespaces are defined.
// b/171305388 and b/171305607
default:
@@ -890,6 +913,8 @@
switch (uid) {
case KeyStore.UID_SELF:
return NAMESPACE_APPLICATION;
+ case Process.WIFI_UID:
+ return NAMESPACE_WIFI;
// TODO Translate WIFI and VPN UIDs once the namespaces are defined.
// b/171305388 and b/171305607
default:
@@ -897,4 +922,9 @@
+ uid);
}
}
+
+ /**
+ * This value indicates that there is no restriction on the number of times the key can be used.
+ */
+ public static final int UNRESTRICTED_USAGE_COUNT = -1;
}
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index 2e793de..76ce23e 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -235,6 +235,7 @@
private final boolean mUserConfirmationRequired;
private final boolean mUnlockedDeviceRequired;
private final boolean mIsStrongBoxBacked;
+ private final int mMaxUsageCount;
private KeyProtection(
Date keyValidityStart,
@@ -256,7 +257,8 @@
boolean criticalToDeviceEncryption,
boolean userConfirmationRequired,
boolean unlockedDeviceRequired,
- boolean isStrongBoxBacked) {
+ boolean isStrongBoxBacked,
+ int maxUsageCount) {
mKeyValidityStart = Utils.cloneIfNotNull(keyValidityStart);
mKeyValidityForOriginationEnd = Utils.cloneIfNotNull(keyValidityForOriginationEnd);
mKeyValidityForConsumptionEnd = Utils.cloneIfNotNull(keyValidityForConsumptionEnd);
@@ -279,6 +281,7 @@
mUserConfirmationRequired = userConfirmationRequired;
mUnlockedDeviceRequired = unlockedDeviceRequired;
mIsStrongBoxBacked = isStrongBoxBacked;
+ mMaxUsageCount = maxUsageCount;
}
/**
@@ -548,6 +551,17 @@
}
/**
+ * Returns the maximum number of times the limited use key is allowed to be used or
+ * {@link KeyProperties#UNRESTRICTED_USAGE_COUNT} if there’s no restriction on the number of
+ * times the key can be used.
+ *
+ * @see Builder#setMaxUsageCount(int)
+ */
+ public int getMaxUsageCount() {
+ return mMaxUsageCount;
+ }
+
+ /**
* Builder of {@link KeyProtection} instances.
*/
public final static class Builder {
@@ -574,6 +588,7 @@
private long mBoundToSecureUserId = GateKeeper.INVALID_SECURE_USER_ID;
private boolean mCriticalToDeviceEncryption = false;
private boolean mIsStrongBoxBacked = false;
+ private int mMaxUsageCount = KeyProperties.UNRESTRICTED_USAGE_COUNT;
/**
* Creates a new instance of the {@code Builder}.
@@ -1040,6 +1055,47 @@
}
/**
+ * Sets the maximum number of times the key is allowed to be used. After every use of the
+ * key, the use counter will decrease. This authorization applies only to secret key and
+ * private key operations. Public key operations are not restricted. For example, after
+ * successfully encrypting and decrypting data using methods such as
+ * {@link Cipher#doFinal()}, the use counter of the secret key will decrease. After
+ * successfully signing data using methods such as {@link Signature#sign()}, the use
+ * counter of the private key will decrease.
+ *
+ * When the use counter is depleted, the key will be marked for deletion by Android
+ * Keystore and any subsequent attempt to use the key will throw
+ * {@link KeyPermanentlyInvalidatedException}. There is no key to be loaded from the
+ * Android Keystore once the exhausted key is permanently deleted, as if the key never
+ * existed before.
+ *
+ * <p>By default, there is no restriction on the usage of key.
+ *
+ * <p>Some secure hardware may not support this feature at all, in which case it will
+ * be enforced in software, some secure hardware may support it but only with
+ * maxUsageCount = 1, and some secure hardware may support it with larger value
+ * of maxUsageCount.
+ *
+ * <p>The PackageManger feature flags:
+ * {@link android.content.pm.PackageManager#FEATURE_KEYSTORE_SINGLE_USE_KEY} and
+ * {@link android.content.pm.PackageManager#FEATURE_KEYSTORE_LIMITED_USE_KEY} can be used
+ * to check whether the secure hardware cannot enforce this feature, can only enforce it
+ * with maxUsageCount = 1, or can enforce it with larger value of maxUsageCount.
+ *
+ * @param maxUsageCount maximum number of times the key is allowed to be used or
+ * {@link KeyProperties#UNRESTRICTED_USAGE_COUNT} if there is no restriction on the
+ * usage.
+ */
+ @NonNull
+ public Builder setMaxUsageCount(int maxUsageCount) {
+ if (maxUsageCount == KeyProperties.UNRESTRICTED_USAGE_COUNT || maxUsageCount > 0) {
+ mMaxUsageCount = maxUsageCount;
+ return this;
+ }
+ throw new IllegalArgumentException("maxUsageCount is not valid");
+ }
+
+ /**
* Builds an instance of {@link KeyProtection}.
*
* @throws IllegalArgumentException if a required field is missing
@@ -1066,7 +1122,8 @@
mCriticalToDeviceEncryption,
mUserConfirmationRequired,
mUnlockedDeviceRequired,
- mIsStrongBoxBacked);
+ mIsStrongBoxBacked,
+ mMaxUsageCount);
}
}
}
diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
index 69c15cc..8163472 100644
--- a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
@@ -108,6 +108,7 @@
out.writeBoolean(mSpec.isUserConfirmationRequired());
out.writeBoolean(mSpec.isUnlockedDeviceRequired());
out.writeBoolean(mSpec.isCriticalToDeviceEncryption());
+ out.writeInt(mSpec.getMaxUsageCount());
}
private static Date readDateOrNull(Parcel in) {
@@ -166,6 +167,7 @@
final boolean userConfirmationRequired = in.readBoolean();
final boolean unlockedDeviceRequired = in.readBoolean();
final boolean criticalToDeviceEncryption = in.readBoolean();
+ final int maxUsageCount = in.readInt();
// The KeyGenParameterSpec is intentionally not constructed using a Builder here:
// The intention is for this class to break if new parameters are added to the
// KeyGenParameterSpec constructor (whereas using a builder would silently drop them).
@@ -199,7 +201,8 @@
isStrongBoxBacked,
userConfirmationRequired,
unlockedDeviceRequired,
- criticalToDeviceEncryption);
+ criticalToDeviceEncryption,
+ maxUsageCount);
}
public static final @android.annotation.NonNull Creator<ParcelableKeyGenParameterSpec> CREATOR = new Creator<ParcelableKeyGenParameterSpec>() {
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java
new file mode 100644
index 0000000..fc963a8
--- /dev/null
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore2;
+
+import android.hardware.security.keymint.Algorithm;
+import android.hardware.security.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyPurpose;
+import android.hardware.security.keymint.Tag;
+import android.security.KeyStoreException;
+import android.security.KeyStoreOperation;
+import android.security.keystore.KeyStoreCryptoOperation;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.ProviderException;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.crypto.KeyAgreementSpi;
+import javax.crypto.SecretKey;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * {@link KeyAgreementSpi} which provides an ECDH implementation backed by Android KeyStore.
+ *
+ * @hide
+ */
+public class AndroidKeyStoreKeyAgreementSpi extends KeyAgreementSpi
+ implements KeyStoreCryptoOperation {
+
+ private static final String TAG = "AndroidKeyStoreKeyAgreementSpi";
+
+ /**
+ * ECDH implementation.
+ *
+ * @hide
+ */
+ public static class ECDH extends AndroidKeyStoreKeyAgreementSpi {
+ public ECDH() {
+ super(Algorithm.EC);
+ }
+ }
+
+ private final int mKeymintAlgorithm;
+
+ // Fields below are populated by engineInit and should be preserved after engineDoFinal.
+ private AndroidKeyStorePrivateKey mKey;
+ private PublicKey mOtherPartyKey;
+
+ // Fields below are reset when engineDoFinal succeeds.
+ private KeyStoreOperation mOperation;
+ private long mOperationHandle;
+
+ protected AndroidKeyStoreKeyAgreementSpi(int keymintAlgorithm) {
+ resetAll();
+
+ mKeymintAlgorithm = keymintAlgorithm;
+ }
+
+ @Override
+ protected void engineInit(Key key, SecureRandom random) throws InvalidKeyException {
+ resetAll();
+
+ if (key == null) {
+ throw new InvalidKeyException("key == null");
+ } else if (!(key instanceof AndroidKeyStorePrivateKey)) {
+ throw new InvalidKeyException(
+ "Only Android KeyStore private keys supported. Key: " + key);
+ }
+ // Checking the correct KEY_PURPOSE and algorithm is done by the Keymint implementation in
+ // ensureKeystoreOperationInitialized() below.
+ mKey = (AndroidKeyStorePrivateKey) key;
+
+ boolean success = false;
+ try {
+ ensureKeystoreOperationInitialized();
+ success = true;
+ } finally {
+ if (!success) {
+ resetAll();
+ }
+ }
+ }
+
+ @Override
+ protected void engineInit(Key key, AlgorithmParameterSpec params, SecureRandom random)
+ throws InvalidKeyException, InvalidAlgorithmParameterException {
+ if (params != null) {
+ throw new InvalidAlgorithmParameterException(
+ "Unsupported algorithm parameters: " + params);
+ }
+ engineInit(key, random);
+ }
+
+ @Override
+ protected Key engineDoPhase(Key key, boolean lastPhase)
+ throws InvalidKeyException, IllegalStateException {
+ ensureKeystoreOperationInitialized();
+
+ if (key == null) {
+ throw new InvalidKeyException("key == null");
+ } else if (!(key instanceof PublicKey)) {
+ throw new InvalidKeyException("Only public keys supported. Key: " + key);
+ } else if (!lastPhase) {
+ throw new IllegalStateException(
+ "Only one other party supported. lastPhase must be set to true.");
+ } else if (mOtherPartyKey != null) {
+ throw new IllegalStateException(
+ "Only one other party supported. doPhase() must only be called exactly once.");
+ }
+ // The other party key will be passed as part of the doFinal() call, to prevent an
+ // additional IPC.
+ mOtherPartyKey = (PublicKey) key;
+
+ return null; // No intermediate key
+ }
+
+ @Override
+ protected byte[] engineGenerateSecret() throws IllegalStateException {
+ try {
+ ensureKeystoreOperationInitialized();
+ } catch (InvalidKeyException e) {
+ throw new IllegalStateException("Not initialized", e);
+ }
+
+ if (mOtherPartyKey == null) {
+ throw new IllegalStateException("Other party key not provided. Call doPhase() first.");
+ }
+ byte[] otherPartyKeyEncoded = mOtherPartyKey.getEncoded();
+
+ try {
+ return mOperation.finish(otherPartyKeyEncoded, null);
+ } catch (KeyStoreException e) {
+ throw new ProviderException("Keystore operation failed", e);
+ } finally {
+ resetWhilePreservingInitState();
+ }
+ }
+
+ @Override
+ protected SecretKey engineGenerateSecret(String algorithm)
+ throws IllegalStateException, NoSuchAlgorithmException, InvalidKeyException {
+ byte[] generatedSecret = engineGenerateSecret();
+
+ return new SecretKeySpec(generatedSecret, algorithm);
+ }
+
+ @Override
+ protected int engineGenerateSecret(byte[] sharedSecret, int offset)
+ throws IllegalStateException, ShortBufferException {
+ byte[] generatedSecret = engineGenerateSecret();
+
+ if (generatedSecret.length > sharedSecret.length - offset) {
+ throw new ShortBufferException("Needed: " + generatedSecret.length);
+ }
+ System.arraycopy(generatedSecret, 0, sharedSecret, offset, generatedSecret.length);
+ return generatedSecret.length;
+ }
+
+ @Override
+ public long getOperationHandle() {
+ return mOperationHandle;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ resetAll();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ private void resetWhilePreservingInitState() {
+ KeyStoreCryptoOperationUtils.abortOperation(mOperation);
+ mOperationHandle = 0;
+ mOperation = null;
+ mOtherPartyKey = null;
+ }
+
+ private void resetAll() {
+ resetWhilePreservingInitState();
+ mKey = null;
+ }
+
+ private void ensureKeystoreOperationInitialized()
+ throws InvalidKeyException, IllegalStateException {
+ if (mKey == null) {
+ throw new IllegalStateException("Not initialized");
+ }
+ if (mOperation != null) {
+ return;
+ }
+
+ // We don't need to explicitly pass in any other parameters here, as they're part of the
+ // private key that is available to Keymint.
+ List<KeyParameter> parameters = new ArrayList<>();
+ parameters.add(KeyStore2ParameterUtils.makeEnum(
+ Tag.PURPOSE, KeyPurpose.AGREE_KEY
+ ));
+
+ try {
+ mOperation =
+ mKey.getSecurityLevel().createOperation(mKey.getKeyIdDescriptor(), parameters);
+ } catch (KeyStoreException keyStoreException) {
+ // If necessary, throw an exception due to KeyStore operation having failed.
+ InvalidKeyException e =
+ KeyStoreCryptoOperationUtils.getInvalidKeyException(mKey, keyStoreException);
+ if (e != null) {
+ throw e;
+ }
+ }
+
+ // Set the operation handle. This will be a random number, or the operation challenge if
+ // user authentication is required. If we got a challenge we check if the authorization can
+ // possibly succeed.
+ mOperationHandle =
+ KeyStoreCryptoOperationUtils.getOrMakeOperationChallenge(mOperation, mKey);
+ }
+}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
index 233f352..1575bb4 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
@@ -366,6 +366,13 @@
));
}
+ if (spec.getMaxUsageCount() != KeyProperties.UNRESTRICTED_USAGE_COUNT) {
+ params.add(KeyStore2ParameterUtils.makeInt(
+ KeymasterDefs.KM_TAG_USAGE_COUNT_LIMIT,
+ spec.getMaxUsageCount()
+ ));
+ }
+
byte[] additionalEntropy =
KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
mRng, (mKeySizeBits + 7) / 8);
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index df0e146..70e30d2 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -585,6 +585,37 @@
mSpec.getKeyValidityForConsumptionEnd()
));
}
+ if (mSpec.getCertificateNotAfter() != null) {
+ params.add(KeyStore2ParameterUtils.makeDate(
+ KeymasterDefs.KM_TAG_CERTIFICATE_NOT_AFTER,
+ mSpec.getCertificateNotAfter()
+ ));
+ }
+ if (mSpec.getCertificateNotBefore() != null) {
+ params.add(KeyStore2ParameterUtils.makeDate(
+ KeymasterDefs.KM_TAG_CERTIFICATE_NOT_BEFORE,
+ mSpec.getCertificateNotBefore()
+ ));
+ }
+ if (mSpec.getCertificateSerialNumber() != null) {
+ params.add(KeyStore2ParameterUtils.makeBignum(
+ KeymasterDefs.KM_TAG_CERTIFICATE_SERIAL,
+ mSpec.getCertificateSerialNumber()
+ ));
+ }
+ if (mSpec.getCertificateSubject() != null) {
+ params.add(KeyStore2ParameterUtils.makeBytes(
+ KeymasterDefs.KM_TAG_CERTIFICATE_SUBJECT,
+ mSpec.getCertificateSubject().getEncoded()
+ ));
+ }
+
+ if (mSpec.getMaxUsageCount() != KeyProperties.UNRESTRICTED_USAGE_COUNT) {
+ params.add(KeyStore2ParameterUtils.makeInt(
+ KeymasterDefs.KM_TAG_USAGE_COUNT_LIMIT,
+ mSpec.getMaxUsageCount()
+ ));
+ }
addAlgorithmSpecificParameters(params);
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index 403da18..e101115 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -94,6 +94,9 @@
put("KeyGenerator.DESede", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$DESede");
}
+ // javax.crypto.KeyAgreement
+ put("KeyAgreement.ECDH", PACKAGE_NAME + ".AndroidKeyStoreKeyAgreementSpi$ECDH");
+
// java.security.SecretKeyFactory
putSecretKeyFactoryImpl("AES");
if (supports3DES) {
@@ -349,17 +352,25 @@
try {
response = keyStore.getKeyEntry(descriptor);
} catch (android.security.KeyStoreException e) {
- if (e.getErrorCode() == ResponseCode.KEY_PERMANENTLY_INVALIDATED) {
- throw new KeyPermanentlyInvalidatedException(
- "User changed or deleted their auth credentials",
- e);
- } else {
- throw (UnrecoverableKeyException)
- new UnrecoverableKeyException("Failed to obtain information about key")
- .initCause(e);
+ switch (e.getErrorCode()) {
+ case ResponseCode.KEY_NOT_FOUND:
+ return null;
+ case ResponseCode.KEY_PERMANENTLY_INVALIDATED:
+ throw new KeyPermanentlyInvalidatedException(
+ "User changed or deleted their auth credentials",
+ e);
+ default:
+ throw (UnrecoverableKeyException)
+ new UnrecoverableKeyException("Failed to obtain information about key")
+ .initCause(e);
}
}
+ if (response.iSecurityLevel == null) {
+ // This seems to be a pure certificate entry, nothing to return here.
+ return null;
+ }
+
Integer keymasterAlgorithm = null;
// We just need one digest for the algorithm name
int keymasterDigest = -1;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
index 74503e1..fe05989 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -95,6 +95,7 @@
boolean userAuthenticationValidWhileOnBody = false;
boolean trustedUserPresenceRequired = false;
boolean trustedUserConfirmationRequired = false;
+ int remainingUsageCount = KeyProperties.UNRESTRICTED_USAGE_COUNT;
try {
for (Authorization a : key.getAuthorizations()) {
switch (a.keyParameter.tag) {
@@ -195,6 +196,16 @@
trustedUserConfirmationRequired =
KeyStore2ParameterUtils.isSecureHardware(a.securityLevel);
break;
+ case KeymasterDefs.KM_TAG_USAGE_COUNT_LIMIT:
+ long remainingUsageCountUnsigned =
+ KeyStore2ParameterUtils.getUnsignedInt(a);
+ if (remainingUsageCountUnsigned > Integer.MAX_VALUE) {
+ throw new ProviderException(
+ "Usage count of limited use key too long: "
+ + remainingUsageCountUnsigned);
+ }
+ remainingUsageCount = (int) remainingUsageCountUnsigned;
+ break;
}
}
} catch (IllegalArgumentException e) {
@@ -247,7 +258,8 @@
trustedUserPresenceRequired,
invalidatedByBiometricEnrollment,
trustedUserConfirmationRequired,
- securityLevel);
+ securityLevel,
+ remainingUsageCount);
}
private static BigInteger getGateKeeperSecureUserId() throws ProviderException {
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index 5e7f648..8c8acc4 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -490,7 +490,7 @@
int[] keymasterEncryptionPaddings =
KeyProperties.EncryptionPadding.allToKeymaster(
spec.getEncryptionPaddings());
- if (((spec.getPurposes() & KeyProperties.PURPOSE_DECRYPT) != 0)
+ if (((spec.getPurposes() & KeyProperties.PURPOSE_ENCRYPT) != 0)
&& (spec.isRandomizedEncryptionRequired())) {
for (int keymasterPadding : keymasterEncryptionPaddings) {
if (!KeymasterUtils
@@ -535,6 +535,12 @@
spec.getKeyValidityForConsumptionEnd()
));
}
+ if (spec.getMaxUsageCount() != KeyProperties.UNRESTRICTED_USAGE_COUNT) {
+ importArgs.add(KeyStore2ParameterUtils.makeInt(
+ KeymasterDefs.KM_TAG_USAGE_COUNT_LIMIT,
+ spec.getMaxUsageCount()
+ ));
+ }
} catch (IllegalArgumentException | IllegalStateException e) {
throw new KeyStoreException(e);
}
@@ -772,6 +778,12 @@
params.getKeyValidityForConsumptionEnd()
));
}
+ if (params.getMaxUsageCount() != KeyProperties.UNRESTRICTED_USAGE_COUNT) {
+ importArgs.add(KeyStore2ParameterUtils.makeInt(
+ KeymasterDefs.KM_TAG_USAGE_COUNT_LIMIT,
+ params.getMaxUsageCount()
+ ));
+ }
} catch (IllegalArgumentException | IllegalStateException e) {
throw new KeyStoreException(e);
}
diff --git a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
index 4c8ab8d..dcdd7de 100644
--- a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
+++ b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
@@ -28,6 +28,7 @@
import android.security.keystore.UserAuthArgs;
import android.system.keystore2.Authorization;
+import java.math.BigInteger;
import java.security.ProviderException;
import java.util.ArrayList;
import java.util.Date;
@@ -154,6 +155,23 @@
}
/**
+ * This function constructs a {@link KeyParameter} expressing a Bignum.
+ * @param tag Must be KeyMint tag with the associated type BIGNUM.
+ * @param b A BitInteger to be stored in the new key parameter.
+ * @return An instance of {@link KeyParameter}.
+ * @hide
+ */
+ static @NonNull KeyParameter makeBignum(int tag, @NonNull BigInteger b) {
+ if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_BIGNUM) {
+ throw new IllegalArgumentException("Not a bignum tag: " + tag);
+ }
+ KeyParameter p = new KeyParameter();
+ p.tag = tag;
+ p.value = KeyParameterValue.blob(b.toByteArray());
+ return p;
+ }
+
+ /**
* This function constructs a {@link KeyParameter} expressing date.
* @param tag Must be KeyMint tag with the associated type DATE.
* @param date A date
@@ -167,10 +185,6 @@
KeyParameter p = new KeyParameter();
p.tag = tag;
p.value = KeyParameterValue.dateTime(date.getTime());
- if (p.value.getDateTime() < 0) {
- throw new IllegalArgumentException("Date tag value out of range: "
- + p.value.getDateTime());
- }
return p;
}
/**
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index d67cf8c..621f286 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -301,7 +301,8 @@
}
sk_sp<SkImage> image = bitmap.makeImage();
- applyLooper(get_looper(paint), *filterBitmap(paint), [&](SkScalar x, SkScalar y, const SkPaint& p) {
+ applyLooper(get_looper(paint), *filterBitmap(std::move(filteredPaint)),
+ [&](SkScalar x, SkScalar y, const SkPaint& p) {
mRecorder.drawImageLattice(image, lattice, dst.makeOffset(x, y), &p, bitmap.palette());
});
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 33c6951..ccb74eb 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -1445,6 +1445,7 @@
@TestApi
@RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
@Nullable
+ @SuppressWarnings("NullableCollection")
public List<String> getProviderPackages(@NonNull String provider) {
try {
return mService.getProviderPackages(provider);
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 7dff0c2..d22e97c 100755
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -5434,8 +5434,12 @@
public boolean setAdditionalOutputDeviceDelay(
@NonNull AudioDeviceInfo device, @IntRange(from = 0) long delayMillis) {
Objects.requireNonNull(device);
- // Implement the setter in r-dev or r-tv-dev as needed.
- return false;
+ try {
+ return getService().setAdditionalOutputDeviceDelay(
+ new AudioDeviceAttributes(device), delayMillis);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -5450,8 +5454,11 @@
@IntRange(from = 0)
public long getAdditionalOutputDeviceDelay(@NonNull AudioDeviceInfo device) {
Objects.requireNonNull(device);
- // Implement the getter in r-dev or r-tv-dev as needed.
- return 0;
+ try {
+ return getService().getAdditionalOutputDeviceDelay(new AudioDeviceAttributes(device));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -5468,8 +5475,12 @@
@IntRange(from = 0)
public long getMaxAdditionalOutputDeviceDelay(@NonNull AudioDeviceInfo device) {
Objects.requireNonNull(device);
- // Implement the getter in r-dev or r-tv-dev as needed.
- return 0;
+ try {
+ return getService().getMaxAdditionalOutputDeviceDelay(
+ new AudioDeviceAttributes(device));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index b2c2c4b..d7ef454 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1269,10 +1269,12 @@
// native code figure out the minimum buffer size.
if (mMode == MODE_STREAM && mBufferSizeInBytes == 0) {
int bytesPerSample = 1;
- try {
- bytesPerSample = mFormat.getBytesPerSample(mFormat.getEncoding());
- } catch (IllegalArgumentException e) {
- // do nothing
+ if (AudioFormat.isEncodingLinearFrames(mFormat.getEncoding())) {
+ try {
+ bytesPerSample = mFormat.getBytesPerSample(mFormat.getEncoding());
+ } catch (IllegalArgumentException e) {
+ // do nothing
+ }
}
mBufferSizeInBytes = mFormat.getChannelCount() * bytesPerSample;
}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index ebaa316..ed48b56 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -330,4 +330,10 @@
oneway void unregisterCommunicationDeviceDispatcher(
ICommunicationDeviceDispatcher dispatcher);
+
+ boolean setAdditionalOutputDeviceDelay(in AudioDeviceAttributes device, long delayMillis);
+
+ long getAdditionalOutputDeviceDelay(in AudioDeviceAttributes device);
+
+ long getMaxAdditionalOutputDeviceDelay(in AudioDeviceAttributes device);
}
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 5daf8b0..694b939 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -169,8 +169,9 @@
static int IP_V4_LENGTH = 4;
static int IP_V6_LENGTH = 16;
-void DestroyCallback(const C2Buffer * /* buf */, void *arg) {
+void DestroyCallback(const C2Buffer * buf, void *arg) {
android::sp<android::MediaEvent> event = (android::MediaEvent *)arg;
+ android::Mutex::Autolock autoLock(event->mLock);
if (event->mLinearBlockObj != NULL) {
JNIEnv *env = android::AndroidRuntime::getJNIEnv();
env->DeleteWeakGlobalRef(event->mLinearBlockObj);
@@ -179,6 +180,7 @@
event->mAvHandleRefCnt--;
event->finalize();
+ event->decStrong(buf);
}
namespace android {
@@ -369,6 +371,7 @@
pC2Buffer->setInfo(info);
}
pC2Buffer->registerOnDestroyNotify(&DestroyCallback, this);
+ incStrong(pC2Buffer.get());
jobject linearBlock =
env->NewObject(
env->FindClass("android/media/MediaCodec$LinearBlock"),
@@ -3646,6 +3649,7 @@
ALOGD("Failed get MediaEvent");
return NULL;
}
+ android::Mutex::Autolock autoLock(mediaEventSp->mLock);
return mediaEventSp->getLinearBlock();
}
diff --git a/media/native/midi/include/amidi/AMidi.h b/media/native/midi/include/amidi/AMidi.h
index 0f930b5..742db34 100644
--- a/media/native/midi/include/amidi/AMidi.h
+++ b/media/native/midi/include/amidi/AMidi.h
@@ -61,8 +61,6 @@
AMIDI_DEVICE_TYPE_BLUETOOTH = 3 /* A MIDI device connected via BlueTooth */
};
-#if __ANDROID_API__ >= 29
-
/*
* Device API
*/
@@ -249,8 +247,6 @@
*/
void AMIDI_API AMidiInputPort_close(const AMidiInputPort *inputPort) __INTRODUCED_IN(29);
-#endif /* __ANDROID_API__ >= 29 */
-
#ifdef __cplusplus
}
#endif
diff --git a/native/android/OWNERS b/native/android/OWNERS
index ac5a895..d414ed4 100644
--- a/native/android/OWNERS
+++ b/native/android/OWNERS
@@ -1,3 +1,4 @@
per-file libandroid_net.map.txt, net.c = set noparent
per-file libandroid_net.map.txt, net.c = codewiz@google.com, jchalard@google.com, junyulai@google.com
per-file libandroid_net.map.txt, net.c = lorenzo@google.com, reminv@google.com, satk@google.com
+per-file system_fonts.cpp = file:/graphics/java/android/graphics/fonts/OWNERS
diff --git a/packages/Connectivity/framework/Android.bp b/packages/Connectivity/framework/Android.bp
new file mode 100644
index 0000000..8db8d76
--- /dev/null
+++ b/packages/Connectivity/framework/Android.bp
@@ -0,0 +1,29 @@
+//
+// 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.
+//
+
+// TODO: use a java_library in the bootclasspath instead
+filegroup {
+ name: "framework-connectivity-sources",
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.aidl",
+ ],
+ path: "src",
+ visibility: [
+ "//frameworks/base",
+ "//packages/modules/Connectivity:__subpackages__",
+ ],
+}
\ No newline at end of file
diff --git a/core/java/android/net/CaptivePortal.java b/packages/Connectivity/framework/src/android/net/CaptivePortal.java
similarity index 100%
rename from core/java/android/net/CaptivePortal.java
rename to packages/Connectivity/framework/src/android/net/CaptivePortal.java
diff --git a/core/java/android/net/CaptivePortalData.aidl b/packages/Connectivity/framework/src/android/net/CaptivePortalData.aidl
similarity index 100%
rename from core/java/android/net/CaptivePortalData.aidl
rename to packages/Connectivity/framework/src/android/net/CaptivePortalData.aidl
diff --git a/core/java/android/net/CaptivePortalData.java b/packages/Connectivity/framework/src/android/net/CaptivePortalData.java
similarity index 99%
rename from core/java/android/net/CaptivePortalData.java
rename to packages/Connectivity/framework/src/android/net/CaptivePortalData.java
index 18467fa..9b56b23 100644
--- a/core/java/android/net/CaptivePortalData.java
+++ b/packages/Connectivity/framework/src/android/net/CaptivePortalData.java
@@ -276,7 +276,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (!(obj instanceof CaptivePortalData)) return false;
final CaptivePortalData other = (CaptivePortalData) obj;
return mRefreshTimeMillis == other.mRefreshTimeMillis
diff --git a/core/java/android/net/ConnectionInfo.aidl b/packages/Connectivity/framework/src/android/net/ConnectionInfo.aidl
similarity index 100%
rename from core/java/android/net/ConnectionInfo.aidl
rename to packages/Connectivity/framework/src/android/net/ConnectionInfo.aidl
diff --git a/core/java/android/net/ConnectionInfo.java b/packages/Connectivity/framework/src/android/net/ConnectionInfo.java
similarity index 100%
rename from core/java/android/net/ConnectionInfo.java
rename to packages/Connectivity/framework/src/android/net/ConnectionInfo.java
diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.aidl b/packages/Connectivity/framework/src/android/net/ConnectivityDiagnosticsManager.aidl
similarity index 100%
rename from core/java/android/net/ConnectivityDiagnosticsManager.aidl
rename to packages/Connectivity/framework/src/android/net/ConnectivityDiagnosticsManager.aidl
diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityDiagnosticsManager.java
similarity index 100%
rename from core/java/android/net/ConnectivityDiagnosticsManager.java
rename to packages/Connectivity/framework/src/android/net/ConnectivityDiagnosticsManager.java
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityFrameworkInitializer.java b/packages/Connectivity/framework/src/android/net/ConnectivityFrameworkInitializer.java
new file mode 100644
index 0000000..9afa5d1
--- /dev/null
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityFrameworkInitializer.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.SystemApi;
+import android.app.SystemServiceRegistry;
+import android.content.Context;
+
+/**
+ * Class for performing registration for all core connectivity services.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class ConnectivityFrameworkInitializer {
+ private ConnectivityFrameworkInitializer() {}
+
+ /**
+ * Called by {@link SystemServiceRegistry}'s static initializer and registers all core
+ * connectivity services to {@link Context}, so that {@link Context#getSystemService} can
+ * return them.
+ *
+ * @throws IllegalStateException if this is called anywhere besides
+ * {@link SystemServiceRegistry}.
+ */
+ public static void registerServiceWrappers() {
+ // registerContextAwareService will throw if this is called outside of SystemServiceRegistry
+ // initialization.
+ SystemServiceRegistry.registerContextAwareService(
+ Context.CONNECTIVITY_SERVICE,
+ ConnectivityManager.class,
+ (context, serviceBinder) -> {
+ IConnectivityManager icm = IConnectivityManager.Stub.asInterface(serviceBinder);
+ return new ConnectivityManager(context, icm);
+ }
+ );
+
+ // TODO: move outside of the connectivity JAR
+ SystemServiceRegistry.registerContextAwareService(
+ Context.VPN_MANAGEMENT_SERVICE,
+ VpnManager.class,
+ (context) -> {
+ final ConnectivityManager cm = context.getSystemService(
+ ConnectivityManager.class);
+ return cm.createVpnManager();
+ }
+ );
+
+ SystemServiceRegistry.registerContextAwareService(
+ Context.CONNECTIVITY_DIAGNOSTICS_SERVICE,
+ ConnectivityDiagnosticsManager.class,
+ (context) -> {
+ final ConnectivityManager cm = context.getSystemService(
+ ConnectivityManager.class);
+ return cm.createDiagnosticsManager();
+ }
+ );
+
+ SystemServiceRegistry.registerContextAwareService(
+ Context.TEST_NETWORK_SERVICE,
+ TestNetworkManager.class,
+ context -> {
+ final ConnectivityManager cm = context.getSystemService(
+ ConnectivityManager.class);
+ return cm.startOrGetTestNetworkManager();
+ }
+ );
+ }
+}
diff --git a/core/java/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
similarity index 96%
rename from core/java/android/net/ConnectivityManager.java
rename to packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index d107261..fef4152 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -15,7 +15,9 @@
*/
package android.net;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
import static android.net.IpSecManager.INVALID_RESOURCE_ID;
+import static android.net.NetworkRequest.Type.BACKGROUND_REQUEST;
import static android.net.NetworkRequest.Type.LISTEN;
import static android.net.NetworkRequest.Type.REQUEST;
import static android.net.NetworkRequest.Type.TRACK_DEFAULT;
@@ -28,6 +30,7 @@
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.app.PendingIntent;
@@ -3228,32 +3231,6 @@
}
}
- /** {@hide} - returns the factory serial number */
- @UnsupportedAppUsage
- @RequiresPermission(anyOf = {
- NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
- android.Manifest.permission.NETWORK_FACTORY})
- public int registerNetworkFactory(Messenger messenger, String name) {
- try {
- return mService.registerNetworkFactory(messenger, name);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /** {@hide} */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- @RequiresPermission(anyOf = {
- NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
- android.Manifest.permission.NETWORK_FACTORY})
- public void unregisterNetworkFactory(Messenger messenger) {
- try {
- mService.unregisterNetworkFactory(messenger);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
/**
* Registers the specified {@link NetworkProvider}.
* Each listener must only be registered once. The listener can be unregistered with
@@ -4820,6 +4797,28 @@
}
}
+ /** @hide */
+ public TestNetworkManager startOrGetTestNetworkManager() {
+ final IBinder tnBinder;
+ try {
+ tnBinder = mService.startOrGetTestNetworkService();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ return new TestNetworkManager(ITestNetworkManager.Stub.asInterface(tnBinder));
+ }
+
+ /** @hide */
+ public VpnManager createVpnManager() {
+ return new VpnManager(mContext, mService);
+ }
+
+ /** @hide */
+ public ConnectivityDiagnosticsManager createDiagnosticsManager() {
+ return new ConnectivityDiagnosticsManager(mContext, mService);
+ }
+
/**
* Simulates a Data Stall for the specified Network.
*
@@ -4846,9 +4845,13 @@
}
}
- private void setOemNetworkPreference(@NonNull OemNetworkPreferences preference) {
- Log.d(TAG, "setOemNetworkPreference called with preference: "
- + preference.toString());
+ private void setOemNetworkPreference(@NonNull final OemNetworkPreferences preference) {
+ try {
+ mService.setOemNetworkPreference(preference);
+ } catch (RemoteException e) {
+ Log.e(TAG, "setOemNetworkPreference() failed for preference: " + preference.toString());
+ throw e.rethrowFromSystemServer();
+ }
}
@NonNull
@@ -4964,4 +4967,92 @@
}
return null;
}
+
+ /**
+ * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, but
+ * does not cause any networks to retain the NET_CAPABILITY_FOREGROUND capability. This can
+ * be used to request that the system provide a network without causing the network to be
+ * in the foreground.
+ *
+ * <p>This method will attempt to find the best network that matches the passed
+ * {@link NetworkRequest}, and to bring up one that does if none currently satisfies the
+ * criteria. The platform will evaluate which network is the best at its own discretion.
+ * Throughput, latency, cost per byte, policy, user preference and other considerations
+ * may be factored in the decision of what is considered the best network.
+ *
+ * <p>As long as this request is outstanding, the platform will try to maintain the best network
+ * matching this request, while always attempting to match the request to a better network if
+ * possible. If a better match is found, the platform will switch this request to the now-best
+ * network and inform the app of the newly best network by invoking
+ * {@link NetworkCallback#onAvailable(Network)} on the provided callback. Note that the platform
+ * will not try to maintain any other network than the best one currently matching the request:
+ * a network not matching any network request may be disconnected at any time.
+ *
+ * <p>For example, an application could use this method to obtain a connected cellular network
+ * even if the device currently has a data connection over Ethernet. This may cause the cellular
+ * radio to consume additional power. Or, an application could inform the system that it wants
+ * a network supporting sending MMSes and have the system let it know about the currently best
+ * MMS-supporting network through the provided {@link NetworkCallback}.
+ *
+ * <p>The status of the request can be followed by listening to the various callbacks described
+ * in {@link NetworkCallback}. The {@link Network} object passed to the callback methods can be
+ * used to direct traffic to the network (although accessing some networks may be subject to
+ * holding specific permissions). Callers will learn about the specific characteristics of the
+ * network through
+ * {@link NetworkCallback#onCapabilitiesChanged(Network, NetworkCapabilities)} and
+ * {@link NetworkCallback#onLinkPropertiesChanged(Network, LinkProperties)}. The methods of the
+ * provided {@link NetworkCallback} will only be invoked due to changes in the best network
+ * matching the request at any given time; therefore when a better network matching the request
+ * becomes available, the {@link NetworkCallback#onAvailable(Network)} method is called
+ * with the new network after which no further updates are given about the previously-best
+ * network, unless it becomes the best again at some later time. All callbacks are invoked
+ * in order on the same thread, which by default is a thread created by the framework running
+ * in the app.
+ *
+ * <p>This{@link NetworkRequest} will live until released via
+ * {@link #unregisterNetworkCallback(NetworkCallback)} or the calling application exits, at
+ * which point the system may let go of the network at any time.
+ *
+ * <p>It is presently unsupported to request a network with mutable
+ * {@link NetworkCapabilities} such as
+ * {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED} or
+ * {@link NetworkCapabilities#NET_CAPABILITY_CAPTIVE_PORTAL}
+ * as these {@code NetworkCapabilities} represent states that a particular
+ * network may never attain, and whether a network will attain these states
+ * is unknown prior to bringing up the network so the framework does not
+ * know how to go about satisfying a request with these capabilities.
+ *
+ * <p>To avoid performance issues due to apps leaking callbacks, the system will limit the
+ * number of outstanding requests to 100 per app (identified by their UID), shared with
+ * all variants of this method, of {@link #registerNetworkCallback} as well as
+ * {@link ConnectivityDiagnosticsManager#registerConnectivityDiagnosticsCallback}.
+ * Requesting a network with this method will count toward this limit. If this limit is
+ * exceeded, an exception will be thrown. To avoid hitting this issue and to conserve resources,
+ * make sure to unregister the callbacks with
+ * {@link #unregisterNetworkCallback(NetworkCallback)}.
+ *
+ * @param request {@link NetworkRequest} describing this request.
+ * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
+ * If null, the callback is invoked on the default internal Handler.
+ * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
+ * the callback must not be shared - it uniquely specifies this request.
+ * @throws IllegalArgumentException if {@code request} contains invalid network capabilities.
+ * @throws SecurityException if missing the appropriate permissions.
+ * @throws RuntimeException if the app already has too many callbacks registered.
+ *
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ @SuppressLint("ExecutorRegistration")
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_STACK,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
+ })
+ public void requestBackgroundNetwork(@NonNull NetworkRequest request,
+ @Nullable Handler handler, @NonNull NetworkCallback networkCallback) {
+ final NetworkCapabilities nc = request.networkCapabilities;
+ sendRequestForNetwork(nc, networkCallback, 0, BACKGROUND_REQUEST,
+ TYPE_NONE, handler == null ? getDefaultHandler() : new CallbackHandler(handler));
+ }
}
diff --git a/core/java/android/net/ConnectivityMetricsEvent.aidl b/packages/Connectivity/framework/src/android/net/ConnectivityMetricsEvent.aidl
similarity index 100%
rename from core/java/android/net/ConnectivityMetricsEvent.aidl
rename to packages/Connectivity/framework/src/android/net/ConnectivityMetricsEvent.aidl
diff --git a/core/java/android/net/ConnectivityThread.java b/packages/Connectivity/framework/src/android/net/ConnectivityThread.java
similarity index 100%
rename from core/java/android/net/ConnectivityThread.java
rename to packages/Connectivity/framework/src/android/net/ConnectivityThread.java
diff --git a/core/java/android/net/DhcpInfo.aidl b/packages/Connectivity/framework/src/android/net/DhcpInfo.aidl
similarity index 100%
rename from core/java/android/net/DhcpInfo.aidl
rename to packages/Connectivity/framework/src/android/net/DhcpInfo.aidl
diff --git a/core/java/android/net/DhcpInfo.java b/packages/Connectivity/framework/src/android/net/DhcpInfo.java
similarity index 100%
rename from core/java/android/net/DhcpInfo.java
rename to packages/Connectivity/framework/src/android/net/DhcpInfo.java
diff --git a/core/java/android/net/DnsResolver.java b/packages/Connectivity/framework/src/android/net/DnsResolver.java
similarity index 100%
rename from core/java/android/net/DnsResolver.java
rename to packages/Connectivity/framework/src/android/net/DnsResolver.java
diff --git a/core/java/android/net/ICaptivePortal.aidl b/packages/Connectivity/framework/src/android/net/ICaptivePortal.aidl
similarity index 100%
rename from core/java/android/net/ICaptivePortal.aidl
rename to packages/Connectivity/framework/src/android/net/ICaptivePortal.aidl
diff --git a/core/java/android/net/IConnectivityDiagnosticsCallback.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityDiagnosticsCallback.aidl
similarity index 100%
rename from core/java/android/net/IConnectivityDiagnosticsCallback.aidl
rename to packages/Connectivity/framework/src/android/net/IConnectivityDiagnosticsCallback.aidl
diff --git a/core/java/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
similarity index 97%
rename from core/java/android/net/IConnectivityManager.aidl
rename to packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
index 6fecee6..e2672c4 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
@@ -29,6 +29,7 @@
import android.net.NetworkInfo;
import android.net.NetworkRequest;
import android.net.NetworkState;
+import android.net.OemNetworkPreferences;
import android.net.ProxyInfo;
import android.net.UidRange;
import android.net.QosSocketInfo;
@@ -43,7 +44,6 @@
import com.android.connectivity.aidl.INetworkAgent;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
-import com.android.internal.net.VpnInfo;
import com.android.internal.net.VpnProfile;
/**
@@ -157,9 +157,6 @@
boolean requestBandwidthUpdate(in Network network);
- int registerNetworkFactory(in Messenger messenger, in String name);
- void unregisterNetworkFactory(in Messenger messenger);
-
int registerNetworkProvider(in Messenger messenger, in String name);
void unregisterNetworkProvider(in Messenger messenger);
@@ -244,4 +241,6 @@
void registerQosSocketCallback(in QosSocketInfo socketInfo, in IQosCallback callback);
void unregisterQosCallback(in IQosCallback callback);
+
+ void setOemNetworkPreference(in OemNetworkPreferences preference);
}
diff --git a/core/java/android/net/ISocketKeepaliveCallback.aidl b/packages/Connectivity/framework/src/android/net/ISocketKeepaliveCallback.aidl
similarity index 100%
rename from core/java/android/net/ISocketKeepaliveCallback.aidl
rename to packages/Connectivity/framework/src/android/net/ISocketKeepaliveCallback.aidl
diff --git a/core/java/android/net/ITestNetworkManager.aidl b/packages/Connectivity/framework/src/android/net/ITestNetworkManager.aidl
similarity index 100%
rename from core/java/android/net/ITestNetworkManager.aidl
rename to packages/Connectivity/framework/src/android/net/ITestNetworkManager.aidl
diff --git a/core/java/android/net/InetAddresses.java b/packages/Connectivity/framework/src/android/net/InetAddresses.java
similarity index 100%
rename from core/java/android/net/InetAddresses.java
rename to packages/Connectivity/framework/src/android/net/InetAddresses.java
diff --git a/core/java/android/net/InterfaceConfiguration.aidl b/packages/Connectivity/framework/src/android/net/InterfaceConfiguration.aidl
similarity index 100%
rename from core/java/android/net/InterfaceConfiguration.aidl
rename to packages/Connectivity/framework/src/android/net/InterfaceConfiguration.aidl
diff --git a/core/java/android/net/InvalidPacketException.java b/packages/Connectivity/framework/src/android/net/InvalidPacketException.java
similarity index 100%
rename from core/java/android/net/InvalidPacketException.java
rename to packages/Connectivity/framework/src/android/net/InvalidPacketException.java
diff --git a/core/java/android/net/IpConfiguration.aidl b/packages/Connectivity/framework/src/android/net/IpConfiguration.aidl
similarity index 100%
rename from core/java/android/net/IpConfiguration.aidl
rename to packages/Connectivity/framework/src/android/net/IpConfiguration.aidl
diff --git a/core/java/android/net/IpConfiguration.java b/packages/Connectivity/framework/src/android/net/IpConfiguration.java
similarity index 98%
rename from core/java/android/net/IpConfiguration.java
rename to packages/Connectivity/framework/src/android/net/IpConfiguration.java
index 0b20564..d5f8b2e 100644
--- a/core/java/android/net/IpConfiguration.java
+++ b/packages/Connectivity/framework/src/android/net/IpConfiguration.java
@@ -167,7 +167,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o == this) {
return true;
}
diff --git a/core/java/android/net/IpPrefix.aidl b/packages/Connectivity/framework/src/android/net/IpPrefix.aidl
similarity index 100%
rename from core/java/android/net/IpPrefix.aidl
rename to packages/Connectivity/framework/src/android/net/IpPrefix.aidl
diff --git a/core/java/android/net/IpPrefix.java b/packages/Connectivity/framework/src/android/net/IpPrefix.java
similarity index 98%
rename from core/java/android/net/IpPrefix.java
rename to packages/Connectivity/framework/src/android/net/IpPrefix.java
index e7c8014..bcb65fa 100644
--- a/core/java/android/net/IpPrefix.java
+++ b/packages/Connectivity/framework/src/android/net/IpPrefix.java
@@ -18,6 +18,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -124,7 +125,7 @@
* @return {@code true} if both objects are equal, {@code false} otherwise.
*/
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (!(obj instanceof IpPrefix)) {
return false;
}
diff --git a/core/java/android/net/KeepalivePacketData.aidl b/packages/Connectivity/framework/src/android/net/KeepalivePacketData.aidl
similarity index 100%
rename from core/java/android/net/KeepalivePacketData.aidl
rename to packages/Connectivity/framework/src/android/net/KeepalivePacketData.aidl
diff --git a/core/java/android/net/KeepalivePacketData.java b/packages/Connectivity/framework/src/android/net/KeepalivePacketData.java
similarity index 100%
rename from core/java/android/net/KeepalivePacketData.java
rename to packages/Connectivity/framework/src/android/net/KeepalivePacketData.java
diff --git a/core/java/android/net/LinkAddress.aidl b/packages/Connectivity/framework/src/android/net/LinkAddress.aidl
similarity index 100%
rename from core/java/android/net/LinkAddress.aidl
rename to packages/Connectivity/framework/src/android/net/LinkAddress.aidl
diff --git a/core/java/android/net/LinkAddress.java b/packages/Connectivity/framework/src/android/net/LinkAddress.java
similarity index 99%
rename from core/java/android/net/LinkAddress.java
rename to packages/Connectivity/framework/src/android/net/LinkAddress.java
index 44d25a1..d1bdaa0 100644
--- a/core/java/android/net/LinkAddress.java
+++ b/packages/Connectivity/framework/src/android/net/LinkAddress.java
@@ -349,7 +349,7 @@
* @return {@code true} if both objects are equal, {@code false} otherwise.
*/
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (!(obj instanceof LinkAddress)) {
return false;
}
diff --git a/core/java/android/net/LinkProperties.aidl b/packages/Connectivity/framework/src/android/net/LinkProperties.aidl
similarity index 100%
rename from core/java/android/net/LinkProperties.aidl
rename to packages/Connectivity/framework/src/android/net/LinkProperties.aidl
diff --git a/core/java/android/net/LinkProperties.java b/packages/Connectivity/framework/src/android/net/LinkProperties.java
similarity index 99%
rename from core/java/android/net/LinkProperties.java
rename to packages/Connectivity/framework/src/android/net/LinkProperties.java
index 486e2d7..e41ed72 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/packages/Connectivity/framework/src/android/net/LinkProperties.java
@@ -1613,7 +1613,7 @@
* @return {@code true} if both objects are equal, {@code false} otherwise.
*/
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) return true;
if (!(obj instanceof LinkProperties)) return false;
diff --git a/core/java/android/net/MacAddress.aidl b/packages/Connectivity/framework/src/android/net/MacAddress.aidl
similarity index 100%
rename from core/java/android/net/MacAddress.aidl
rename to packages/Connectivity/framework/src/android/net/MacAddress.aidl
diff --git a/core/java/android/net/MacAddress.java b/packages/Connectivity/framework/src/android/net/MacAddress.java
similarity index 99%
rename from core/java/android/net/MacAddress.java
rename to packages/Connectivity/framework/src/android/net/MacAddress.java
index c7116b4..c83c23a 100644
--- a/core/java/android/net/MacAddress.java
+++ b/packages/Connectivity/framework/src/android/net/MacAddress.java
@@ -161,7 +161,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
return (o instanceof MacAddress) && ((MacAddress) o).mAddr == mAddr;
}
diff --git a/core/java/android/net/NattKeepalivePacketData.java b/packages/Connectivity/framework/src/android/net/NattKeepalivePacketData.java
similarity index 100%
rename from core/java/android/net/NattKeepalivePacketData.java
rename to packages/Connectivity/framework/src/android/net/NattKeepalivePacketData.java
diff --git a/core/java/android/net/NattSocketKeepalive.java b/packages/Connectivity/framework/src/android/net/NattSocketKeepalive.java
similarity index 100%
rename from core/java/android/net/NattSocketKeepalive.java
rename to packages/Connectivity/framework/src/android/net/NattSocketKeepalive.java
diff --git a/core/java/android/net/Network.aidl b/packages/Connectivity/framework/src/android/net/Network.aidl
similarity index 100%
rename from core/java/android/net/Network.aidl
rename to packages/Connectivity/framework/src/android/net/Network.aidl
diff --git a/core/java/android/net/Network.java b/packages/Connectivity/framework/src/android/net/Network.java
similarity index 94%
rename from core/java/android/net/Network.java
rename to packages/Connectivity/framework/src/android/net/Network.java
index f98a1f8..46141e0 100644
--- a/core/java/android/net/Network.java
+++ b/packages/Connectivity/framework/src/android/net/Network.java
@@ -17,10 +17,12 @@
package android.net;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import android.system.ErrnoException;
import android.system.Os;
@@ -380,7 +382,13 @@
// Query a property of the underlying socket to ensure that the socket's file descriptor
// exists, is available to bind to a network and is not closed.
socket.getReuseAddress();
- bindSocket(socket.getFileDescriptor$());
+ final ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(socket);
+ bindSocket(pfd.getFileDescriptor());
+ // ParcelFileDescriptor.fromSocket() creates a dup of the original fd. The original and the
+ // dup share the underlying socket in the kernel. The socket is never truly closed until the
+ // last fd pointing to the socket being closed. So close the dup one after binding the
+ // socket to control the lifetime of the dup fd.
+ pfd.close();
}
/**
@@ -392,7 +400,13 @@
// Query a property of the underlying socket to ensure that the socket's file descriptor
// exists, is available to bind to a network and is not closed.
socket.getReuseAddress();
- bindSocket(socket.getFileDescriptor$());
+ final ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket);
+ bindSocket(pfd.getFileDescriptor());
+ // ParcelFileDescriptor.fromSocket() creates a dup of the original fd. The original and the
+ // dup share the underlying socket in the kernel. The socket is never truly closed until the
+ // last fd pointing to the socket being closed. So close the dup one after binding the
+ // socket to control the lifetime of the dup fd.
+ pfd.close();
}
/**
@@ -420,7 +434,7 @@
throw new SocketException("Only AF_INET/AF_INET6 sockets supported");
}
- final int err = NetworkUtils.bindSocketToNetwork(fd.getInt$(), netId);
+ final int err = NetworkUtils.bindSocketToNetwork(fd, netId);
if (err != 0) {
// bindSocketToNetwork returns negative errno.
throw new ErrnoException("Binding socket to network " + netId, -err)
@@ -497,7 +511,7 @@
};
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (!(obj instanceof Network)) return false;
Network other = (Network)obj;
return this.netId == other.netId;
diff --git a/core/java/android/net/NetworkAgent.java b/packages/Connectivity/framework/src/android/net/NetworkAgent.java
similarity index 99%
rename from core/java/android/net/NetworkAgent.java
rename to packages/Connectivity/framework/src/android/net/NetworkAgent.java
index d22d82d..27aa15d 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkAgent.java
@@ -20,6 +20,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -775,7 +776,8 @@
* @param underlyingNetworks the new list of underlying networks.
* @see {@link VpnService.Builder#setUnderlyingNetworks(Network[])}
*/
- public final void setUnderlyingNetworks(@Nullable List<Network> underlyingNetworks) {
+ public final void setUnderlyingNetworks(
+ @SuppressLint("NullableCollection") @Nullable List<Network> underlyingNetworks) {
final ArrayList<Network> underlyingArray = (underlyingNetworks != null)
? new ArrayList<>(underlyingNetworks) : null;
queueOrSendMessage(reg -> reg.sendUnderlyingNetworks(underlyingArray));
diff --git a/core/java/android/net/NetworkAgentConfig.aidl b/packages/Connectivity/framework/src/android/net/NetworkAgentConfig.aidl
similarity index 100%
rename from core/java/android/net/NetworkAgentConfig.aidl
rename to packages/Connectivity/framework/src/android/net/NetworkAgentConfig.aidl
diff --git a/core/java/android/net/NetworkAgentConfig.java b/packages/Connectivity/framework/src/android/net/NetworkAgentConfig.java
similarity index 98%
rename from core/java/android/net/NetworkAgentConfig.java
rename to packages/Connectivity/framework/src/android/net/NetworkAgentConfig.java
index fe1268d..664c265 100644
--- a/core/java/android/net/NetworkAgentConfig.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkAgentConfig.java
@@ -16,6 +16,8 @@
package android.net;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -125,6 +127,7 @@
* @return the subscriber ID, or null if none.
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
@Nullable
public String getSubscriberId() {
return subscriberId;
@@ -275,6 +278,7 @@
* @hide
*/
@NonNull
+ @SystemApi(client = MODULE_LIBRARIES)
public Builder setSubscriberId(@Nullable String subscriberId) {
mConfig.subscriberId = subscriberId;
return this;
diff --git a/core/java/android/net/NetworkCapabilities.aidl b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.aidl
similarity index 100%
rename from core/java/android/net/NetworkCapabilities.aidl
rename to packages/Connectivity/framework/src/android/net/NetworkCapabilities.aidl
diff --git a/core/java/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
similarity index 96%
rename from core/java/android/net/NetworkCapabilities.java
rename to packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
index 0a895b9..55b2c3c 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
@@ -34,9 +34,9 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.BitUtils;
import com.android.internal.util.Preconditions;
+import com.android.net.module.util.CollectionUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -204,6 +204,7 @@
NET_CAPABILITY_TEMPORARILY_NOT_METERED,
NET_CAPABILITY_OEM_PRIVATE,
NET_CAPABILITY_VEHICLE_INTERNAL,
+ NET_CAPABILITY_NOT_VCN_MANAGED,
})
public @interface NetCapability { }
@@ -399,8 +400,23 @@
@SystemApi
public static final int NET_CAPABILITY_VEHICLE_INTERNAL = 27;
+ /**
+ * Indicates that this network is not subsumed by a Virtual Carrier Network (VCN).
+ * <p>
+ * To provide an experience on a VCN similar to a single traditional carrier network, in
+ * some cases the system sets this bit is set by default in application's network requests,
+ * and may choose to remove it at its own discretion when matching the request to a network.
+ * <p>
+ * Applications that want to know about a Virtual Carrier Network's underlying networks,
+ * for example to use them for multipath purposes, should remove this bit from their network
+ * requests ; the system will not add it back once removed.
+ * @hide
+ */
+ @SystemApi
+ public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28;
+
private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
- private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_VEHICLE_INTERNAL;
+ private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_VCN_MANAGED;
/**
* Network capabilities that are expected to be mutable, i.e., can change while a particular
@@ -417,7 +433,8 @@
| (1 << NET_CAPABILITY_NOT_CONGESTED)
| (1 << NET_CAPABILITY_NOT_SUSPENDED)
| (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY)
- | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED);
+ | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED)
+ | (1 << NET_CAPABILITY_NOT_VCN_MANAGED);
/**
* Network capabilities that are not allowed in NetworkRequests. This exists because the
@@ -426,16 +443,21 @@
* can get into a cycle where the NetworkFactory endlessly churns out NetworkAgents that then
* get immediately torn down because they do not have the requested capability.
*/
+ // Note that as a historical exception, the TRUSTED and NOT_VCN_MANAGED capabilities
+ // are mutable but requestable. Factories are responsible for not getting
+ // in an infinite loop about these.
private static final long NON_REQUESTABLE_CAPABILITIES =
- MUTABLE_CAPABILITIES & ~(1 << NET_CAPABILITY_TRUSTED);
+ MUTABLE_CAPABILITIES
+ & ~(1 << NET_CAPABILITY_TRUSTED)
+ & ~(1 << NET_CAPABILITY_NOT_VCN_MANAGED);
/**
* Capabilities that are set by default when the object is constructed.
*/
private static final long DEFAULT_CAPABILITIES =
- (1 << NET_CAPABILITY_NOT_RESTRICTED) |
- (1 << NET_CAPABILITY_TRUSTED) |
- (1 << NET_CAPABILITY_NOT_VPN);
+ (1 << NET_CAPABILITY_NOT_RESTRICTED)
+ | (1 << NET_CAPABILITY_TRUSTED)
+ | (1 << NET_CAPABILITY_NOT_VPN);
/**
* Capabilities that suggest that a network is restricted.
@@ -495,7 +517,8 @@
| (1 << NET_CAPABILITY_NOT_VPN)
| (1 << NET_CAPABILITY_NOT_ROAMING)
| (1 << NET_CAPABILITY_NOT_CONGESTED)
- | (1 << NET_CAPABILITY_NOT_SUSPENDED);
+ | (1 << NET_CAPABILITY_NOT_SUSPENDED)
+ | (1 << NET_CAPABILITY_NOT_VCN_MANAGED);
/**
* Adds the given capability to this {@code NetworkCapability} instance.
@@ -751,7 +774,7 @@
if (originalOwnerUid == creatorUid) {
setOwnerUid(creatorUid);
}
- if (ArrayUtils.contains(originalAdministratorUids, creatorUid)) {
+ if (CollectionUtils.contains(originalAdministratorUids, creatorUid)) {
setAdministratorUids(new int[] {creatorUid});
}
// There is no need to clear the UIDs, they have already been cleared by clearAll() above.
@@ -1763,6 +1786,15 @@
return 0;
}
+ private <T extends Parcelable> void writeParcelableArraySet(Parcel in,
+ @Nullable ArraySet<T> val, int flags) {
+ final int size = (val != null) ? val.size() : -1;
+ in.writeInt(size);
+ for (int i = 0; i < size; i++) {
+ in.writeParcelable(val.valueAt(i), flags);
+ }
+ }
+
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(mNetworkCapabilities);
@@ -1773,7 +1805,7 @@
dest.writeParcelable((Parcelable) mNetworkSpecifier, flags);
dest.writeParcelable((Parcelable) mTransportInfo, flags);
dest.writeInt(mSignalStrength);
- dest.writeArraySet(mUids);
+ writeParcelableArraySet(dest, mUids, flags);
dest.writeString(mSSID);
dest.writeBoolean(mPrivateDnsBroken);
dest.writeIntArray(getAdministratorUids());
@@ -1796,8 +1828,7 @@
netCap.mNetworkSpecifier = in.readParcelable(null);
netCap.mTransportInfo = in.readParcelable(null);
netCap.mSignalStrength = in.readInt();
- netCap.mUids = (ArraySet<UidRange>) in.readArraySet(
- null /* ClassLoader, null for default */);
+ netCap.mUids = readParcelableArraySet(in, null /* ClassLoader, null for default */);
netCap.mSSID = in.readString();
netCap.mPrivateDnsBroken = in.readBoolean();
netCap.setAdministratorUids(in.createIntArray());
@@ -1810,6 +1841,20 @@
public NetworkCapabilities[] newArray(int size) {
return new NetworkCapabilities[size];
}
+
+ private @Nullable <T extends Parcelable> ArraySet<T> readParcelableArraySet(Parcel in,
+ @Nullable ClassLoader loader) {
+ final int size = in.readInt();
+ if (size < 0) {
+ return null;
+ }
+ final ArraySet<T> result = new ArraySet<>(size);
+ for (int i = 0; i < size; i++) {
+ final T value = in.readParcelable(loader);
+ result.append(value);
+ }
+ return result;
+ }
};
@Override
@@ -1857,7 +1902,7 @@
sb.append(" OwnerUid: ").append(mOwnerUid);
}
- if (!ArrayUtils.isEmpty(mAdministratorUids)) {
+ if (mAdministratorUids != null && mAdministratorUids.length != 0) {
sb.append(" AdminUids: ").append(Arrays.toString(mAdministratorUids));
}
@@ -1982,6 +2027,7 @@
case NET_CAPABILITY_TEMPORARILY_NOT_METERED: return "TEMPORARILY_NOT_METERED";
case NET_CAPABILITY_OEM_PRIVATE: return "OEM_PRIVATE";
case NET_CAPABILITY_VEHICLE_INTERNAL: return "NET_CAPABILITY_VEHICLE_INTERNAL";
+ case NET_CAPABILITY_NOT_VCN_MANAGED: return "NOT_VCN_MANAGED";
default: return Integer.toString(capability);
}
}
@@ -2489,7 +2535,7 @@
@NonNull
public NetworkCapabilities build() {
if (mCaps.getOwnerUid() != Process.INVALID_UID) {
- if (!ArrayUtils.contains(mCaps.getAdministratorUids(), mCaps.getOwnerUid())) {
+ if (!CollectionUtils.contains(mCaps.getAdministratorUids(), mCaps.getOwnerUid())) {
throw new IllegalStateException("The owner UID must be included in "
+ " administrator UIDs.");
}
diff --git a/core/java/android/net/NetworkConfig.java b/packages/Connectivity/framework/src/android/net/NetworkConfig.java
similarity index 100%
rename from core/java/android/net/NetworkConfig.java
rename to packages/Connectivity/framework/src/android/net/NetworkConfig.java
diff --git a/core/java/android/net/NetworkInfo.aidl b/packages/Connectivity/framework/src/android/net/NetworkInfo.aidl
similarity index 100%
rename from core/java/android/net/NetworkInfo.aidl
rename to packages/Connectivity/framework/src/android/net/NetworkInfo.aidl
diff --git a/core/java/android/net/NetworkInfo.java b/packages/Connectivity/framework/src/android/net/NetworkInfo.java
similarity index 100%
rename from core/java/android/net/NetworkInfo.java
rename to packages/Connectivity/framework/src/android/net/NetworkInfo.java
diff --git a/core/java/android/net/NetworkProvider.java b/packages/Connectivity/framework/src/android/net/NetworkProvider.java
similarity index 100%
rename from core/java/android/net/NetworkProvider.java
rename to packages/Connectivity/framework/src/android/net/NetworkProvider.java
diff --git a/core/java/android/net/NetworkRequest.aidl b/packages/Connectivity/framework/src/android/net/NetworkRequest.aidl
similarity index 100%
rename from core/java/android/net/NetworkRequest.aidl
rename to packages/Connectivity/framework/src/android/net/NetworkRequest.aidl
diff --git a/core/java/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
similarity index 96%
rename from core/java/android/net/NetworkRequest.java
rename to packages/Connectivity/framework/src/android/net/NetworkRequest.java
index f0c637c..b9ef4c2 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
@@ -353,7 +353,9 @@
* NetworkSpecifier.
*/
public Builder setNetworkSpecifier(NetworkSpecifier networkSpecifier) {
- MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(networkSpecifier);
+ if (networkSpecifier instanceof MatchAllNetworkSpecifier) {
+ throw new IllegalArgumentException("A MatchAllNetworkSpecifier is not permitted");
+ }
mNetworkCapabilities.setNetworkSpecifier(networkSpecifier);
return this;
}
@@ -433,25 +435,7 @@
* @hide
*/
public boolean isRequest() {
- return isForegroundRequest() || isBackgroundRequest();
- }
-
- /**
- * Returns true iff. the contained NetworkRequest is one that:
- *
- * - should be associated with at most one satisfying network
- * at a time;
- *
- * - should cause a network to be kept up and in the foreground if
- * it is the best network which can satisfy the NetworkRequest.
- *
- * For full detail of how isRequest() is used for pairing Networks with
- * NetworkRequests read rematchNetworkAndRequests().
- *
- * @hide
- */
- public boolean isForegroundRequest() {
- return type == Type.TRACK_DEFAULT || type == Type.REQUEST;
+ return type == Type.REQUEST || type == Type.BACKGROUND_REQUEST;
}
/**
@@ -565,7 +549,7 @@
proto.end(token);
}
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (obj instanceof NetworkRequest == false) return false;
NetworkRequest that = (NetworkRequest)obj;
return (that.legacyType == this.legacyType &&
diff --git a/core/java/android/net/NetworkUtils.java b/packages/Connectivity/framework/src/android/net/NetworkUtils.java
similarity index 97%
rename from core/java/android/net/NetworkUtils.java
rename to packages/Connectivity/framework/src/android/net/NetworkUtils.java
index b5962c5..8be4af7 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkUtils.java
@@ -81,11 +81,11 @@
public native static boolean bindProcessToNetworkForHostResolution(int netId);
/**
- * Explicitly binds {@code socketfd} to the network designated by {@code netId}. This
+ * Explicitly binds {@code fd} to the network designated by {@code netId}. This
* overrides any binding via {@link #bindProcessToNetwork}.
* @return 0 on success or negative errno on failure.
*/
- public native static int bindSocketToNetwork(int socketfd, int netId);
+ public static native int bindSocketToNetwork(FileDescriptor fd, int netId);
/**
* Protect {@code fd} from VPN connections. After protecting, data sent through
@@ -93,9 +93,7 @@
* forwarded through the VPN.
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static boolean protectFromVpn(FileDescriptor fd) {
- return protectFromVpn(fd.getInt$());
- }
+ public static native boolean protectFromVpn(FileDescriptor fd);
/**
* Protect {@code socketfd} from VPN connections. After protecting, data sent through
diff --git a/core/java/android/net/PacProxySelector.java b/packages/Connectivity/framework/src/android/net/PacProxySelector.java
similarity index 100%
rename from core/java/android/net/PacProxySelector.java
rename to packages/Connectivity/framework/src/android/net/PacProxySelector.java
diff --git a/core/java/android/net/Proxy.java b/packages/Connectivity/framework/src/android/net/Proxy.java
similarity index 79%
rename from core/java/android/net/Proxy.java
rename to packages/Connectivity/framework/src/android/net/Proxy.java
index 03b07e0..9cd7ab2 100644
--- a/core/java/android/net/Proxy.java
+++ b/packages/Connectivity/framework/src/android/net/Proxy.java
@@ -30,8 +30,6 @@
import java.net.ProxySelector;
import java.net.URI;
import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
/**
* A convenience class for accessing the user and default proxy
@@ -64,40 +62,9 @@
@Deprecated
public static final String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO";
- /** @hide */
- public static final int PROXY_VALID = 0;
- /** @hide */
- public static final int PROXY_HOSTNAME_EMPTY = 1;
- /** @hide */
- public static final int PROXY_HOSTNAME_INVALID = 2;
- /** @hide */
- public static final int PROXY_PORT_EMPTY = 3;
- /** @hide */
- public static final int PROXY_PORT_INVALID = 4;
- /** @hide */
- public static final int PROXY_EXCLLIST_INVALID = 5;
-
private static ConnectivityManager sConnectivityManager = null;
- // Hostname / IP REGEX validation
- // Matches blank input, ips, and domain names
- private static final String NAME_IP_REGEX =
- "[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*(\\.[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*)*";
-
- private static final String HOSTNAME_REGEXP = "^$|^" + NAME_IP_REGEX + "$";
-
- private static final Pattern HOSTNAME_PATTERN;
-
- private static final String EXCL_REGEX =
- "[a-zA-Z0-9*]+(\\-[a-zA-Z0-9*]+)*(\\.[a-zA-Z0-9*]+(\\-[a-zA-Z0-9*]+)*)*";
-
- private static final String EXCLLIST_REGEXP = "^$|^" + EXCL_REGEX + "(," + EXCL_REGEX + ")*$";
-
- private static final Pattern EXCLLIST_PATTERN;
-
static {
- HOSTNAME_PATTERN = Pattern.compile(HOSTNAME_REGEXP);
- EXCLLIST_PATTERN = Pattern.compile(EXCLLIST_REGEXP);
sDefaultProxySelector = ProxySelector.getDefault();
}
@@ -216,33 +183,6 @@
return false;
}
- /**
- * Validate syntax of hostname, port and exclusion list entries
- * {@hide}
- */
- public static int validate(String hostname, String port, String exclList) {
- Matcher match = HOSTNAME_PATTERN.matcher(hostname);
- Matcher listMatch = EXCLLIST_PATTERN.matcher(exclList);
-
- if (!match.matches()) return PROXY_HOSTNAME_INVALID;
-
- if (!listMatch.matches()) return PROXY_EXCLLIST_INVALID;
-
- if (hostname.length() > 0 && port.length() == 0) return PROXY_PORT_EMPTY;
-
- if (port.length() > 0) {
- if (hostname.length() == 0) return PROXY_HOSTNAME_EMPTY;
- int portVal = -1;
- try {
- portVal = Integer.parseInt(port);
- } catch (NumberFormatException ex) {
- return PROXY_PORT_INVALID;
- }
- if (portVal <= 0 || portVal > 0xFFFF) return PROXY_PORT_INVALID;
- }
- return PROXY_VALID;
- }
-
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static final void setHttpProxySystemProperty(ProxyInfo p) {
diff --git a/core/java/android/net/ProxyInfo.aidl b/packages/Connectivity/framework/src/android/net/ProxyInfo.aidl
similarity index 100%
rename from core/java/android/net/ProxyInfo.aidl
rename to packages/Connectivity/framework/src/android/net/ProxyInfo.aidl
diff --git a/core/java/android/net/ProxyInfo.java b/packages/Connectivity/framework/src/android/net/ProxyInfo.java
similarity index 97%
rename from core/java/android/net/ProxyInfo.java
rename to packages/Connectivity/framework/src/android/net/ProxyInfo.java
index a202d77..229db0d 100644
--- a/core/java/android/net/ProxyInfo.java
+++ b/packages/Connectivity/framework/src/android/net/ProxyInfo.java
@@ -23,6 +23,8 @@
import android.os.Parcelable;
import android.text.TextUtils;
+import com.android.net.module.util.ProxyUtils;
+
import java.net.InetSocketAddress;
import java.net.URLConnection;
import java.util.List;
@@ -233,7 +235,7 @@
*/
public boolean isValid() {
if (!Uri.EMPTY.equals(mPacFileUrl)) return true;
- return Proxy.PROXY_VALID == Proxy.validate(mHost == null ? "" : mHost,
+ return ProxyUtils.PROXY_VALID == ProxyUtils.validate(mHost == null ? "" : mHost,
mPort == 0 ? "" : Integer.toString(mPort),
mExclusionList == null ? "" : mExclusionList);
}
@@ -275,7 +277,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (!(o instanceof ProxyInfo)) return false;
ProxyInfo p = (ProxyInfo)o;
// If PAC URL is present in either then they must be equal.
@@ -355,7 +357,7 @@
port = in.readInt();
}
String exclList = in.readString();
- String[] parsedExclList = in.readStringArray();
+ String[] parsedExclList = in.createStringArray();
ProxyInfo proxyProperties = new ProxyInfo(host, port, exclList, parsedExclList);
return proxyProperties;
}
diff --git a/core/java/android/net/RouteInfo.aidl b/packages/Connectivity/framework/src/android/net/RouteInfo.aidl
similarity index 100%
rename from core/java/android/net/RouteInfo.aidl
rename to packages/Connectivity/framework/src/android/net/RouteInfo.aidl
diff --git a/core/java/android/net/RouteInfo.java b/packages/Connectivity/framework/src/android/net/RouteInfo.java
similarity index 99%
rename from core/java/android/net/RouteInfo.java
rename to packages/Connectivity/framework/src/android/net/RouteInfo.java
index 94f849f..5b6684a 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/packages/Connectivity/framework/src/android/net/RouteInfo.java
@@ -534,7 +534,7 @@
* Compares this RouteInfo object against the specified object and indicates if they are equal.
* @return {@code true} if the objects are equal, {@code false} otherwise.
*/
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) return true;
if (!(obj instanceof RouteInfo)) return false;
@@ -570,7 +570,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (!(o instanceof RouteKey)) {
return false;
}
diff --git a/core/java/android/net/SocketKeepalive.java b/packages/Connectivity/framework/src/android/net/SocketKeepalive.java
similarity index 100%
rename from core/java/android/net/SocketKeepalive.java
rename to packages/Connectivity/framework/src/android/net/SocketKeepalive.java
diff --git a/core/java/android/net/StaticIpConfiguration.aidl b/packages/Connectivity/framework/src/android/net/StaticIpConfiguration.aidl
similarity index 100%
rename from core/java/android/net/StaticIpConfiguration.aidl
rename to packages/Connectivity/framework/src/android/net/StaticIpConfiguration.aidl
diff --git a/core/java/android/net/StaticIpConfiguration.java b/packages/Connectivity/framework/src/android/net/StaticIpConfiguration.java
similarity index 100%
rename from core/java/android/net/StaticIpConfiguration.java
rename to packages/Connectivity/framework/src/android/net/StaticIpConfiguration.java
diff --git a/core/java/android/net/TcpKeepalivePacketData.java b/packages/Connectivity/framework/src/android/net/TcpKeepalivePacketData.java
similarity index 100%
rename from core/java/android/net/TcpKeepalivePacketData.java
rename to packages/Connectivity/framework/src/android/net/TcpKeepalivePacketData.java
diff --git a/core/java/android/net/TcpRepairWindow.java b/packages/Connectivity/framework/src/android/net/TcpRepairWindow.java
similarity index 100%
rename from core/java/android/net/TcpRepairWindow.java
rename to packages/Connectivity/framework/src/android/net/TcpRepairWindow.java
diff --git a/core/java/android/net/TcpSocketKeepalive.java b/packages/Connectivity/framework/src/android/net/TcpSocketKeepalive.java
similarity index 100%
rename from core/java/android/net/TcpSocketKeepalive.java
rename to packages/Connectivity/framework/src/android/net/TcpSocketKeepalive.java
diff --git a/core/java/android/net/TestNetworkInterface.aidl b/packages/Connectivity/framework/src/android/net/TestNetworkInterface.aidl
similarity index 100%
rename from core/java/android/net/TestNetworkInterface.aidl
rename to packages/Connectivity/framework/src/android/net/TestNetworkInterface.aidl
diff --git a/core/java/android/net/TestNetworkInterface.java b/packages/Connectivity/framework/src/android/net/TestNetworkInterface.java
similarity index 100%
rename from core/java/android/net/TestNetworkInterface.java
rename to packages/Connectivity/framework/src/android/net/TestNetworkInterface.java
diff --git a/core/java/android/net/TestNetworkManager.java b/packages/Connectivity/framework/src/android/net/TestNetworkManager.java
similarity index 100%
rename from core/java/android/net/TestNetworkManager.java
rename to packages/Connectivity/framework/src/android/net/TestNetworkManager.java
diff --git a/core/java/android/net/TransportInfo.java b/packages/Connectivity/framework/src/android/net/TransportInfo.java
similarity index 100%
rename from core/java/android/net/TransportInfo.java
rename to packages/Connectivity/framework/src/android/net/TransportInfo.java
diff --git a/core/java/android/net/UidRange.aidl b/packages/Connectivity/framework/src/android/net/UidRange.aidl
similarity index 100%
rename from core/java/android/net/UidRange.aidl
rename to packages/Connectivity/framework/src/android/net/UidRange.aidl
diff --git a/core/java/android/net/VpnManager.java b/packages/Connectivity/framework/src/android/net/VpnManager.java
similarity index 63%
rename from core/java/android/net/VpnManager.java
rename to packages/Connectivity/framework/src/android/net/VpnManager.java
index c87b827..1812509 100644
--- a/core/java/android/net/VpnManager.java
+++ b/packages/Connectivity/framework/src/android/net/VpnManager.java
@@ -21,6 +21,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
@@ -28,6 +29,8 @@
import android.content.res.Resources;
import android.os.RemoteException;
+import com.android.internal.net.LegacyVpnInfo;
+import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
import java.io.IOException;
@@ -161,4 +164,104 @@
throw e.rethrowFromSystemServer();
}
}
-}
+
+ /**
+ * Return the VPN configuration for the given user ID.
+ * @hide
+ */
+ @Nullable
+ public VpnConfig getVpnConfig(@UserIdInt int userId) {
+ try {
+ return mService.getVpnConfig(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Prepare for a VPN application.
+ * VPN permissions are checked in the {@link Vpn} class. If the caller is not {@code userId},
+ * {@link android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
+ *
+ * @param oldPackage Package name of the application which currently controls VPN, which will
+ * be replaced. If there is no such application, this should should either be
+ * {@code null} or {@link VpnConfig.LEGACY_VPN}.
+ * @param newPackage Package name of the application which should gain control of VPN, or
+ * {@code null} to disable.
+ * @param userId User for whom to prepare the new VPN.
+ *
+ * @hide
+ */
+ public boolean prepareVpn(@Nullable String oldPackage, @Nullable String newPackage,
+ int userId) {
+ try {
+ return mService.prepareVpn(oldPackage, newPackage, userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set whether the VPN package has the ability to launch VPNs without user intervention. This
+ * method is used by system-privileged apps. VPN permissions are checked in the {@link Vpn}
+ * class. If the caller is not {@code userId}, {@link
+ * android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
+ *
+ * @param packageName The package for which authorization state should change.
+ * @param userId User for whom {@code packageName} is installed.
+ * @param vpnType The {@link VpnManager.VpnType} constant representing what class of VPN
+ * permissions should be granted. When unauthorizing an app, {@link
+ * VpnManager.TYPE_VPN_NONE} should be used.
+ * @hide
+ */
+ public void setVpnPackageAuthorization(
+ String packageName, int userId, @VpnManager.VpnType int vpnType) {
+ try {
+ mService.setVpnPackageAuthorization(packageName, userId, vpnType);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Return the legacy VPN information for the specified user ID.
+ * @hide
+ */
+ public LegacyVpnInfo getLegacyVpnInfo(@UserIdInt int userId) {
+ try {
+ return mService.getLegacyVpnInfo(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Starts a legacy VPN.
+ * @hide
+ */
+ public void startLegacyVpn(VpnProfile profile) {
+ try {
+ mService.startLegacyVpn(profile);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Informs the service that legacy lockdown VPN state should be updated (e.g., if its keystore
+ * entry has been updated). If the LockdownVpn mechanism is enabled, updates the vpn
+ * with a reload of its profile.
+ *
+ * <p>This method can only be called by the system UID
+ * @return a boolean indicating success
+ *
+ * @hide
+ */
+ public boolean updateLockdownVpn() {
+ try {
+ return mService.updateLockdownVpn();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/net/VpnService.java b/packages/Connectivity/framework/src/android/net/VpnService.java
similarity index 100%
rename from core/java/android/net/VpnService.java
rename to packages/Connectivity/framework/src/android/net/VpnService.java
diff --git a/core/java/android/net/apf/ApfCapabilities.aidl b/packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.aidl
similarity index 100%
rename from core/java/android/net/apf/ApfCapabilities.aidl
rename to packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.aidl
diff --git a/core/java/android/net/apf/ApfCapabilities.java b/packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.java
similarity index 100%
rename from core/java/android/net/apf/ApfCapabilities.java
rename to packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.java
diff --git a/core/java/android/net/util/DnsUtils.java b/packages/Connectivity/framework/src/android/net/util/DnsUtils.java
similarity index 100%
rename from core/java/android/net/util/DnsUtils.java
rename to packages/Connectivity/framework/src/android/net/util/DnsUtils.java
diff --git a/core/java/android/net/util/KeepaliveUtils.java b/packages/Connectivity/framework/src/android/net/util/KeepaliveUtils.java
similarity index 100%
rename from core/java/android/net/util/KeepaliveUtils.java
rename to packages/Connectivity/framework/src/android/net/util/KeepaliveUtils.java
diff --git a/core/java/android/net/util/MultinetworkPolicyTracker.java b/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java
similarity index 100%
rename from core/java/android/net/util/MultinetworkPolicyTracker.java
rename to packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java
diff --git a/core/java/android/net/util/SocketUtils.java b/packages/Connectivity/framework/src/android/net/util/SocketUtils.java
similarity index 100%
rename from core/java/android/net/util/SocketUtils.java
rename to packages/Connectivity/framework/src/android/net/util/SocketUtils.java
diff --git a/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgent.aidl b/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgent.aidl
new file mode 100644
index 0000000..64b5567
--- /dev/null
+++ b/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgent.aidl
@@ -0,0 +1,49 @@
+/**
+ * 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 perNmissions and
+ * limitations under the License.
+ */
+package com.android.connectivity.aidl;
+
+import android.net.NattKeepalivePacketData;
+import android.net.QosFilterParcelable;
+import android.net.TcpKeepalivePacketData;
+
+import com.android.connectivity.aidl.INetworkAgentRegistry;
+
+/**
+ * Interface to notify NetworkAgent of connectivity events.
+ * @hide
+ */
+oneway interface INetworkAgent {
+ void onRegistered(in INetworkAgentRegistry registry);
+ void onDisconnected();
+ void onBandwidthUpdateRequested();
+ void onValidationStatusChanged(int validationStatus,
+ in @nullable String captivePortalUrl);
+ void onSaveAcceptUnvalidated(boolean acceptUnvalidated);
+ void onStartNattSocketKeepalive(int slot, int intervalDurationMs,
+ in NattKeepalivePacketData packetData);
+ void onStartTcpSocketKeepalive(int slot, int intervalDurationMs,
+ in TcpKeepalivePacketData packetData);
+ void onStopSocketKeepalive(int slot);
+ void onSignalStrengthThresholdsUpdated(in int[] thresholds);
+ void onPreventAutomaticReconnect();
+ void onAddNattKeepalivePacketFilter(int slot,
+ in NattKeepalivePacketData packetData);
+ void onAddTcpKeepalivePacketFilter(int slot,
+ in TcpKeepalivePacketData packetData);
+ void onRemoveKeepalivePacketFilter(int slot);
+ void onQosFilterCallbackRegistered(int qosCallbackId, in QosFilterParcelable filterParcel);
+ void onQosCallbackUnregistered(int qosCallbackId);
+}
diff --git a/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl b/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl
new file mode 100644
index 0000000..f0193db
--- /dev/null
+++ b/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl
@@ -0,0 +1,41 @@
+/**
+ * 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 perNmissions and
+ * limitations under the License.
+ */
+package com.android.connectivity.aidl;
+
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.net.QosSession;
+import android.telephony.data.EpsBearerQosSessionAttributes;
+
+/**
+ * Interface for NetworkAgents to send network network properties.
+ * @hide
+ */
+oneway interface INetworkAgentRegistry {
+ void sendNetworkCapabilities(in NetworkCapabilities nc);
+ void sendLinkProperties(in LinkProperties lp);
+ // TODO: consider replacing this by "markConnected()" and removing
+ void sendNetworkInfo(in NetworkInfo info);
+ void sendScore(int score);
+ void sendExplicitlySelected(boolean explicitlySelected, boolean acceptPartial);
+ void sendSocketKeepaliveEvent(int slot, int reason);
+ void sendUnderlyingNetworks(in @nullable List<Network> networks);
+ void sendEpsQosSessionAvailable(int callbackId, in QosSession session, in EpsBearerQosSessionAttributes attributes);
+ void sendQosSessionLost(int qosCallbackId, in QosSession session);
+ void sendQosCallbackError(int qosCallbackId, int exceptionType);
+}
diff --git a/packages/Connectivity/service/Android.bp b/packages/Connectivity/service/Android.bp
index c8f3bd3..8fc3181 100644
--- a/packages/Connectivity/service/Android.bp
+++ b/packages/Connectivity/service/Android.bp
@@ -57,6 +57,7 @@
static_libs: [
"net-utils-device-common",
"net-utils-framework-common",
+ "netd-client",
],
apex_available: [
"//apex_available:platform",
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index c5d4fa9..cb610fc 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -89,15 +89,7 @@
return value == null || value.length() < MAX_LENGTH;
}
});
- VALIDATORS.put(
- System.FONT_SCALE,
- value -> {
- try {
- return Float.parseFloat(value) >= 0;
- } catch (NumberFormatException | NullPointerException e) {
- return false;
- }
- });
+ VALIDATORS.put(System.FONT_SCALE, new InclusiveFloatRangeValidator(0.85f, 1.3f));
VALIDATORS.put(System.DIM_SCREEN, BOOLEAN_VALIDATOR);
VALIDATORS.put(
System.DISPLAY_COLOR_MODE,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 266bfe0..6568bff 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -846,8 +846,8 @@
try {
stmt = db.compileStatement("INSERT INTO system(name,value)"
+ " VALUES(?,?);");
- loadBooleanSetting(stmt, Settings.System.USER_ROTATION,
- R.integer.def_user_rotation); // should be zero degrees
+ loadIntegerSetting(stmt, Settings.System.USER_ROTATION,
+ R.integer.def_user_rotation);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
@@ -2265,6 +2265,8 @@
loadBooleanSetting(stmt, Settings.System.ACCELEROMETER_ROTATION,
R.bool.def_accelerometer_rotation);
+ loadIntegerSetting(stmt, Settings.System.USER_ROTATION, R.integer.def_user_rotation);
+
loadDefaultHapticSettings(stmt);
loadBooleanSetting(stmt, Settings.System.NOTIFICATION_LIGHT_PULSE,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 4b6862c..39be9cb 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -121,6 +121,8 @@
<uses-permission android:name="android.permission.CREATE_USERS" />
<uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
<uses-permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" />
+ <uses-permission android:name="android.permission.QUERY_USERS" />
+ <uses-permission android:name="android.permission.MODIFY_QUIET_MODE" />
<uses-permission android:name="android.permission.ACCESS_LOWPAN_STATE"/>
<uses-permission android:name="android.permission.CHANGE_LOWPAN_STATE"/>
<uses-permission android:name="android.permission.READ_LOWPAN_CREDENTIAL"/>
@@ -292,8 +294,8 @@
<!-- Permission needed to test mainline permission module rollback -->
<uses-permission android:name="android.permission.UPGRADE_RUNTIME_PERMISSIONS" />
- <!-- Permission needed to read wifi network credentials for CtsNetTestCases -->
- <uses-permission android:name="android.permission.NETWORK_AIRPLANE_MODE" />
+ <!-- Permission needed to restart WiFi Subsystem -->
+ <uses-permission android:name="android.permission.RESTART_WIFI_SUBSYSTEM" />
<!-- Permission needed to read wifi network credentials for CtsNetTestCases -->
<uses-permission android:name="android.permission.READ_WIFI_CREDENTIAL" />
@@ -349,6 +351,13 @@
<!-- Permission required for CTS test - CtsSensorPrivacyTestCases -->
<uses-permission android:name="android.permission.MANAGE_SENSOR_PRIVACY" />
+ <!-- Permission required for GTS test - GtsAssistIntentTestCases -->
+ <uses-permission android:name="android.permission.MANAGE_SOUND_TRIGGER" />
+ <uses-permission android:name="android.permission.CAPTURE_AUDIO_HOTWORD" />
+ <uses-permission android:name="android.permission.BIND_VOICE_INTERACTION" />
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+ <uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java
index 4d95ef1..6dcad25 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java
@@ -20,14 +20,11 @@
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
-import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
-import android.net.IConnectivityManager;
+import android.net.ConnectivityManager;
import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.SpannableStringBuilder;
@@ -45,7 +42,7 @@
private static final String TAG = "VpnDisconnected";
- private IConnectivityManager mService;
+ private ConnectivityManager mService;
private int mUserId;
private String mVpnPackage;
@@ -53,10 +50,9 @@
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mService = IConnectivityManager.Stub.asInterface(
- ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
mUserId = UserHandle.myUserId();
- mVpnPackage = getAlwaysOnVpnPackage();
+ final ConnectivityManager cm = getSystemService(ConnectivityManager.class);
+ mVpnPackage = cm.getAlwaysOnVpnPackageForUser(mUserId);
if (mVpnPackage == null) {
finish();
return;
@@ -102,15 +98,6 @@
}
}
- private String getAlwaysOnVpnPackage() {
- try {
- return mService.getAlwaysOnVpnPackage(mUserId);
- } catch (RemoteException e) {
- Log.e(TAG, "Can't getAlwaysOnVpnPackage()", e);
- return null;
- }
- }
-
private CharSequence getVpnLabel() {
try {
return VpnConfig.getVpnLabel(this, mVpnPackage);
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
index e66f2cc..aab01d0 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
@@ -18,15 +18,12 @@
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
-import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
-import android.net.IConnectivityManager;
+import android.net.ConnectivityManager;
import android.net.VpnManager;
import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.Html;
@@ -48,7 +45,8 @@
private String mPackage;
- private IConnectivityManager mService;
+ private ConnectivityManager mCm; // TODO: switch entirely to VpnManager once VPN code moves
+ private VpnManager mVm;
public ConfirmDialog() {
this(VpnManager.TYPE_VPN_SERVICE);
@@ -62,10 +60,10 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPackage = getCallingPackage();
- mService = IConnectivityManager.Stub.asInterface(
- ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
+ mCm = getSystemService(ConnectivityManager.class);
+ mVm = getSystemService(VpnManager.class);
- if (prepareVpn()) {
+ if (mVm.prepareVpn(mPackage, null, UserHandle.myUserId())) {
setResult(RESULT_OK);
finish();
return;
@@ -74,7 +72,7 @@
finish();
return;
}
- final String alwaysOnVpnPackage = getAlwaysOnVpnPackage();
+ final String alwaysOnVpnPackage = mCm.getAlwaysOnVpnPackageForUser(UserHandle.myUserId());
// Can't prepare new vpn app when another vpn is always-on
if (alwaysOnVpnPackage != null && !alwaysOnVpnPackage.equals(mPackage)) {
finish();
@@ -97,24 +95,6 @@
button.setFilterTouchesWhenObscured(true);
}
- private String getAlwaysOnVpnPackage() {
- try {
- return mService.getAlwaysOnVpnPackage(UserHandle.myUserId());
- } catch (RemoteException e) {
- Log.e(TAG, "fail to call getAlwaysOnVpnPackage", e);
- // Fallback to null to show the dialog
- return null;
- }
- }
-
- private boolean prepareVpn() {
- try {
- return mService.prepareVpn(mPackage, null, UserHandle.myUserId());
- } catch (RemoteException e) {
- throw new IllegalStateException(e);
- }
- }
-
private CharSequence getVpnLabel() {
try {
return VpnConfig.getVpnLabel(this, mPackage);
@@ -146,10 +126,10 @@
@Override
public void onClick(DialogInterface dialog, int which) {
try {
- if (mService.prepareVpn(null, mPackage, UserHandle.myUserId())) {
+ if (mVm.prepareVpn(null, mPackage, UserHandle.myUserId())) {
// Authorize this app to initiate VPN connections in the future without user
// intervention.
- mService.setVpnPackageAuthorization(mPackage, UserHandle.myUserId(), mVpnType);
+ mVm.setVpnPackageAuthorization(mPackage, UserHandle.myUserId(), mVpnType);
setResult(RESULT_OK);
}
} catch (Exception e) {
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
index 01dca7e..1fc74f7 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
@@ -16,13 +16,11 @@
package com.android.vpndialogs;
-import android.content.Context;
import android.content.DialogInterface;
-import android.net.IConnectivityManager;
+import android.net.VpnManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
-import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Log;
@@ -41,7 +39,7 @@
private VpnConfig mConfig;
- private IConnectivityManager mService;
+ private VpnManager mVm;
private TextView mDuration;
private TextView mDataTransmitted;
@@ -55,11 +53,9 @@
super.onCreate(savedInstanceState);
try {
+ mVm = getSystemService(VpnManager.class);
- mService = IConnectivityManager.Stub.asInterface(
- ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
-
- mConfig = mService.getVpnConfig(UserHandle.myUserId());
+ mConfig = mVm.getVpnConfig(UserHandle.myUserId());
// mConfig can be null if we are a restricted user, in that case don't show this dialog
if (mConfig == null) {
@@ -118,9 +114,9 @@
} else if (which == DialogInterface.BUTTON_NEUTRAL) {
final int myUserId = UserHandle.myUserId();
if (mConfig.legacy) {
- mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, myUserId);
+ mVm.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, myUserId);
} else {
- mService.prepareVpn(mConfig.user, VpnConfig.LEGACY_VPN, myUserId);
+ mVm.prepareVpn(mConfig.user, VpnConfig.LEGACY_VPN, myUserId);
}
}
} catch (Exception e) {
diff --git a/services/Android.bp b/services/Android.bp
index 785ca35..a13dbe6 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -139,7 +139,7 @@
last_released: {
api_file: ":android.api.system-server.latest",
removed_api_file: ":removed.api.system-server.latest",
- baseline_file: ":system-server-api-incompatibilities-with-last-released"
+ baseline_file: ":android-incompatibilities.api.system-server.latest"
},
api_lint: {
enabled: true,
diff --git a/services/backup/OWNERS b/services/backup/OWNERS
index 3c5268c..ba2a63a 100644
--- a/services/backup/OWNERS
+++ b/services/backup/OWNERS
@@ -3,6 +3,7 @@
aabhinav@google.com
bryanmawhinney@google.com
jstemmer@google.com
+millmore@google.com
nathch@google.com
niagra@google.com
niamhfw@google.com
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 5ad5805..4bebe39 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -84,6 +84,7 @@
":storaged_aidl",
":vold_aidl",
":platform-compat-config",
+ ":platform-compat-overrides",
":display-device-config",
"java/com/android/server/EventLogTags.logtags",
"java/com/android/server/am/EventLogTags.logtags",
diff --git a/services/core/java/android/content/pm/OWNERS b/services/core/java/android/content/pm/OWNERS
new file mode 100644
index 0000000..5eed0b5
--- /dev/null
+++ b/services/core/java/android/content/pm/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/content/pm/OWNERS
\ No newline at end of file
diff --git a/services/core/java/android/os/OWNERS b/services/core/java/android/os/OWNERS
new file mode 100644
index 0000000..d0a2daf
--- /dev/null
+++ b/services/core/java/android/os/OWNERS
@@ -0,0 +1 @@
+per-file BatteryStats* = file:/BATTERY_STATS_OWNERS
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 9aadcf8..3933e37 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -120,6 +120,7 @@
import android.net.NetworkTestResultParcelable;
import android.net.NetworkUtils;
import android.net.NetworkWatchlistManager;
+import android.net.OemNetworkPreferences;
import android.net.PrivateDnsConfigParcel;
import android.net.ProxyInfo;
import android.net.QosCallbackException;
@@ -132,6 +133,7 @@
import android.net.TetheringManager;
import android.net.UidRange;
import android.net.UidRangeParcel;
+import android.net.UnderlyingNetworkInfo;
import android.net.Uri;
import android.net.VpnManager;
import android.net.VpnService;
@@ -184,7 +186,6 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
-import com.android.internal.net.VpnInfo;
import com.android.internal.net.VpnProfile;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.AsyncChannel;
@@ -216,14 +217,13 @@
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.utils.PriorityDump;
-import com.google.android.collect.Lists;
-
import libcore.io.IoUtils;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.net.Inet4Address;
import java.net.InetAddress;
+import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
@@ -282,15 +282,18 @@
// connect anyway?" dialog after the user selects a network that doesn't validate.
private static final int PROMPT_UNVALIDATED_DELAY_MS = 8 * 1000;
- // Default to 30s linger time-out. Modifiable only for testing.
+ // Default to 30s linger time-out, and 5s for nascent network. Modifiable only for testing.
private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger";
private static final int DEFAULT_LINGER_DELAY_MS = 30_000;
+ private static final int DEFAULT_NASCENT_DELAY_MS = 5_000;
// The maximum number of network request allowed per uid before an exception is thrown.
private static final int MAX_NETWORK_REQUESTS_PER_UID = 100;
@VisibleForTesting
protected int mLingerDelayMs; // Can't be final, or test subclass constructors can't change it.
+ @VisibleForTesting
+ protected int mNascentDelayMs;
// How long to delay to removal of a pending intent based request.
// See Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS
@@ -992,6 +995,15 @@
}
/**
+ * Gets the UID that owns a socket connection. Needed because opening SOCK_DIAG sockets
+ * requires CAP_NET_ADMIN, which the unit tests do not have.
+ */
+ public int getConnectionOwnerUid(int protocol, InetSocketAddress local,
+ InetSocketAddress remote) {
+ return InetDiagMessage.getConnectionOwnerUid(protocol, local, remote);
+ }
+
+ /**
* @see MultinetworkPolicyTracker
*/
public MultinetworkPolicyTracker makeMultinetworkPolicyTracker(
@@ -1023,11 +1035,13 @@
mNetworkRequestCounter = new PerUidCounter(MAX_NETWORK_REQUESTS_PER_UID);
mMetricsLog = logger;
- mDefaultRequest = createDefaultInternetRequestForTransport(-1, NetworkRequest.Type.REQUEST);
mNetworkRanker = new NetworkRanker();
- NetworkRequestInfo defaultNRI = new NetworkRequestInfo(null, mDefaultRequest, new Binder());
- mNetworkRequests.put(mDefaultRequest, defaultNRI);
- mNetworkRequestInfoLogs.log("REGISTER " + defaultNRI);
+ final NetworkRequest defaultInternetRequest = createDefaultInternetRequestForTransport(
+ -1, NetworkRequest.Type.REQUEST);
+ mDefaultRequest = new NetworkRequestInfo(null, defaultInternetRequest, new Binder());
+ mNetworkRequests.put(defaultInternetRequest, mDefaultRequest);
+ mDefaultNetworkRequests.add(mDefaultRequest);
+ mNetworkRequestInfoLogs.log("REGISTER " + mDefaultRequest);
mDefaultMobileDataRequest = createDefaultInternetRequestForTransport(
NetworkCapabilities.TRANSPORT_CELLULAR, NetworkRequest.Type.BACKGROUND_REQUEST);
@@ -1053,6 +1067,8 @@
Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, 5_000);
mLingerDelayMs = mSystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
+ // TODO: Consider making the timer customizable.
+ mNascentDelayMs = DEFAULT_NASCENT_DELAY_MS;
mNMS = Objects.requireNonNull(netManager, "missing INetworkManagementService");
mStatsService = Objects.requireNonNull(statsService, "missing INetworkStatsService");
@@ -1217,6 +1233,14 @@
mDnsManager = new DnsManager(mContext, mDnsResolver);
registerPrivateDnsSettingsCallbacks();
+
+ mNoServiceNetwork = new NetworkAgentInfo(null,
+ new Network(NO_SERVICE_NET_ID),
+ new NetworkInfo(TYPE_NONE, 0, "", ""),
+ new LinkProperties(), new NetworkCapabilities(), 0, mContext,
+ null, new NetworkAgentConfig(), this, null,
+ null, null, 0, INVALID_UID,
+ mQosCallbackTracker);
}
private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
@@ -1329,31 +1353,6 @@
return mNextNetworkRequestId++;
}
- private NetworkState getFilteredNetworkState(int networkType, int uid) {
- if (mLegacyTypeTracker.isTypeSupported(networkType)) {
- final NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
- final NetworkState state;
- if (nai != null) {
- state = nai.getNetworkState();
- state.networkInfo.setType(networkType);
- } else {
- final NetworkInfo info = new NetworkInfo(networkType, 0,
- getNetworkTypeName(networkType), "");
- info.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null);
- info.setIsAvailable(true);
- final NetworkCapabilities capabilities = new NetworkCapabilities();
- capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING,
- !info.isRoaming());
- state = new NetworkState(info, new LinkProperties(), capabilities,
- null, null, null);
- }
- filterNetworkStateForUid(state, uid, false);
- return state;
- } else {
- return NetworkState.EMPTY;
- }
- }
-
@VisibleForTesting
protected NetworkAgentInfo getNetworkAgentInfoForNetwork(Network network) {
if (network == null) {
@@ -1391,7 +1390,7 @@
}
private NetworkState getUnfilteredActiveNetworkState(int uid) {
- NetworkAgentInfo nai = getDefaultNetwork();
+ NetworkAgentInfo nai = getDefaultNetworkForUid(uid);
final Network[] networks = getVpnUnderlyingNetworks(uid);
if (networks != null) {
@@ -1458,13 +1457,24 @@
return;
}
final String action = blocked ? "BLOCKED" : "UNBLOCKED";
- final NetworkRequest satisfiedRequest = nri.getSatisfiedRequest();
- final int requestId = satisfiedRequest != null
- ? satisfiedRequest.requestId : nri.mRequests.get(0).requestId;
+ final int requestId = nri.getActiveRequest() != null
+ ? nri.getActiveRequest().requestId : nri.mRequests.get(0).requestId;
mNetworkInfoBlockingLogs.log(String.format(
"%s %d(%d) on netId %d", action, nri.mUid, requestId, net.getNetId()));
}
+ private void filterNetworkInfo(@NonNull NetworkInfo networkInfo,
+ @NonNull NetworkCapabilities nc, int uid, boolean ignoreBlocked) {
+ if (isNetworkWithCapabilitiesBlocked(nc, uid, ignoreBlocked)) {
+ networkInfo.setDetailedState(DetailedState.BLOCKED, null, null);
+ }
+ synchronized (mVpns) {
+ if (mLockdownTracker != null) {
+ mLockdownTracker.augmentNetworkInfo(networkInfo);
+ }
+ }
+ }
+
/**
* Apply any relevant filters to {@link NetworkState} for the given UID. For
* example, this may mark the network as {@link DetailedState#BLOCKED} based
@@ -1472,16 +1482,7 @@
*/
private void filterNetworkStateForUid(NetworkState state, int uid, boolean ignoreBlocked) {
if (state == null || state.networkInfo == null || state.linkProperties == null) return;
-
- if (isNetworkWithCapabilitiesBlocked(state.networkCapabilities, uid,
- ignoreBlocked)) {
- state.networkInfo.setDetailedState(DetailedState.BLOCKED, null, null);
- }
- synchronized (mVpns) {
- if (mLockdownTracker != null) {
- mLockdownTracker.augmentNetworkInfo(state.networkInfo);
- }
- }
+ filterNetworkInfo(state.networkInfo, state.networkCapabilities, uid, ignoreBlocked);
}
/**
@@ -1522,7 +1523,7 @@
}
}
- NetworkAgentInfo nai = getDefaultNetwork();
+ NetworkAgentInfo nai = getDefaultNetworkForUid(uid);
if (nai == null || isNetworkWithCapabilitiesBlocked(nai.networkCapabilities, uid,
ignoreBlocked)) {
return null;
@@ -1546,6 +1547,27 @@
return state.networkInfo;
}
+ private NetworkInfo getFilteredNetworkInfo(int networkType, int uid) {
+ if (!mLegacyTypeTracker.isTypeSupported(networkType)) {
+ return null;
+ }
+ final NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
+ final NetworkInfo info;
+ final NetworkCapabilities nc;
+ if (nai != null) {
+ info = new NetworkInfo(nai.networkInfo);
+ info.setType(networkType);
+ nc = nai.networkCapabilities;
+ } else {
+ info = new NetworkInfo(networkType, 0, getNetworkTypeName(networkType), "");
+ info.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null);
+ info.setIsAvailable(true);
+ nc = new NetworkCapabilities();
+ }
+ filterNetworkInfo(info, nc, uid, false);
+ return info;
+ }
+
@Override
public NetworkInfo getNetworkInfo(int networkType) {
enforceAccessPermission();
@@ -1560,8 +1582,7 @@
return state.networkInfo;
}
}
- final NetworkState state = getFilteredNetworkState(networkType, uid);
- return state.networkInfo;
+ return getFilteredNetworkInfo(networkType, uid);
}
@Override
@@ -1580,7 +1601,7 @@
@Override
public NetworkInfo[] getAllNetworkInfo() {
enforceAccessPermission();
- final ArrayList<NetworkInfo> result = Lists.newArrayList();
+ final ArrayList<NetworkInfo> result = new ArrayList<>();
for (int networkType = 0; networkType <= ConnectivityManager.MAX_NETWORK_TYPE;
networkType++) {
NetworkInfo info = getNetworkInfo(networkType);
@@ -1594,10 +1615,16 @@
@Override
public Network getNetworkForType(int networkType) {
enforceAccessPermission();
+ if (!mLegacyTypeTracker.isTypeSupported(networkType)) {
+ return null;
+ }
+ final NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
+ if (nai == null) {
+ return null;
+ }
final int uid = mDeps.getCallingUid();
- NetworkState state = getFilteredNetworkState(networkType, uid);
- if (!isNetworkWithCapabilitiesBlocked(state.networkCapabilities, uid, false)) {
- return state.network;
+ if (!isNetworkWithCapabilitiesBlocked(nai.networkCapabilities, uid, false)) {
+ return nai.network;
}
return null;
}
@@ -1635,21 +1662,28 @@
HashMap<Network, NetworkCapabilities> result = new HashMap<>();
- NetworkAgentInfo nai = getDefaultNetwork();
- NetworkCapabilities nc = getNetworkCapabilitiesInternal(nai);
- if (nc != null) {
- result.put(
- nai.network,
- createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- nc, mDeps.getCallingUid(), callingPackageName));
+ for (final NetworkRequestInfo nri : mDefaultNetworkRequests) {
+ if (!nri.isBeingSatisfied()) {
+ continue;
+ }
+ final NetworkAgentInfo nai = nri.getSatisfier();
+ final NetworkCapabilities nc = getNetworkCapabilitiesInternal(nai);
+ if (null != nc
+ && nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)
+ && !result.containsKey(nai.network)) {
+ result.put(
+ nai.network,
+ createWithLocationInfoSanitizedIfNecessaryWhenParceled(
+ nc, mDeps.getCallingUid(), callingPackageName));
+ }
}
// No need to check mLockdownEnabled. If it's true, getVpnUnderlyingNetworks returns null.
final Network[] networks = getVpnUnderlyingNetworks(Binder.getCallingUid());
- if (networks != null) {
- for (Network network : networks) {
- nc = getNetworkCapabilitiesInternal(network);
- if (nc != null) {
+ if (null != networks) {
+ for (final Network network : networks) {
+ final NetworkCapabilities nc = getNetworkCapabilitiesInternal(network);
+ if (null != nc) {
result.put(
network,
createWithLocationInfoSanitizedIfNecessaryWhenParceled(
@@ -1671,9 +1705,7 @@
/**
* Return LinkProperties for the active (i.e., connected) default
- * network interface. It is assumed that at most one default network
- * is active at a time. If more than one is active, it is indeterminate
- * which will be returned.
+ * network interface for the calling uid.
* @return the ip properties for the active network, or {@code null} if
* none is active
*/
@@ -1848,7 +1880,7 @@
// This contains IMSI details, so make sure the caller is privileged.
NetworkStack.checkNetworkStackPermission(mContext);
- final ArrayList<NetworkState> result = Lists.newArrayList();
+ final ArrayList<NetworkState> result = new ArrayList<>();
for (Network network : getAllNetworks()) {
final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
if (nai != null) {
@@ -2004,7 +2036,7 @@
mHandler.sendMessage(mHandler.obtainMessage(
EVENT_PRIVATE_DNS_VALIDATION_UPDATE,
new PrivateDnsValidationUpdate(netId,
- InetAddress.parseNumericAddress(ipAddress),
+ InetAddresses.parseNumericAddress(ipAddress),
hostname, validated)));
} catch (IllegalArgumentException e) {
loge("Error parsing ip address in validation event");
@@ -2022,7 +2054,7 @@
// TODO: Move the Dns Event to NetworkMonitor. NetdEventListenerService only allow one
// callback from each caller type. Need to re-factor NetdEventListenerService to allow
// multiple NetworkMonitor registrants.
- if (nai != null && nai.satisfies(mDefaultRequest)) {
+ if (nai != null && nai.satisfies(mDefaultRequest.mRequests.get(0))) {
nai.networkMonitor().notifyDnsResponse(returnCode);
}
}
@@ -2116,8 +2148,8 @@
private boolean isUidBlockedByRules(int uid, int uidRules, boolean isNetworkMetered,
boolean isBackgroundRestricted) {
- return NetworkPolicyManagerInternal.isUidNetworkingBlocked(uid, uidRules,
- isNetworkMetered, isBackgroundRestricted);
+ return mPolicyManager.checkUidNetworkingBlocked(uid, uidRules, isNetworkMetered,
+ isBackgroundRestricted);
}
/**
@@ -2700,9 +2732,9 @@
pw.println(nai.requestAt(i).toString());
}
pw.decreaseIndent();
- pw.println("Lingered:");
+ pw.println("Inactivity Timers:");
pw.increaseIndent();
- nai.dumpLingerTimers(pw);
+ nai.dumpInactivityTimers(pw);
pw.decreaseIndent();
pw.decreaseIndent();
}
@@ -2730,7 +2762,7 @@
@VisibleForTesting
NetworkRequestInfo[] requestsSortedById() {
NetworkRequestInfo[] requests = new NetworkRequestInfo[0];
- requests = mNetworkRequests.values().toArray(requests);
+ requests = getNrisFromGlobalRequests().toArray(requests);
// Sort the array based off the NRI containing the min requestId in its requests.
Arrays.sort(requests,
Comparator.comparingInt(nri -> Collections.min(nri.mRequests,
@@ -3297,27 +3329,27 @@
}
/**
- * Updates the linger state from the network requests inside the NAI.
+ * Updates the inactivity state from the network requests inside the NAI.
* @param nai the agent info to update
* @param now the timestamp of the event causing this update
- * @return whether the network was lingered as a result of this update
+ * @return whether the network was inactive as a result of this update
*/
- private boolean updateLingerState(@NonNull final NetworkAgentInfo nai, final long now) {
- // 1. Update the linger timer. If it's changed, reschedule or cancel the alarm.
- // 2. If the network was lingering and there are now requests, unlinger it.
+ private boolean updateInactivityState(@NonNull final NetworkAgentInfo nai, final long now) {
+ // 1. Update the inactivity timer. If it's changed, reschedule or cancel the alarm.
+ // 2. If the network was inactive and there are now requests, unset inactive.
// 3. If this network is unneeded (which implies it is not lingering), and there is at least
- // one lingered request, start lingering.
- nai.updateLingerTimer();
- if (nai.isLingering() && nai.numForegroundNetworkRequests() > 0) {
- if (DBG) log("Unlingering " + nai.toShortString());
- nai.unlinger();
+ // one lingered request, set inactive.
+ nai.updateInactivityTimer();
+ if (nai.isInactive() && nai.numForegroundNetworkRequests() > 0) {
+ if (DBG) log("Unsetting inactive " + nai.toShortString());
+ nai.unsetInactive();
logNetworkEvent(nai, NetworkEvent.NETWORK_UNLINGER);
- } else if (unneeded(nai, UnneededFor.LINGER) && nai.getLingerExpiry() > 0) {
+ } else if (unneeded(nai, UnneededFor.LINGER) && nai.getInactivityExpiry() > 0) {
if (DBG) {
- final int lingerTime = (int) (nai.getLingerExpiry() - now);
- log("Lingering " + nai.toShortString() + " for " + lingerTime + "ms");
+ final int lingerTime = (int) (nai.getInactivityExpiry() - now);
+ log("Setting inactive " + nai.toShortString() + " for " + lingerTime + "ms");
}
- nai.linger();
+ nai.setInactive();
logNetworkEvent(nai, NetworkEvent.NETWORK_LINGER);
return true;
}
@@ -3331,7 +3363,6 @@
if (VDBG) log("NetworkFactory connected");
// Finish setting up the full connection
NetworkProviderInfo npi = mNetworkProviderInfos.get(msg.replyTo);
- npi.completeConnection();
sendAllRequestsToProvider(npi);
} else {
loge("Error connecting NetworkFactory");
@@ -3433,25 +3464,33 @@
propagateUnderlyingNetworkCapabilities(nai.network);
// Remove all previously satisfied requests.
for (int i = 0; i < nai.numNetworkRequests(); i++) {
- NetworkRequest request = nai.requestAt(i);
+ final NetworkRequest request = nai.requestAt(i);
final NetworkRequestInfo nri = mNetworkRequests.get(request);
- final NetworkAgentInfo currentNetwork = nri.mSatisfier;
+ final NetworkAgentInfo currentNetwork = nri.getSatisfier();
if (currentNetwork != null
&& currentNetwork.network.getNetId() == nai.network.getNetId()) {
- nri.mSatisfier = null;
- sendUpdatedScoreToFactories(request, null);
+ // uid rules for this network will be removed in destroyNativeNetwork(nai).
+ nri.setSatisfier(null, null);
+ if (request.isRequest()) {
+ sendUpdatedScoreToFactories(request, null);
+ }
+
+ if (mDefaultRequest == nri) {
+ // TODO : make battery stats aware that since 2013 multiple interfaces may be
+ // active at the same time. For now keep calling this with the default
+ // network, because while incorrect this is the closest to the old (also
+ // incorrect) behavior.
+ mNetworkActivityTracker.updateDataActivityTracking(
+ null /* newNetwork */, nai);
+ notifyLockdownVpn(nai);
+ ensureNetworkTransitionWakelock(nai.toShortString());
+ }
}
}
- nai.clearLingerState();
- // TODO: this loop, and the mLegacyTypeTracker.remove just below it, seem redundant given
- // there's a full rematch right after. Currently, deleting it breaks tests that check for
- // the default network disconnecting. Find out why, fix the rematch code, and delete this.
- if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) {
- mDefaultNetworkNai = null;
- mNetworkActivityTracker.updateDataActivityTracking(null /* newNetwork */, nai);
- notifyLockdownVpn(nai);
- ensureNetworkTransitionWakelock(nai.toShortString());
- }
+ nai.clearInactivityState();
+ // TODO: mLegacyTypeTracker.remove seems redundant given there's a full rematch right after.
+ // Currently, deleting it breaks tests that check for the default network disconnecting.
+ // Find out why, fix the rematch code, and delete this.
mLegacyTypeTracker.remove(nai, wasDefault);
rematchAllNetworksAndRequests();
mLingerMonitor.noteDisconnect(nai);
@@ -3460,10 +3499,9 @@
// (routing rules, DNS, etc).
// This may be slow as it requires a lot of netd shelling out to ip and
// ip[6]tables to flush routes and remove the incoming packet mark rule, so do it
- // after we've rematched networks with requests which should make a potential
- // fallback network the default or requested a new network from the
- // NetworkProviders, so network traffic isn't interrupted for an unnecessarily
- // long time.
+ // after we've rematched networks with requests (which might change the default
+ // network or service a new request from an app), so network traffic isn't interrupted
+ // for an unnecessarily long time.
destroyNativeNetwork(nai);
mDnsManager.removeNetwork(nai.network);
}
@@ -3516,42 +3554,60 @@
return null;
}
- private void handleRegisterNetworkRequestWithIntent(Message msg) {
+ private void handleRegisterNetworkRequestWithIntent(@NonNull final Message msg) {
final NetworkRequestInfo nri = (NetworkRequestInfo) (msg.obj);
-
- NetworkRequestInfo existingRequest = findExistingNetworkRequestInfo(nri.mPendingIntent);
+ // handleRegisterNetworkRequestWithIntent() doesn't apply to multilayer requests.
+ ensureNotMultilayerRequest(nri, "handleRegisterNetworkRequestWithIntent");
+ final NetworkRequestInfo existingRequest =
+ findExistingNetworkRequestInfo(nri.mPendingIntent);
if (existingRequest != null) { // remove the existing request.
- if (DBG) log("Replacing " + existingRequest.request + " with "
- + nri.request + " because their intents matched.");
- handleReleaseNetworkRequest(existingRequest.request, getCallingUid(),
+ if (DBG) {
+ log("Replacing " + existingRequest.mRequests.get(0) + " with "
+ + nri.mRequests.get(0) + " because their intents matched.");
+ }
+ handleReleaseNetworkRequest(existingRequest.mRequests.get(0), getCallingUid(),
/* callOnUnavailable */ false);
}
handleRegisterNetworkRequest(nri);
}
- private void handleRegisterNetworkRequest(NetworkRequestInfo nri) {
+ private void handleRegisterNetworkRequest(@NonNull final NetworkRequestInfo nri) {
ensureRunningOnConnectivityServiceThread();
- mNetworkRequests.put(nri.request, nri);
mNetworkRequestInfoLogs.log("REGISTER " + nri);
- if (nri.request.isListen()) {
- for (NetworkAgentInfo network : mNetworkAgentInfos) {
- if (nri.request.networkCapabilities.hasSignalStrength() &&
- network.satisfiesImmutableCapabilitiesOf(nri.request)) {
- updateSignalStrengthThresholds(network, "REGISTER", nri.request);
+ for (final NetworkRequest req : nri.mRequests) {
+ mNetworkRequests.put(req, nri);
+ if (req.isListen()) {
+ for (final NetworkAgentInfo network : mNetworkAgentInfos) {
+ if (req.networkCapabilities.hasSignalStrength()
+ && network.satisfiesImmutableCapabilitiesOf(req)) {
+ updateSignalStrengthThresholds(network, "REGISTER", req);
+ }
}
}
}
rematchAllNetworksAndRequests();
- if (nri.request.isRequest() && nri.mSatisfier == null) {
- sendUpdatedScoreToFactories(nri.request, null);
+ // If the nri is satisfied, return as its score has already been sent if needed.
+ if (nri.isBeingSatisfied()) {
+ return;
+ }
+
+ // As this request was not satisfied on rematch and thus never had any scores sent to the
+ // factories, send null now for each request of type REQUEST.
+ for (final NetworkRequest req : nri.mRequests) {
+ if (req.isRequest()) sendUpdatedScoreToFactories(req, null);
}
}
- private void handleReleaseNetworkRequestWithIntent(PendingIntent pendingIntent,
- int callingUid) {
- NetworkRequestInfo nri = findExistingNetworkRequestInfo(pendingIntent);
+ private void handleReleaseNetworkRequestWithIntent(@NonNull final PendingIntent pendingIntent,
+ final int callingUid) {
+ final NetworkRequestInfo nri = findExistingNetworkRequestInfo(pendingIntent);
if (nri != null) {
- handleReleaseNetworkRequest(nri.request, callingUid, /* callOnUnavailable */ false);
+ // handleReleaseNetworkRequestWithIntent() paths don't apply to multilayer requests.
+ ensureNotMultilayerRequest(nri, "handleReleaseNetworkRequestWithIntent");
+ handleReleaseNetworkRequest(
+ nri.mRequests.get(0),
+ callingUid,
+ /* callOnUnavailable */ false);
}
}
@@ -3578,7 +3634,7 @@
return true;
}
- if (!nai.everConnected || nai.isVPN() || nai.isLingering() || numRequests > 0) {
+ if (!nai.everConnected || nai.isVPN() || nai.isInactive() || numRequests > 0) {
return false;
}
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
@@ -3605,6 +3661,11 @@
return false;
}
for (final NetworkRequest req : nri.mRequests) {
+ // This multilayer listen request is satisfied therefore no further requests need to be
+ // evaluated deeming this network not a potential satisfier.
+ if (req.isListen() && nri.getActiveRequest() == req) {
+ return false;
+ }
// As non-multilayer listen requests have already returned, the below would only happen
// for a multilayer request therefore continue to the next request if available.
if (req.isListen()) {
@@ -3625,7 +3686,7 @@
// 2. Unvalidated WiFi will not be reaped when validated cellular
// is currently satisfying the request. This is desirable when
// WiFi ends up validating and out scoring cellular.
- || nri.mSatisfier.getCurrentScore()
+ || nri.getSatisfier().getCurrentScore()
< candidate.getCurrentScoreAsValidated();
return isNetworkNeeded;
}
@@ -3650,30 +3711,45 @@
return nri;
}
- private void handleTimedOutNetworkRequest(final NetworkRequestInfo nri) {
- ensureRunningOnConnectivityServiceThread();
- if (mNetworkRequests.get(nri.request) == null) {
- return;
+ private void ensureNotMultilayerRequest(@NonNull final NetworkRequestInfo nri,
+ final String callingMethod) {
+ if (nri.isMultilayerRequest()) {
+ throw new IllegalStateException(
+ callingMethod + " does not support multilayer requests.");
}
- if (nri.mSatisfier != null) {
- return;
- }
- if (VDBG || (DBG && nri.request.isRequest())) {
- log("releasing " + nri.request + " (timeout)");
- }
- handleRemoveNetworkRequest(nri);
- callCallbackForRequest(nri, null, ConnectivityManager.CALLBACK_UNAVAIL, 0);
}
- private void handleReleaseNetworkRequest(NetworkRequest request, int callingUid,
- boolean callOnUnavailable) {
+ private void handleTimedOutNetworkRequest(@NonNull final NetworkRequestInfo nri) {
+ ensureRunningOnConnectivityServiceThread();
+ // handleTimedOutNetworkRequest() is part of the requestNetwork() flow which works off of a
+ // single NetworkRequest and thus does not apply to multilayer requests.
+ ensureNotMultilayerRequest(nri, "handleTimedOutNetworkRequest");
+ if (mNetworkRequests.get(nri.mRequests.get(0)) == null) {
+ return;
+ }
+ if (nri.isBeingSatisfied()) {
+ return;
+ }
+ if (VDBG || (DBG && nri.mRequests.get(0).isRequest())) {
+ log("releasing " + nri.mRequests.get(0) + " (timeout)");
+ }
+ handleRemoveNetworkRequest(nri);
+ callCallbackForRequest(
+ nri, null, ConnectivityManager.CALLBACK_UNAVAIL, 0);
+ }
+
+ private void handleReleaseNetworkRequest(@NonNull final NetworkRequest request,
+ final int callingUid,
+ final boolean callOnUnavailable) {
final NetworkRequestInfo nri =
getNriForAppRequest(request, callingUid, "release NetworkRequest");
if (nri == null) {
return;
}
- if (VDBG || (DBG && nri.request.isRequest())) {
- log("releasing " + nri.request + " (release request)");
+ // handleReleaseNetworkRequest() paths don't apply to multilayer requests.
+ ensureNotMultilayerRequest(nri, "handleReleaseNetworkRequest");
+ if (VDBG || (DBG && request.isRequest())) {
+ log("releasing " + request + " (release request)");
}
handleRemoveNetworkRequest(nri);
if (callOnUnavailable) {
@@ -3681,42 +3757,88 @@
}
}
- private void handleRemoveNetworkRequest(final NetworkRequestInfo nri) {
+ private void handleRemoveNetworkRequest(@NonNull final NetworkRequestInfo nri) {
ensureRunningOnConnectivityServiceThread();
nri.unlinkDeathRecipient();
- mNetworkRequests.remove(nri.request);
-
+ for (final NetworkRequest req : nri.mRequests) {
+ mNetworkRequests.remove(req);
+ if (req.isListen()) {
+ removeListenRequestFromNetworks(req);
+ }
+ }
mNetworkRequestCounter.decrementCount(nri.mUid);
-
mNetworkRequestInfoLogs.log("RELEASE " + nri);
- if (nri.request.isRequest()) {
- boolean wasKept = false;
- final NetworkAgentInfo nai = nri.mSatisfier;
- if (nai != null) {
- boolean wasBackgroundNetwork = nai.isBackgroundNetwork();
- nai.removeRequest(nri.request.requestId);
- if (VDBG || DDBG) {
- log(" Removing from current network " + nai.toShortString()
- + ", leaving " + nai.numNetworkRequests() + " requests.");
- }
- // If there are still lingered requests on this network, don't tear it down,
- // but resume lingering instead.
- final long now = SystemClock.elapsedRealtime();
- if (updateLingerState(nai, now)) {
- notifyNetworkLosing(nai, now);
- }
- if (unneeded(nai, UnneededFor.TEARDOWN)) {
- if (DBG) log("no live requests for " + nai.toShortString() + "; disconnecting");
- teardownUnneededNetwork(nai);
- } else {
- wasKept = true;
- }
- nri.mSatisfier = null;
- if (!wasBackgroundNetwork && nai.isBackgroundNetwork()) {
- // Went from foreground to background.
- updateCapabilitiesForNetwork(nai);
- }
+
+ if (null != nri.getActiveRequest()) {
+ if (!nri.getActiveRequest().isListen()) {
+ removeSatisfiedNetworkRequestFromNetwork(nri);
+ } else {
+ nri.setSatisfier(null, null);
+ }
+ }
+
+ cancelNpiRequests(nri);
+ }
+
+ private void cancelNpiRequests(@NonNull final NetworkRequestInfo nri) {
+ for (final NetworkRequest req : nri.mRequests) {
+ cancelNpiRequest(req);
+ }
+ }
+
+ private void cancelNpiRequest(@NonNull final NetworkRequest req) {
+ if (req.isRequest()) {
+ for (final NetworkProviderInfo npi : mNetworkProviderInfos.values()) {
+ npi.cancelRequest(req);
+ }
+ }
+ }
+
+ private void removeListenRequestFromNetworks(@NonNull final NetworkRequest req) {
+ // listens don't have a singular affected Network. Check all networks to see
+ // if this listen request applies and remove it.
+ for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
+ nai.removeRequest(req.requestId);
+ if (req.networkCapabilities.hasSignalStrength()
+ && nai.satisfiesImmutableCapabilitiesOf(req)) {
+ updateSignalStrengthThresholds(nai, "RELEASE", req);
+ }
+ }
+ }
+
+ /**
+ * Remove a NetworkRequestInfo's satisfied request from its 'satisfier' (NetworkAgentInfo) and
+ * manage the necessary upkeep (linger, teardown networks, etc.) when doing so.
+ * @param nri the NetworkRequestInfo to disassociate from its current NetworkAgentInfo
+ */
+ private void removeSatisfiedNetworkRequestFromNetwork(@NonNull final NetworkRequestInfo nri) {
+ boolean wasKept = false;
+ final NetworkAgentInfo nai = nri.getSatisfier();
+ if (nai != null) {
+ final int requestLegacyType = nri.getActiveRequest().legacyType;
+ final boolean wasBackgroundNetwork = nai.isBackgroundNetwork();
+ nai.removeRequest(nri.getActiveRequest().requestId);
+ if (VDBG || DDBG) {
+ log(" Removing from current network " + nai.toShortString()
+ + ", leaving " + nai.numNetworkRequests() + " requests.");
+ }
+ // If there are still lingered requests on this network, don't tear it down,
+ // but resume lingering instead.
+ final long now = SystemClock.elapsedRealtime();
+ if (updateInactivityState(nai, now)) {
+ notifyNetworkLosing(nai, now);
+ }
+ if (unneeded(nai, UnneededFor.TEARDOWN)) {
+ if (DBG) log("no live requests for " + nai.toShortString() + "; disconnecting");
+ teardownUnneededNetwork(nai);
+ } else {
+ wasKept = true;
+ }
+ nri.setSatisfier(null, null);
+ if (!wasBackgroundNetwork && nai.isBackgroundNetwork()) {
+ // Went from foreground to background.
+ updateCapabilitiesForNetwork(nai);
}
// Maintain the illusion. When this request arrived, we might have pretended
@@ -3724,15 +3846,15 @@
// connected. Now that this request has gone away, we might have to pretend
// that the network disconnected. LegacyTypeTracker will generate that
// phantom disconnect for this type.
- if (nri.request.legacyType != TYPE_NONE && nai != null) {
+ if (requestLegacyType != TYPE_NONE) {
boolean doRemove = true;
if (wasKept) {
// check if any of the remaining requests for this network are for the
// same legacy type - if so, don't remove the nai
for (int i = 0; i < nai.numNetworkRequests(); i++) {
NetworkRequest otherRequest = nai.requestAt(i);
- if (otherRequest.legacyType == nri.request.legacyType &&
- otherRequest.isRequest()) {
+ if (otherRequest.legacyType == requestLegacyType
+ && otherRequest.isRequest()) {
if (DBG) log(" still have other legacy request - leaving");
doRemove = false;
}
@@ -3740,21 +3862,7 @@
}
if (doRemove) {
- mLegacyTypeTracker.remove(nri.request.legacyType, nai, false);
- }
- }
-
- for (NetworkProviderInfo npi : mNetworkProviderInfos.values()) {
- npi.cancelRequest(nri.request);
- }
- } else {
- // listens don't have a singular affectedNetwork. Check all networks to see
- // if this listen request applies and remove it.
- for (NetworkAgentInfo nai : mNetworkAgentInfos) {
- nai.removeRequest(nri.request.requestId);
- if (nri.request.networkCapabilities.hasSignalStrength() &&
- nai.satisfiesImmutableCapabilitiesOf(nri.request)) {
- updateSignalStrengthThresholds(nai, "RELEASE", nri.request);
+ mLegacyTypeTracker.remove(requestLegacyType, nai, false);
}
}
}
@@ -4179,7 +4287,7 @@
@Override
public NetworkRequest getDefaultRequest() {
- return mDefaultRequest;
+ return mDefaultRequest.mRequests.get(0);
}
private class InternalHandler extends Handler {
@@ -4757,7 +4865,7 @@
}
synchronized (mVpns) {
throwIfLockdownEnabled();
- mVpns.get(user).startLegacyVpn(profile, mKeyStore, egress);
+ mVpns.get(user).startLegacyVpn(profile, mKeyStore, null /* underlying */, egress);
}
}
@@ -4782,35 +4890,36 @@
*
* <p>Must be called on the handler thread.
*/
- private VpnInfo[] getAllVpnInfo() {
+ private UnderlyingNetworkInfo[] getAllVpnInfo() {
ensureRunningOnConnectivityServiceThread();
synchronized (mVpns) {
if (mLockdownEnabled) {
- return new VpnInfo[0];
+ return new UnderlyingNetworkInfo[0];
}
}
- List<VpnInfo> infoList = new ArrayList<>();
+ List<UnderlyingNetworkInfo> infoList = new ArrayList<>();
for (NetworkAgentInfo nai : mNetworkAgentInfos) {
- VpnInfo info = createVpnInfo(nai);
+ UnderlyingNetworkInfo info = createVpnInfo(nai);
if (info != null) {
infoList.add(info);
}
}
- return infoList.toArray(new VpnInfo[infoList.size()]);
+ return infoList.toArray(new UnderlyingNetworkInfo[infoList.size()]);
}
/**
* @return VPN information for accounting, or null if we can't retrieve all required
* information, e.g underlying ifaces.
*/
- private VpnInfo createVpnInfo(NetworkAgentInfo nai) {
+ private UnderlyingNetworkInfo createVpnInfo(NetworkAgentInfo nai) {
if (!nai.isVPN()) return null;
Network[] underlyingNetworks = nai.declaredUnderlyingNetworks;
// see VpnService.setUnderlyingNetworks()'s javadoc about how to interpret
// the underlyingNetworks list.
if (underlyingNetworks == null) {
- NetworkAgentInfo defaultNai = getDefaultNetwork();
+ final NetworkAgentInfo defaultNai = getDefaultNetworkForUid(
+ nai.networkCapabilities.getOwnerUid());
if (defaultNai != null) {
underlyingNetworks = new Network[] { defaultNai.network };
}
@@ -4832,16 +4941,14 @@
if (interfaces.isEmpty()) return null;
- VpnInfo info = new VpnInfo();
- info.ownerUid = nai.networkCapabilities.getOwnerUid();
- info.vpnIface = nai.linkProperties.getInterfaceName();
// Must be non-null or NetworkStatsService will crash.
// Cannot happen in production code because Vpn only registers the NetworkAgent after the
// tun or ipsec interface is created.
- if (info.vpnIface == null) return null;
- info.underlyingIfaces = interfaces.toArray(new String[0]);
+ // TODO: Remove this check.
+ if (nai.linkProperties.getInterfaceName() == null) return null;
- return info;
+ return new UnderlyingNetworkInfo(nai.networkCapabilities.getOwnerUid(),
+ nai.linkProperties.getInterfaceName(), interfaces);
}
/**
@@ -4863,8 +4970,10 @@
}
}
- private Network[] underlyingNetworksOrDefault(Network[] underlyingNetworks) {
- final Network defaultNetwork = getNetwork(getDefaultNetwork());
+ // TODO This needs to be the default network that applies to the NAI.
+ private Network[] underlyingNetworksOrDefault(final int ownerUid,
+ Network[] underlyingNetworks) {
+ final Network defaultNetwork = getNetwork(getDefaultNetworkForUid(ownerUid));
if (underlyingNetworks == null && defaultNetwork != null) {
// null underlying networks means to track the default.
underlyingNetworks = new Network[] { defaultNetwork };
@@ -4877,7 +4986,8 @@
// TODO: support more than one level of underlying networks, either via a fixed-depth search
// (e.g., 2 levels of underlying networks), or via loop detection, or....
if (!nai.supportsUnderlyingNetworks()) return false;
- final Network[] underlying = underlyingNetworksOrDefault(nai.declaredUnderlyingNetworks);
+ final Network[] underlying = underlyingNetworksOrDefault(
+ nai.networkCapabilities.getOwnerUid(), nai.declaredUnderlyingNetworks);
return ArrayUtils.contains(underlying, network);
}
@@ -5335,27 +5445,21 @@
private static class NetworkProviderInfo {
public final String name;
public final Messenger messenger;
- private final AsyncChannel mAsyncChannel;
private final IBinder.DeathRecipient mDeathRecipient;
public final int providerId;
NetworkProviderInfo(String name, Messenger messenger, AsyncChannel asyncChannel,
- int providerId, IBinder.DeathRecipient deathRecipient) {
+ int providerId, @NonNull IBinder.DeathRecipient deathRecipient) {
this.name = name;
this.messenger = messenger;
this.providerId = providerId;
- mAsyncChannel = asyncChannel;
mDeathRecipient = deathRecipient;
- if ((mAsyncChannel == null) == (mDeathRecipient == null)) {
- throw new AssertionError("Must pass exactly one of asyncChannel or deathRecipient");
+ if (mDeathRecipient == null) {
+ throw new AssertionError("Must pass a deathRecipient");
}
}
- boolean isLegacyNetworkFactory() {
- return mAsyncChannel != null;
- }
-
void sendMessageToNetworkProvider(int what, int arg1, int arg2, Object obj) {
try {
messenger.send(Message.obtain(null /* handler */, what, arg1, arg2, obj));
@@ -5366,38 +5470,19 @@
}
void requestNetwork(NetworkRequest request, int score, int servingProviderId) {
- if (isLegacyNetworkFactory()) {
- mAsyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score,
- servingProviderId, request);
- } else {
- sendMessageToNetworkProvider(NetworkProvider.CMD_REQUEST_NETWORK, score,
+ sendMessageToNetworkProvider(NetworkProvider.CMD_REQUEST_NETWORK, score,
servingProviderId, request);
- }
}
void cancelRequest(NetworkRequest request) {
- if (isLegacyNetworkFactory()) {
- mAsyncChannel.sendMessage(android.net.NetworkFactory.CMD_CANCEL_REQUEST, request);
- } else {
- sendMessageToNetworkProvider(NetworkProvider.CMD_CANCEL_REQUEST, 0, 0, request);
- }
+ sendMessageToNetworkProvider(NetworkProvider.CMD_CANCEL_REQUEST, 0, 0, request);
}
void connect(Context context, Handler handler) {
- if (isLegacyNetworkFactory()) {
- mAsyncChannel.connect(context, handler, messenger);
- } else {
- try {
- messenger.getBinder().linkToDeath(mDeathRecipient, 0);
- } catch (RemoteException e) {
- mDeathRecipient.binderDied();
- }
- }
- }
-
- void completeConnection() {
- if (isLegacyNetworkFactory()) {
- mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
+ try {
+ messenger.getBinder().linkToDeath(mDeathRecipient, 0);
+ } catch (RemoteException e) {
+ mDeathRecipient.binderDied();
}
}
}
@@ -5417,18 +5502,39 @@
/**
* Tracks info about the requester.
- * Also used to notice when the calling process dies so we can self-expire
+ * Also used to notice when the calling process dies so as to self-expire
*/
@VisibleForTesting
protected class NetworkRequestInfo implements IBinder.DeathRecipient {
+ // The requests to be satisfied in priority order. Non-multilayer requests will only have a
+ // single NetworkRequest in mRequests.
final List<NetworkRequest> mRequests;
- final NetworkRequest request;
- // The network currently satisfying this request, or null if none. Must only be touched
- // on the handler thread. This only makes sense for network requests and not for listens,
- // as defined by NetworkRequest#isRequest(). For listens, this is always null.
+ // mSatisfier and mActiveRequest rely on one another therefore set them together.
+ void setSatisfier(
+ @Nullable final NetworkAgentInfo satisfier,
+ @Nullable final NetworkRequest activeRequest) {
+ mSatisfier = satisfier;
+ mActiveRequest = activeRequest;
+ }
+
+ // The network currently satisfying this NRI. Only one request in an NRI can have a
+ // satisfier. For non-multilayer requests, only non-listen requests can have a satisfier.
@Nullable
- NetworkAgentInfo mSatisfier;
+ private NetworkAgentInfo mSatisfier;
+ NetworkAgentInfo getSatisfier() {
+ return mSatisfier;
+ }
+
+ // The request in mRequests assigned to a network agent. This is null if none of the
+ // requests in mRequests can be satisfied. This member has the constraint of only being
+ // accessible on the handler thread.
+ @Nullable
+ private NetworkRequest mActiveRequest;
+ NetworkRequest getActiveRequest() {
+ return mActiveRequest;
+ }
+
final PendingIntent mPendingIntent;
boolean mPendingIntentSent;
private final IBinder mBinder;
@@ -5436,8 +5542,19 @@
final int mUid;
final Messenger messenger;
+ /**
+ * Get the list of UIDs this nri applies to.
+ */
+ @NonNull
+ private Set<UidRange> getUids() {
+ // networkCapabilities.getUids() returns a defensive copy.
+ // multilayer requests will all have the same uids so return the first one.
+ final Set<UidRange> uids = null == mRequests.get(0).networkCapabilities.getUids()
+ ? new ArraySet<>() : mRequests.get(0).networkCapabilities.getUids();
+ return uids;
+ }
+
NetworkRequestInfo(NetworkRequest r, PendingIntent pi) {
- request = r;
mRequests = initializeRequests(r);
ensureAllNetworkRequestsHaveType(mRequests);
mPendingIntent = pi;
@@ -5451,7 +5568,6 @@
NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder) {
super();
messenger = m;
- request = r;
mRequests = initializeRequests(r);
ensureAllNetworkRequestsHaveType(mRequests);
mBinder = binder;
@@ -5471,6 +5587,13 @@
this(r, null);
}
+ // True if this NRI is being satisfied. It also accounts for if the nri has its satisifer
+ // set to the mNoServiceNetwork in which case mActiveRequest will be null thus returning
+ // false.
+ boolean isBeingSatisfied() {
+ return (null != mSatisfier && null != mActiveRequest);
+ }
+
boolean isMultilayerRequest() {
return mRequests.size() > 1;
}
@@ -5481,20 +5604,6 @@
return Collections.unmodifiableList(tempRequests);
}
- private NetworkRequest getSatisfiedRequest() {
- if (mSatisfier == null) {
- return null;
- }
-
- for (NetworkRequest req : mRequests) {
- if (mSatisfier.isSatisfyingRequest(req.requestId)) {
- return req;
- }
- }
-
- return null;
- }
-
void unlinkDeathRecipient() {
if (mBinder != null) {
mBinder.unlinkToDeath(this, 0);
@@ -5510,7 +5619,9 @@
@Override
public String toString() {
- return "uid/pid:" + mUid + "/" + mPid + " " + mRequests
+ return "uid/pid:" + mUid + "/" + mPid + " active request Id: "
+ + (mActiveRequest == null ? null : mActiveRequest.requestId)
+ + " " + mRequests
+ (mPendingIntent == null ? "" : " to trigger " + mPendingIntent);
}
}
@@ -5541,6 +5652,10 @@
private int[] getSignalStrengthThresholds(@NonNull final NetworkAgentInfo nai) {
final SortedSet<Integer> thresholds = new TreeSet<>();
synchronized (nai) {
+ // mNetworkRequests may contain the same value multiple times in case of
+ // multilayer requests. It won't matter in this case because the thresholds
+ // will then be the same and be deduplicated as they enter the `thresholds` set.
+ // TODO : have mNetworkRequests be a Set<NetworkRequestInfo> or the like.
for (final NetworkRequestInfo nri : mNetworkRequests.values()) {
for (final NetworkRequest req : nri.mRequests) {
if (req.networkCapabilities.hasSignalStrength()
@@ -5580,7 +5695,9 @@
if (ns == null) {
return;
}
- MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(ns);
+ if (ns instanceof MatchAllNetworkSpecifier) {
+ throw new IllegalArgumentException("A MatchAllNetworkSpecifier is not permitted");
+ }
}
private void ensureValid(NetworkCapabilities nc) {
@@ -5627,6 +5744,9 @@
networkCapabilities = createDefaultNetworkCapabilitiesForUid(callingUid);
enforceAccessPermission();
break;
+ case BACKGROUND_REQUEST:
+ enforceNetworkStackOrSettingsPermission();
+ // Fall-through since other checks are the same with normal requests.
case REQUEST:
networkCapabilities = new NetworkCapabilities(networkCapabilities);
enforceNetworkRequestPermissions(networkCapabilities, callingPackageName,
@@ -5857,15 +5977,6 @@
EVENT_RELEASE_NETWORK_REQUEST, getCallingUid(), 0, networkRequest));
}
- @Override
- public int registerNetworkFactory(Messenger messenger, String name) {
- enforceNetworkFactoryPermission();
- NetworkProviderInfo npi = new NetworkProviderInfo(name, messenger, new AsyncChannel(),
- nextNetworkProviderId(), null /* deathRecipient */);
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_PROVIDER, npi));
- return npi.providerId;
- }
-
private void handleRegisterNetworkProvider(NetworkProviderInfo npi) {
if (mNetworkProviderInfos.containsKey(npi.messenger)) {
// Avoid creating duplicates. even if an app makes a direct AIDL call.
@@ -5879,10 +5990,7 @@
if (DBG) log("Got NetworkProvider Messenger for " + npi.name);
mNetworkProviderInfos.put(npi.messenger, npi);
npi.connect(mContext, mTrackerHandler);
- if (!npi.isLegacyNetworkFactory()) {
- // Legacy NetworkFactories get their requests when their AsyncChannel connects.
- sendAllRequestsToProvider(npi);
- }
+ sendAllRequestsToProvider(npi);
}
@Override
@@ -5901,11 +6009,6 @@
mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNREGISTER_NETWORK_PROVIDER, messenger));
}
- @Override
- public void unregisterNetworkFactory(Messenger messenger) {
- unregisterNetworkProvider(messenger);
- }
-
private void handleUnregisterNetworkProvider(Messenger messenger) {
NetworkProviderInfo npi = mNetworkProviderInfos.remove(messenger);
if (npi == null) {
@@ -5916,13 +6019,19 @@
}
@Override
- public void declareNetworkRequestUnfulfillable(NetworkRequest request) {
+ public void declareNetworkRequestUnfulfillable(@NonNull final NetworkRequest request) {
if (request.hasTransport(TRANSPORT_TEST)) {
enforceNetworkFactoryOrTestNetworksPermission();
} else {
enforceNetworkFactoryPermission();
}
- mHandler.post(() -> handleReleaseNetworkRequest(request, mDeps.getCallingUid(), true));
+ final NetworkRequestInfo nri = mNetworkRequests.get(request);
+ if (nri != null) {
+ // declareNetworkRequestUnfulfillable() paths don't apply to multilayer requests.
+ ensureNotMultilayerRequest(nri, "declareNetworkRequestUnfulfillable");
+ mHandler.post(() -> handleReleaseNetworkRequest(
+ nri.mRequests.get(0), mDeps.getCallingUid(), true));
+ }
}
// NOTE: Accessed on multiple threads, must be synchronized on itself.
@@ -5947,11 +6056,33 @@
@GuardedBy("mBlockedAppUids")
private final HashSet<Integer> mBlockedAppUids = new HashSet<>();
+ // The always-on request for an Internet-capable network that apps without a specific default
+ // fall back to.
@NonNull
- private final NetworkRequest mDefaultRequest;
- // The NetworkAgentInfo currently satisfying the default request, if any.
- @Nullable
- private volatile NetworkAgentInfo mDefaultNetworkNai = null;
+ private final NetworkRequestInfo mDefaultRequest;
+ // Collection of NetworkRequestInfo's used for default networks.
+ @NonNull
+ private final ArraySet<NetworkRequestInfo> mDefaultNetworkRequests = new ArraySet<>();
+
+ private boolean isPerAppDefaultRequest(@NonNull final NetworkRequestInfo nri) {
+ return (mDefaultNetworkRequests.contains(nri) && mDefaultRequest != nri);
+ }
+
+ /**
+ * Determine if an nri is a managed default request that disallows default networking.
+ * @param nri the request to evaluate
+ * @return true if device-default networking is disallowed
+ */
+ private boolean isDefaultBlocked(@NonNull final NetworkRequestInfo nri) {
+ // Check if this nri is a managed default that supports the default network at its
+ // lowest priority request.
+ final NetworkRequest defaultNetworkRequest = mDefaultRequest.mRequests.get(0);
+ final NetworkCapabilities lowestPriorityNetCap =
+ nri.mRequests.get(nri.mRequests.size() - 1).networkCapabilities;
+ return isPerAppDefaultRequest(nri)
+ && !(defaultNetworkRequest.networkCapabilities.equalRequestableCapabilities(
+ lowestPriorityNetCap));
+ }
// Request used to optionally keep mobile data active even when higher
// priority networks like Wi-Fi are active.
@@ -5964,8 +6095,37 @@
// Request used to optionally keep vehicle internal network always active
private final NetworkRequest mDefaultVehicleRequest;
+ // TODO replace with INetd.DUMMY_NET_ID when available.
+ private static final int NO_SERVICE_NET_ID = 51;
+ // Sentinel NAI used to direct apps with default networks that should have no connectivity to a
+ // network with no service. This NAI should never be matched against, nor should any public API
+ // ever return the associated network. For this reason, this NAI is not in the list of available
+ // NAIs. It is used in computeNetworkReassignment() to be set as the satisfier for non-device
+ // default requests that don't support using the device default network which will ultimately
+ // allow ConnectivityService to use this no-service network when calling makeDefaultForApps().
+ @VisibleForTesting
+ final NetworkAgentInfo mNoServiceNetwork;
+
+ // The NetworkAgentInfo currently satisfying the default request, if any.
private NetworkAgentInfo getDefaultNetwork() {
- return mDefaultNetworkNai;
+ return mDefaultRequest.mSatisfier;
+ }
+
+ private NetworkAgentInfo getDefaultNetworkForUid(final int uid) {
+ for (final NetworkRequestInfo nri : mDefaultNetworkRequests) {
+ // Currently, all network requests will have the same uids therefore checking the first
+ // one is sufficient. If/when uids are tracked at the nri level, this can change.
+ final Set<UidRange> uids = nri.mRequests.get(0).networkCapabilities.getUids();
+ if (null == uids) {
+ continue;
+ }
+ for (final UidRange range : uids) {
+ if (range.contains(uid)) {
+ return nri.getSatisfier();
+ }
+ }
+ }
+ return getDefaultNetwork();
}
@Nullable
@@ -6052,8 +6212,6 @@
LinkProperties lp = new LinkProperties(linkProperties);
- // TODO: Instead of passing mDefaultRequest, provide an API to determine whether a Network
- // satisfies mDefaultRequest.
final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
final NetworkAgentInfo nai = new NetworkAgentInfo(na,
new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc,
@@ -6110,7 +6268,7 @@
nai.networkAgentPortalData = lp.getCaptivePortalData();
}
- private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties newLp,
+ private void updateLinkProperties(NetworkAgentInfo networkAgent, @NonNull LinkProperties newLp,
@NonNull LinkProperties oldLp) {
int netId = networkAgent.network.getNetId();
@@ -6119,8 +6277,7 @@
// the LinkProperties for the network are accurate.
networkAgent.clatd.fixupLinkProperties(oldLp, newLp);
- updateInterfaces(newLp, oldLp, netId, networkAgent.networkCapabilities,
- networkAgent.networkInfo.getType());
+ updateInterfaces(newLp, oldLp, netId, networkAgent.networkCapabilities);
// update filtering rules, need to happen after the interface update so netd knows about the
// new interface (the interface name -> index map becomes initialized)
@@ -6259,7 +6416,7 @@
private void updateInterfaces(final @Nullable LinkProperties newLp,
final @Nullable LinkProperties oldLp, final int netId,
- final @Nullable NetworkCapabilities caps, final int legacyType) {
+ final @NonNull NetworkCapabilities caps) {
final CompareResult<String> interfaceDiff = new CompareResult<>(
oldLp != null ? oldLp.getAllInterfaceNames() : null,
newLp != null ? newLp.getAllInterfaceNames() : null);
@@ -6270,7 +6427,7 @@
if (DBG) log("Adding iface " + iface + " to network " + netId);
mNetd.networkAddInterface(netId, iface);
wakeupModifyInterface(iface, caps, true);
- bs.noteNetworkInterfaceType(iface, legacyType);
+ bs.noteNetworkInterfaceForTransports(iface, caps.getTransportTypes());
} catch (Exception e) {
loge("Exception adding interface: " + e);
}
@@ -6477,7 +6634,8 @@
@VisibleForTesting
void applyUnderlyingCapabilities(@Nullable Network[] underlyingNetworks,
@NonNull NetworkCapabilities agentCaps, @NonNull NetworkCapabilities newNc) {
- underlyingNetworks = underlyingNetworksOrDefault(underlyingNetworks);
+ underlyingNetworks = underlyingNetworksOrDefault(
+ agentCaps.getOwnerUid(), underlyingNetworks);
int[] transportTypes = agentCaps.getTransportTypes();
int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
@@ -6542,6 +6700,7 @@
* maintained here that the NetworkAgent is not aware of (e.g., validated, captive portal,
* and foreground status).
*/
+ @NonNull
private NetworkCapabilities mixInCapabilities(NetworkAgentInfo nai, NetworkCapabilities nc) {
// Once a NetworkAgent is connected, complain if some immutable capabilities are removed.
// Don't complain for VPNs since they're not driven by requests and there is no risk of
@@ -6598,6 +6757,25 @@
return newNc;
}
+ private void updateNetworkInfoForRoamingAndSuspended(NetworkAgentInfo nai,
+ NetworkCapabilities prevNc, NetworkCapabilities newNc) {
+ final boolean prevSuspended = !prevNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ final boolean suspended = !newNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ final boolean prevRoaming = !prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+ final boolean roaming = !newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+ if (prevSuspended != suspended) {
+ // TODO (b/73132094) : remove this call once the few users of onSuspended and
+ // onResumed have been removed.
+ notifyNetworkCallbacks(nai, suspended ? ConnectivityManager.CALLBACK_SUSPENDED
+ : ConnectivityManager.CALLBACK_RESUMED);
+ }
+ if (prevSuspended != suspended || prevRoaming != roaming) {
+ // updateNetworkInfo will mix in the suspended info from the capabilities and
+ // take appropriate action for the network having possibly changed state.
+ updateNetworkInfo(nai, nai.networkInfo);
+ }
+ }
+
/**
* Update the NetworkCapabilities for {@code nai} to {@code nc}. Specifically:
*
@@ -6629,25 +6807,13 @@
// on this network. We might have been called by rematchNetworkAndRequests when a
// network changed foreground state.
processListenRequests(nai);
- final boolean prevSuspended = !prevNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
- final boolean suspended = !newNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
- final boolean prevRoaming = !prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
- final boolean roaming = !newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
- if (prevSuspended != suspended || prevRoaming != roaming) {
- // TODO (b/73132094) : remove this call once the few users of onSuspended and
- // onResumed have been removed.
- notifyNetworkCallbacks(nai, suspended ? ConnectivityManager.CALLBACK_SUSPENDED
- : ConnectivityManager.CALLBACK_RESUMED);
- // updateNetworkInfo will mix in the suspended info from the capabilities and
- // take appropriate action for the network having possibly changed state.
- updateNetworkInfo(nai, nai.networkInfo);
- }
} else {
// If the requestable capabilities have changed or the score changed, we can't have been
// called by rematchNetworkAndRequests, so it's safe to start a rematch.
rematchAllNetworksAndRequests();
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
}
+ updateNetworkInfoForRoamingAndSuspended(nai, prevNc, newNc);
final boolean oldMetered = prevNc.isMetered();
final boolean newMetered = newNc.isMetered();
@@ -6841,12 +7007,45 @@
private void sendUpdatedScoreToFactories(NetworkAgentInfo nai) {
for (int i = 0; i < nai.numNetworkRequests(); i++) {
NetworkRequest nr = nai.requestAt(i);
- // Don't send listening requests to factories. b/17393458
- if (nr.isListen()) continue;
+ // Don't send listening or track default request to factories. b/17393458
+ if (!nr.isRequest()) continue;
sendUpdatedScoreToFactories(nr, nai);
}
}
+ private void sendUpdatedScoreToFactories(
+ @NonNull final NetworkReassignment.RequestReassignment event) {
+ // If a request of type REQUEST is now being satisfied by a new network.
+ if (null != event.mNewNetworkRequest && event.mNewNetworkRequest.isRequest()) {
+ sendUpdatedScoreToFactories(event.mNewNetworkRequest, event.mNewNetwork);
+ }
+
+ // If a previously satisfied request of type REQUEST is no longer being satisfied.
+ if (null != event.mOldNetworkRequest && event.mOldNetworkRequest.isRequest()
+ && event.mOldNetworkRequest != event.mNewNetworkRequest) {
+ sendUpdatedScoreToFactories(event.mOldNetworkRequest, null);
+ }
+
+ cancelMultilayerLowerPriorityNpiRequests(event.mNetworkRequestInfo);
+ }
+
+ /**
+ * Cancel with all NPIs the given NRI's multilayer requests that are a lower priority than
+ * its currently satisfied active request.
+ * @param nri the NRI to cancel lower priority requests for.
+ */
+ private void cancelMultilayerLowerPriorityNpiRequests(
+ @NonNull final NetworkRequestInfo nri) {
+ if (!nri.isMultilayerRequest() || null == nri.mActiveRequest) {
+ return;
+ }
+
+ final int indexOfNewRequest = nri.mRequests.indexOf(nri.mActiveRequest);
+ for (int i = indexOfNewRequest + 1; i < nri.mRequests.size(); i++) {
+ cancelNpiRequest(nri.mRequests.get(i));
+ }
+ }
+
private void sendUpdatedScoreToFactories(@NonNull NetworkRequest networkRequest,
@Nullable NetworkAgentInfo nai) {
final int score;
@@ -6867,21 +7066,35 @@
}
/** Sends all current NetworkRequests to the specified factory. */
- private void sendAllRequestsToProvider(NetworkProviderInfo npi) {
+ private void sendAllRequestsToProvider(@NonNull final NetworkProviderInfo npi) {
ensureRunningOnConnectivityServiceThread();
- for (NetworkRequestInfo nri : mNetworkRequests.values()) {
- if (nri.request.isListen()) continue;
- NetworkAgentInfo nai = nri.mSatisfier;
- final int score;
- final int serial;
- if (nai != null) {
- score = nai.getCurrentScore();
- serial = nai.factorySerialNumber;
- } else {
- score = 0;
- serial = NetworkProvider.ID_NONE;
+ for (final NetworkRequestInfo nri : getNrisFromGlobalRequests()) {
+ for (final NetworkRequest req : nri.mRequests) {
+ if (!req.isRequest() && nri.getActiveRequest() == req) {
+ break;
+ }
+ if (!req.isRequest()) {
+ continue;
+ }
+ // Only set the nai for the request it is satisfying.
+ final NetworkAgentInfo nai =
+ nri.getActiveRequest() == req ? nri.getSatisfier() : null;
+ final int score;
+ final int serial;
+ if (null != nai) {
+ score = nai.getCurrentScore();
+ serial = nai.factorySerialNumber;
+ } else {
+ score = 0;
+ serial = NetworkProvider.ID_NONE;
+ }
+ npi.requestNetwork(req, score, serial);
+ // For multilayer requests, don't send lower priority requests if a higher priority
+ // request is already satisfied.
+ if (null != nai) {
+ break;
+ }
}
- npi.requestNetwork(nri.request, score, serial);
}
}
@@ -6890,7 +7103,12 @@
if (notificationType == ConnectivityManager.CALLBACK_AVAILABLE && !nri.mPendingIntentSent) {
Intent intent = new Intent();
intent.putExtra(ConnectivityManager.EXTRA_NETWORK, networkAgent.network);
- intent.putExtra(ConnectivityManager.EXTRA_NETWORK_REQUEST, nri.request);
+ // If apps could file multi-layer requests with PendingIntents, they'd need to know
+ // which of the layer is satisfied alongside with some ID for the request. Hence, if
+ // such an API is ever implemented, there is no doubt the right request to send in
+ // EXTRA_NETWORK_REQUEST is mActiveRequest, and whatever ID would be added would need to
+ // be sent as a separate extra.
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK_REQUEST, nri.getActiveRequest());
nri.mPendingIntentSent = true;
sendIntent(nri.mPendingIntent, intent);
}
@@ -6920,8 +7138,9 @@
releasePendingNetworkRequestWithDelay(pendingIntent);
}
- private void callCallbackForRequest(NetworkRequestInfo nri,
- NetworkAgentInfo networkAgent, int notificationType, int arg1) {
+ private void callCallbackForRequest(@NonNull final NetworkRequestInfo nri,
+ @NonNull final NetworkAgentInfo networkAgent, final int notificationType,
+ final int arg1) {
if (nri.messenger == null) {
// Default request has no msgr. Also prevents callbacks from being invoked for
// NetworkRequestInfos registered with ConnectivityDiagnostics requests. Those callbacks
@@ -6929,8 +7148,14 @@
return;
}
Bundle bundle = new Bundle();
+ // In the case of multi-layer NRIs, the first request is not necessarily the one that
+ // is satisfied. This is vexing, but the ConnectivityManager code that receives this
+ // callback is only using the request as a token to identify the callback, so it doesn't
+ // matter too much at this point as long as the callback can be found.
+ // TODO b/177608132: make sure callbacks are indexed by NRIs and not NetworkRequest objects.
// TODO: check if defensive copies of data is needed.
- putParcelable(bundle, new NetworkRequest(nri.request));
+ final NetworkRequest nrForCallback = new NetworkRequest(nri.mRequests.get(0));
+ putParcelable(bundle, nrForCallback);
Message msg = Message.obtain();
if (notificationType != ConnectivityManager.CALLBACK_UNAVAIL) {
putParcelable(bundle, networkAgent.network);
@@ -6943,7 +7168,7 @@
putParcelable(
bundle,
createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- nc, nri.mUid, nri.request.getRequestorPackageName()));
+ nc, nri.mUid, nrForCallback.getRequestorPackageName()));
putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions(
networkAgent.linkProperties, nri.mPid, nri.mUid));
// For this notification, arg1 contains the blocked status.
@@ -6962,7 +7187,7 @@
putParcelable(
bundle,
createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- netCap, nri.mUid, nri.request.getRequestorPackageName()));
+ netCap, nri.mUid, nrForCallback.getRequestorPackageName()));
break;
}
case ConnectivityManager.CALLBACK_IP_CHANGED: {
@@ -6981,12 +7206,12 @@
try {
if (VDBG) {
String notification = ConnectivityManager.getCallbackName(notificationType);
- log("sending notification " + notification + " for " + nri.request);
+ log("sending notification " + notification + " for " + nrForCallback);
}
nri.messenger.send(msg);
} catch (RemoteException e) {
// may occur naturally in the race of binder death.
- loge("RemoteException caught trying to send a callback msg for " + nri.request);
+ loge("RemoteException caught trying to send a callback msg for " + nrForCallback);
}
}
@@ -6998,8 +7223,8 @@
if (nai.numRequestNetworkRequests() != 0) {
for (int i = 0; i < nai.numNetworkRequests(); i++) {
NetworkRequest nr = nai.requestAt(i);
- // Ignore listening requests.
- if (nr.isListen()) continue;
+ // Ignore listening and track default requests.
+ if (!nr.isRequest()) continue;
loge("Dead network still had at least " + nr);
break;
}
@@ -7016,42 +7241,132 @@
// If we get here it means that the last linger timeout for this network expired. So there
// must be no other active linger timers, and we must stop lingering.
- oldNetwork.clearLingerState();
+ oldNetwork.clearInactivityState();
if (unneeded(oldNetwork, UnneededFor.TEARDOWN)) {
// Tear the network down.
teardownUnneededNetwork(oldNetwork);
} else {
- // Put the network in the background.
+ // Put the network in the background if it doesn't satisfy any foreground request.
updateCapabilitiesForNetwork(oldNetwork);
}
}
- private void makeDefault(@Nullable final NetworkAgentInfo newNetwork) {
- if (DBG) log("Switching to new default network: " + newNetwork);
+ private void processDefaultNetworkChanges(@NonNull final NetworkReassignment changes) {
+ boolean isDefaultChanged = false;
+ for (final NetworkRequestInfo defaultRequestInfo : mDefaultNetworkRequests) {
+ final NetworkReassignment.RequestReassignment reassignment =
+ changes.getReassignment(defaultRequestInfo);
+ if (null == reassignment) {
+ continue;
+ }
+ // reassignment only contains those instances where the satisfying network changed.
+ isDefaultChanged = true;
+ // Notify system services of the new default.
+ makeDefault(defaultRequestInfo, reassignment.mOldNetwork, reassignment.mNewNetwork);
+ }
- mDefaultNetworkNai = newNetwork;
+ if (isDefaultChanged) {
+ // Hold a wakelock for a short time to help apps in migrating to a new default.
+ scheduleReleaseNetworkTransitionWakelock();
+ }
+ }
+ private void makeDefault(@NonNull final NetworkRequestInfo nri,
+ @Nullable final NetworkAgentInfo oldDefaultNetwork,
+ @Nullable final NetworkAgentInfo newDefaultNetwork) {
+ if (DBG) {
+ log("Switching to new default network for: " + nri + " using " + newDefaultNetwork);
+ }
+
+ // Fix up the NetworkCapabilities of any networks that have this network as underlying.
+ if (newDefaultNetwork != null) {
+ propagateUnderlyingNetworkCapabilities(newDefaultNetwork.network);
+ }
+
+ // Set an app level managed default and return since further processing only applies to the
+ // default network.
+ if (mDefaultRequest != nri) {
+ makeDefaultForApps(nri, oldDefaultNetwork, newDefaultNetwork);
+ return;
+ }
+
+ makeDefaultNetwork(newDefaultNetwork);
+
+ if (oldDefaultNetwork != null) {
+ mLingerMonitor.noteLingerDefaultNetwork(oldDefaultNetwork, newDefaultNetwork);
+ }
+ mNetworkActivityTracker.updateDataActivityTracking(newDefaultNetwork, oldDefaultNetwork);
+ notifyLockdownVpn(newDefaultNetwork);
+ handleApplyDefaultProxy(null != newDefaultNetwork
+ ? newDefaultNetwork.linkProperties.getHttpProxy() : null);
+ updateTcpBufferSizes(null != newDefaultNetwork
+ ? newDefaultNetwork.linkProperties.getTcpBufferSizes() : null);
+ notifyIfacesChangedForNetworkStats();
+
+ // Log 0 -> X and Y -> X default network transitions, where X is the new default.
+ final Network network = (newDefaultNetwork != null) ? newDefaultNetwork.network : null;
+ final int score = (newDefaultNetwork != null) ? newDefaultNetwork.getCurrentScore() : 0;
+ final boolean validated = newDefaultNetwork != null && newDefaultNetwork.lastValidated;
+ final LinkProperties lp = (newDefaultNetwork != null)
+ ? newDefaultNetwork.linkProperties : null;
+ final NetworkCapabilities nc = (newDefaultNetwork != null)
+ ? newDefaultNetwork.networkCapabilities : null;
+
+ final Network prevNetwork = (oldDefaultNetwork != null)
+ ? oldDefaultNetwork.network : null;
+ final int prevScore = (oldDefaultNetwork != null)
+ ? oldDefaultNetwork.getCurrentScore() : 0;
+ final LinkProperties prevLp = (oldDefaultNetwork != null)
+ ? oldDefaultNetwork.linkProperties : null;
+ final NetworkCapabilities prevNc = (oldDefaultNetwork != null)
+ ? oldDefaultNetwork.networkCapabilities : null;
+
+ mMetricsLog.logDefaultNetworkEvent(network, score, validated, lp, nc,
+ prevNetwork, prevScore, prevLp, prevNc);
+ }
+
+ private void makeDefaultForApps(@NonNull final NetworkRequestInfo nri,
+ @Nullable final NetworkAgentInfo oldDefaultNetwork,
+ @Nullable final NetworkAgentInfo newDefaultNetwork) {
try {
- if (null != newNetwork) {
- mNetd.networkSetDefault(newNetwork.network.getNetId());
+ if (VDBG) {
+ log("Setting default network for " + nri
+ + " using UIDs " + nri.getUids()
+ + " with old network " + (oldDefaultNetwork != null
+ ? oldDefaultNetwork.network().getNetId() : "null")
+ + " and new network " + (newDefaultNetwork != null
+ ? newDefaultNetwork.network().getNetId() : "null"));
+ }
+ if (nri.getUids().isEmpty()) {
+ throw new IllegalStateException("makeDefaultForApps called without specifying"
+ + " any applications to set as the default." + nri);
+ }
+ if (null != newDefaultNetwork) {
+ mNetd.networkAddUidRanges(
+ newDefaultNetwork.network.getNetId(),
+ toUidRangeStableParcels(nri.getUids()));
+ }
+ if (null != oldDefaultNetwork) {
+ mNetd.networkRemoveUidRanges(
+ oldDefaultNetwork.network.getNetId(),
+ toUidRangeStableParcels(nri.getUids()));
+ }
+ } catch (RemoteException | ServiceSpecificException e) {
+ loge("Exception setting OEM network preference default network :" + e);
+ }
+ }
+
+ private void makeDefaultNetwork(@Nullable final NetworkAgentInfo newDefaultNetwork) {
+ try {
+ if (null != newDefaultNetwork) {
+ mNetd.networkSetDefault(newDefaultNetwork.network.getNetId());
} else {
mNetd.networkClearDefault();
}
} catch (RemoteException | ServiceSpecificException e) {
loge("Exception setting default network :" + e);
}
-
- notifyLockdownVpn(newNetwork);
- handleApplyDefaultProxy(null != newNetwork
- ? newNetwork.linkProperties.getHttpProxy() : null);
- updateTcpBufferSizes(null != newNetwork
- ? newNetwork.linkProperties.getTcpBufferSizes() : null);
- notifyIfacesChangedForNetworkStats();
- // Fix up the NetworkCapabilities of any networks that have this network as underlying.
- if (newNetwork != null) {
- propagateUnderlyingNetworkCapabilities(newNetwork.network);
- }
}
private void processListenRequests(@NonNull final NetworkAgentInfo nai) {
@@ -7062,19 +7377,25 @@
}
private void processNewlyLostListenRequests(@NonNull final NetworkAgentInfo nai) {
- for (NetworkRequestInfo nri : mNetworkRequests.values()) {
- NetworkRequest nr = nri.request;
+ for (final NetworkRequestInfo nri : mNetworkRequests.values()) {
+ if (nri.isMultilayerRequest()) {
+ continue;
+ }
+ final NetworkRequest nr = nri.mRequests.get(0);
if (!nr.isListen()) continue;
if (nai.isSatisfyingRequest(nr.requestId) && !nai.satisfies(nr)) {
- nai.removeRequest(nri.request.requestId);
+ nai.removeRequest(nr.requestId);
callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_LOST, 0);
}
}
}
private void processNewlySatisfiedListenRequests(@NonNull final NetworkAgentInfo nai) {
- for (NetworkRequestInfo nri : mNetworkRequests.values()) {
- NetworkRequest nr = nri.request;
+ for (final NetworkRequestInfo nri : mNetworkRequests.values()) {
+ if (nri.isMultilayerRequest()) {
+ continue;
+ }
+ final NetworkRequest nr = nri.mRequests.get(0);
if (!nr.isListen()) continue;
if (nai.satisfies(nr) && !nai.isSatisfyingRequest(nr.requestId)) {
nai.addRequest(nr);
@@ -7086,19 +7407,25 @@
// An accumulator class to gather the list of changes that result from a rematch.
private static class NetworkReassignment {
static class RequestReassignment {
- @NonNull public final NetworkRequestInfo mRequest;
+ @NonNull public final NetworkRequestInfo mNetworkRequestInfo;
+ @NonNull public final NetworkRequest mOldNetworkRequest;
+ @NonNull public final NetworkRequest mNewNetworkRequest;
@Nullable public final NetworkAgentInfo mOldNetwork;
@Nullable public final NetworkAgentInfo mNewNetwork;
- RequestReassignment(@NonNull final NetworkRequestInfo request,
+ RequestReassignment(@NonNull final NetworkRequestInfo networkRequestInfo,
+ @NonNull final NetworkRequest oldNetworkRequest,
+ @NonNull final NetworkRequest newNetworkRequest,
@Nullable final NetworkAgentInfo oldNetwork,
@Nullable final NetworkAgentInfo newNetwork) {
- mRequest = request;
+ mNetworkRequestInfo = networkRequestInfo;
+ mOldNetworkRequest = oldNetworkRequest;
+ mNewNetworkRequest = newNetworkRequest;
mOldNetwork = oldNetwork;
mNewNetwork = newNetwork;
}
public String toString() {
- return mRequest.mRequests.get(0).requestId + " : "
+ return mNetworkRequestInfo.mRequests.get(0).requestId + " : "
+ (null != mOldNetwork ? mOldNetwork.network.getNetId() : "null")
+ " → " + (null != mNewNetwork ? mNewNetwork.network.getNetId() : "null");
}
@@ -7116,7 +7443,7 @@
// sure this stays true, but without imposing this expensive check on all
// reassignments on all user devices.
for (final RequestReassignment existing : mReassignments) {
- if (existing.mRequest.equals(reassignment.mRequest)) {
+ if (existing.mNetworkRequestInfo.equals(reassignment.mNetworkRequestInfo)) {
throw new IllegalStateException("Trying to reassign ["
+ reassignment + "] but already have ["
+ existing + "]");
@@ -7131,7 +7458,7 @@
@Nullable
private RequestReassignment getReassignment(@NonNull final NetworkRequestInfo nri) {
for (final RequestReassignment event : getRequestReassignments()) {
- if (nri == event.mRequest) return event;
+ if (nri == event.mNetworkRequestInfo) return event;
}
return null;
}
@@ -7158,67 +7485,121 @@
}
private void updateSatisfiersForRematchRequest(@NonNull final NetworkRequestInfo nri,
+ @NonNull final NetworkRequest previousRequest,
+ @NonNull final NetworkRequest newRequest,
@Nullable final NetworkAgentInfo previousSatisfier,
@Nullable final NetworkAgentInfo newSatisfier,
final long now) {
- if (newSatisfier != null) {
+ if (null != newSatisfier && mNoServiceNetwork != newSatisfier) {
if (VDBG) log("rematch for " + newSatisfier.toShortString());
- if (previousSatisfier != null) {
+ if (null != previousSatisfier && mNoServiceNetwork != previousSatisfier) {
if (VDBG || DDBG) {
log(" accepting network in place of " + previousSatisfier.toShortString());
}
- previousSatisfier.removeRequest(nri.request.requestId);
- previousSatisfier.lingerRequest(nri.request.requestId, now, mLingerDelayMs);
+ previousSatisfier.removeRequest(previousRequest.requestId);
+ previousSatisfier.lingerRequest(previousRequest.requestId, now, mLingerDelayMs);
} else {
if (VDBG || DDBG) log(" accepting network in place of null");
}
- newSatisfier.unlingerRequest(nri.request.requestId);
- if (!newSatisfier.addRequest(nri.request)) {
- Log.wtf(TAG, "BUG: " + newSatisfier.toShortString() + " already has "
- + nri.request);
+
+ // To prevent constantly CPU wake up for nascent timer, if a network comes up
+ // and immediately satisfies a request then remove the timer. This will happen for
+ // all networks except in the case of an underlying network for a VCN.
+ if (newSatisfier.isNascent()) {
+ newSatisfier.unlingerRequest(NetworkRequest.REQUEST_ID_NONE);
}
- } else {
+
+ newSatisfier.unlingerRequest(newRequest.requestId);
+ if (!newSatisfier.addRequest(newRequest)) {
+ Log.wtf(TAG, "BUG: " + newSatisfier.toShortString() + " already has "
+ + newRequest);
+ }
+ } else if (null != previousSatisfier) {
if (DBG) {
log("Network " + previousSatisfier.toShortString() + " stopped satisfying"
- + " request " + nri.request.requestId);
+ + " request " + previousRequest.requestId);
}
- previousSatisfier.removeRequest(nri.request.requestId);
+ previousSatisfier.removeRequest(previousRequest.requestId);
}
- nri.mSatisfier = newSatisfier;
+ nri.setSatisfier(newSatisfier, newRequest);
}
+ /**
+ * This function is triggered when something can affect what network should satisfy what
+ * request, and it computes the network reassignment from the passed collection of requests to
+ * network match to the one that the system should now have. That data is encoded in an
+ * object that is a list of changes, each of them having an NRI, and old satisfier, and a new
+ * satisfier.
+ *
+ * After the reassignment is computed, it is applied to the state objects.
+ *
+ * @param networkRequests the nri objects to evaluate for possible network reassignment
+ * @return NetworkReassignment listing of proposed network assignment changes
+ */
@NonNull
- private NetworkReassignment computeNetworkReassignment() {
- ensureRunningOnConnectivityServiceThread();
+ private NetworkReassignment computeNetworkReassignment(
+ @NonNull final Collection<NetworkRequestInfo> networkRequests) {
final NetworkReassignment changes = new NetworkReassignment();
// Gather the list of all relevant agents and sort them by score.
final ArrayList<NetworkAgentInfo> nais = new ArrayList<>();
for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
- if (!nai.everConnected) continue;
+ if (!nai.everConnected) {
+ continue;
+ }
nais.add(nai);
}
- for (final NetworkRequestInfo nri : mNetworkRequests.values()) {
- if (nri.request.isListen()) continue;
- final NetworkAgentInfo bestNetwork = mNetworkRanker.getBestNetwork(nri.request, nais);
- if (bestNetwork != nri.mSatisfier) {
+ for (final NetworkRequestInfo nri : networkRequests) {
+ // Non-multilayer listen requests can be ignored.
+ if (!nri.isMultilayerRequest() && nri.mRequests.get(0).isListen()) {
+ continue;
+ }
+ NetworkAgentInfo bestNetwork = null;
+ NetworkRequest bestRequest = null;
+ for (final NetworkRequest req : nri.mRequests) {
+ bestNetwork = mNetworkRanker.getBestNetwork(req, nais);
+ // Stop evaluating as the highest possible priority request is satisfied.
+ if (null != bestNetwork) {
+ bestRequest = req;
+ break;
+ }
+ }
+ if (null == bestNetwork && isDefaultBlocked(nri)) {
+ // Remove default networking if disallowed for managed default requests.
+ bestNetwork = mNoServiceNetwork;
+ }
+ if (nri.getSatisfier() != bestNetwork) {
// bestNetwork may be null if no network can satisfy this request.
changes.addRequestReassignment(new NetworkReassignment.RequestReassignment(
- nri, nri.mSatisfier, bestNetwork));
+ nri, nri.mActiveRequest, bestRequest, nri.getSatisfier(), bestNetwork));
}
}
return changes;
}
+ private Set<NetworkRequestInfo> getNrisFromGlobalRequests() {
+ return new HashSet<>(mNetworkRequests.values());
+ }
+
/**
- * Attempt to rematch all Networks with NetworkRequests. This may result in Networks
+ * Attempt to rematch all Networks with all NetworkRequests. This may result in Networks
* being disconnected.
*/
private void rematchAllNetworksAndRequests() {
+ rematchNetworksAndRequests(getNrisFromGlobalRequests());
+ }
+
+ /**
+ * Attempt to rematch all Networks with given NetworkRequests. This may result in Networks
+ * being disconnected.
+ */
+ private void rematchNetworksAndRequests(
+ @NonNull final Set<NetworkRequestInfo> networkRequests) {
+ ensureRunningOnConnectivityServiceThread();
// TODO: This may be slow, and should be optimized.
final long now = SystemClock.elapsedRealtime();
- final NetworkReassignment changes = computeNetworkReassignment();
+ final NetworkReassignment changes = computeNetworkReassignment(networkRequests);
if (VDBG || DDBG) {
log(changes.debugString());
} else if (DBG) {
@@ -7243,50 +7624,14 @@
// the linger status.
for (final NetworkReassignment.RequestReassignment event :
changes.getRequestReassignments()) {
- updateSatisfiersForRematchRequest(event.mRequest, event.mOldNetwork,
- event.mNewNetwork, now);
+ updateSatisfiersForRematchRequest(event.mNetworkRequestInfo,
+ event.mOldNetworkRequest, event.mNewNetworkRequest,
+ event.mOldNetwork, event.mNewNetwork,
+ now);
}
- final NetworkAgentInfo oldDefaultNetwork = getDefaultNetwork();
- final NetworkRequestInfo defaultRequestInfo = mNetworkRequests.get(mDefaultRequest);
- final NetworkReassignment.RequestReassignment reassignment =
- changes.getReassignment(defaultRequestInfo);
- final NetworkAgentInfo newDefaultNetwork =
- null != reassignment ? reassignment.mNewNetwork : oldDefaultNetwork;
-
- if (oldDefaultNetwork != newDefaultNetwork) {
- if (oldDefaultNetwork != null) {
- mLingerMonitor.noteLingerDefaultNetwork(oldDefaultNetwork, newDefaultNetwork);
- }
- mNetworkActivityTracker.updateDataActivityTracking(
- newDefaultNetwork, oldDefaultNetwork);
- // Notify system services of the new default.
- makeDefault(newDefaultNetwork);
-
- // Log 0 -> X and Y -> X default network transitions, where X is the new default.
- final Network network = (newDefaultNetwork != null) ? newDefaultNetwork.network : null;
- final int score = (newDefaultNetwork != null) ? newDefaultNetwork.getCurrentScore() : 0;
- final boolean validated = newDefaultNetwork != null && newDefaultNetwork.lastValidated;
- final LinkProperties lp = (newDefaultNetwork != null)
- ? newDefaultNetwork.linkProperties : null;
- final NetworkCapabilities nc = (newDefaultNetwork != null)
- ? newDefaultNetwork.networkCapabilities : null;
-
- final Network prevNetwork = (oldDefaultNetwork != null)
- ? oldDefaultNetwork.network : null;
- final int prevScore = (oldDefaultNetwork != null)
- ? oldDefaultNetwork.getCurrentScore() : 0;
- final LinkProperties prevLp = (oldDefaultNetwork != null)
- ? oldDefaultNetwork.linkProperties : null;
- final NetworkCapabilities prevNc = (oldDefaultNetwork != null)
- ? oldDefaultNetwork.networkCapabilities : null;
-
- mMetricsLog.logDefaultNetworkEvent(network, score, validated, lp, nc,
- prevNetwork, prevScore, prevLp, prevNc);
-
- // Have a new default network, release the transition wakelock in
- scheduleReleaseNetworkTransitionWakelock();
- }
+ // Process default network changes if applicable.
+ processDefaultNetworkChanges(changes);
// Notify requested networks are available after the default net is switched, but
// before LegacyTypeTracker sends legacy broadcasts
@@ -7296,29 +7641,29 @@
// trying to connect if they know they cannot match it.
// TODO - this could get expensive if there are a lot of outstanding requests for this
// network. Think of a way to reduce this. Push netid->request mapping to each factory?
- sendUpdatedScoreToFactories(event.mRequest.request, event.mNewNetwork);
+ sendUpdatedScoreToFactories(event);
if (null != event.mNewNetwork) {
- notifyNetworkAvailable(event.mNewNetwork, event.mRequest);
+ notifyNetworkAvailable(event.mNewNetwork, event.mNetworkRequestInfo);
} else {
- callCallbackForRequest(event.mRequest, event.mOldNetwork,
+ callCallbackForRequest(event.mNetworkRequestInfo, event.mOldNetwork,
ConnectivityManager.CALLBACK_LOST, 0);
}
}
- // Update the linger state before processing listen callbacks, because the background
- // computation depends on whether the network is lingering. Don't send the LOSING callbacks
+ // Update the inactivity state before processing listen callbacks, because the background
+ // computation depends on whether the network is inactive. Don't send the LOSING callbacks
// just yet though, because they have to be sent after the listens are processed to keep
// backward compatibility.
- final ArrayList<NetworkAgentInfo> lingeredNetworks = new ArrayList<>();
+ final ArrayList<NetworkAgentInfo> inactiveNetworks = new ArrayList<>();
for (final NetworkAgentInfo nai : nais) {
- // Rematching may have altered the linger state of some networks, so update all linger
- // timers. updateLingerState reads the state from the network agent and does nothing
- // if the state has not changed : the source of truth is controlled with
- // NetworkAgentInfo#lingerRequest and NetworkAgentInfo#unlingerRequest, which have been
- // called while rematching the individual networks above.
- if (updateLingerState(nai, now)) {
- lingeredNetworks.add(nai);
+ // Rematching may have altered the inactivity state of some networks, so update all
+ // inactivity timers. updateInactivityState reads the state from the network agent
+ // and does nothing if the state has not changed : the source of truth is controlled
+ // with NetworkAgentInfo#lingerRequest and NetworkAgentInfo#unlingerRequest, which
+ // have been called while rematching the individual networks above.
+ if (updateInactivityState(nai, now)) {
+ inactiveNetworks.add(nai);
}
}
@@ -7335,16 +7680,20 @@
processNewlySatisfiedListenRequests(nai);
}
- for (final NetworkAgentInfo nai : lingeredNetworks) {
+ for (final NetworkAgentInfo nai : inactiveNetworks) {
+ // For nascent networks, if connecting with no foreground request, skip broadcasting
+ // LOSING for backward compatibility. This is typical when mobile data connected while
+ // wifi connected with mobile data always-on enabled.
+ if (nai.isNascent()) continue;
notifyNetworkLosing(nai, now);
}
- updateLegacyTypeTrackerAndVpnLockdownForRematch(oldDefaultNetwork, newDefaultNetwork, nais);
+ updateLegacyTypeTrackerAndVpnLockdownForRematch(changes, nais);
// Tear down all unneeded networks.
for (NetworkAgentInfo nai : mNetworkAgentInfos) {
if (unneeded(nai, UnneededFor.TEARDOWN)) {
- if (nai.getLingerExpiry() > 0) {
+ if (nai.getInactivityExpiry() > 0) {
// This network has active linger timers and no requests, but is not
// lingering. Linger it.
//
@@ -7352,7 +7701,7 @@
// and became unneeded due to another network improving its score to the
// point where this network will no longer be able to satisfy any requests
// even if it validates.
- if (updateLingerState(nai, now)) {
+ if (updateInactivityState(nai, now)) {
notifyNetworkLosing(nai, now);
}
} else {
@@ -7382,9 +7731,15 @@
}
private void updateLegacyTypeTrackerAndVpnLockdownForRematch(
- @Nullable final NetworkAgentInfo oldDefaultNetwork,
- @Nullable final NetworkAgentInfo newDefaultNetwork,
+ @NonNull final NetworkReassignment changes,
@NonNull final Collection<NetworkAgentInfo> nais) {
+ final NetworkReassignment.RequestReassignment reassignmentOfDefault =
+ changes.getReassignment(mDefaultRequest);
+ final NetworkAgentInfo oldDefaultNetwork =
+ null != reassignmentOfDefault ? reassignmentOfDefault.mOldNetwork : null;
+ final NetworkAgentInfo newDefaultNetwork =
+ null != reassignmentOfDefault ? reassignmentOfDefault.mNewNetwork : null;
+
if (oldDefaultNetwork != newDefaultNetwork) {
// Maintain the illusion : since the legacy API only understands one network at a time,
// if the default network changed, apps should see a disconnected broadcast for the
@@ -7398,7 +7753,8 @@
// network doesn't satisfy the default request any more because it lost a
// capability.
mDefaultInetConditionPublished = newDefaultNetwork.lastValidated ? 100 : 0;
- mLegacyTypeTracker.add(newDefaultNetwork.networkInfo.getType(), newDefaultNetwork);
+ mLegacyTypeTracker.add(
+ newDefaultNetwork.networkInfo.getType(), newDefaultNetwork);
// If the legacy VPN is connected, notifyLockdownVpn may end up sending a broadcast
// to reflect the NetworkInfo of this new network. This broadcast has to be sent
// after the disconnect broadcasts above, but before the broadcasts sent by the
@@ -7450,7 +7806,7 @@
private void updateInetCondition(NetworkAgentInfo nai) {
// Don't bother updating until we've graduated to validated at least once.
if (!nai.everValidated) return;
- // For now only update icons for default connection.
+ // For now only update icons for the default connection.
// TODO: Update WiFi and cellular icons separately. b/17237507
if (!isDefaultNetwork(nai)) return;
@@ -7569,6 +7925,15 @@
// doing.
updateSignalStrengthThresholds(networkAgent, "CONNECT", null);
+ // Before first rematching networks, put an inactivity timer without any request, this
+ // allows {@code updateInactivityState} to update the state accordingly and prevent
+ // tearing down for any {@code unneeded} evaluation in this period.
+ // Note that the timer will not be rescheduled since the expiry time is
+ // fixed after connection regardless of the network satisfying other requests or not.
+ // But it will be removed as soon as the network satisfies a request for the first time.
+ networkAgent.lingerRequest(NetworkRequest.REQUEST_ID_NONE,
+ SystemClock.elapsedRealtime(), mNascentDelayMs);
+
// Consider network even though it is not yet validated.
rematchAllNetworksAndRequests();
@@ -7622,7 +7987,7 @@
// Notify the requests on this NAI that the network is now lingered.
private void notifyNetworkLosing(@NonNull final NetworkAgentInfo nai, final long now) {
- final int lingerTime = (int) (nai.getLingerExpiry() - now);
+ final int lingerTime = (int) (nai.getInactivityExpiry() - now);
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING, lingerTime);
}
@@ -7720,8 +8085,8 @@
intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo());
}
NetworkAgentInfo newDefaultAgent = null;
- if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) {
- newDefaultAgent = getDefaultNetwork();
+ if (nai.isSatisfyingRequest(mDefaultRequest.mRequests.get(0).requestId)) {
+ newDefaultAgent = mDefaultRequest.getSatisfier();
if (newDefaultAgent != null) {
intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO,
newDefaultAgent.networkInfo);
@@ -7768,10 +8133,15 @@
*/
private Network[] getDefaultNetworks() {
ensureRunningOnConnectivityServiceThread();
- ArrayList<Network> defaultNetworks = new ArrayList<>();
- NetworkAgentInfo defaultNetwork = getDefaultNetwork();
+ final ArrayList<Network> defaultNetworks = new ArrayList<>();
+ final Set<Integer> activeNetIds = new ArraySet<>();
+ for (final NetworkRequestInfo nri : mDefaultNetworkRequests) {
+ if (nri.isBeingSatisfied()) {
+ activeNetIds.add(nri.getSatisfier().network().netId);
+ }
+ }
for (NetworkAgentInfo nai : mNetworkAgentInfos) {
- if (nai.everConnected && (nai == defaultNetwork || nai.isVPN())) {
+ if (nai.everConnected && (activeNetIds.contains(nai.network().netId) || nai.isVPN())) {
defaultNetworks.add(nai.network);
}
}
@@ -7790,10 +8160,10 @@
activeIface = activeLinkProperties.getInterfaceName();
}
- final VpnInfo[] vpnInfos = getAllVpnInfo();
+ final UnderlyingNetworkInfo[] underlyingNetworkInfos = getAllVpnInfo();
try {
- mStatsService.forceUpdateIfaces(
- getDefaultNetworks(), getAllNetworkState(), activeIface, vpnInfos);
+ mStatsService.forceUpdateIfaces(getDefaultNetworks(), getAllNetworkState(), activeIface,
+ underlyingNetworkInfos);
} catch (Exception ignored) {
}
}
@@ -7821,7 +8191,6 @@
int user = UserHandle.getUserId(mDeps.getCallingUid());
final boolean success;
synchronized (mVpns) {
- throwIfLockdownEnabled();
success = mVpns.get(user).setUnderlyingNetworks(networks);
}
return success;
@@ -8053,6 +8422,7 @@
return getVpnIfOwner(mDeps.getCallingUid());
}
+ // TODO: stop calling into Vpn.java and get this information from data in this class.
@GuardedBy("mVpns")
private Vpn getVpnIfOwner(int uid) {
final int user = UserHandle.getUserId(uid);
@@ -8061,7 +8431,7 @@
if (vpn == null) {
return null;
} else {
- final VpnInfo info = vpn.getVpnInfo();
+ final UnderlyingNetworkInfo info = vpn.getUnderlyingNetworkInfo();
return (info == null || info.ownerUid != uid) ? null : vpn;
}
}
@@ -8103,7 +8473,7 @@
throw new IllegalArgumentException("Unsupported protocol " + connectionInfo.protocol);
}
- final int uid = InetDiagMessage.getConnectionOwnerUid(connectionInfo.protocol,
+ final int uid = mDeps.getConnectionOwnerUid(connectionInfo.protocol,
connectionInfo.local, connectionInfo.remote);
/* Filter out Uids not associated with the VPN. */
@@ -8754,6 +9124,7 @@
}
}
}
+
/**
* Registers {@link QosSocketFilter} with {@link IQosCallback}.
*
@@ -8803,4 +9174,10 @@
public void unregisterQosCallback(@NonNull final IQosCallback callback) {
mQosCallbackTracker.unregisterCallback(callback);
}
+
+ @Override
+ public void setOemNetworkPreference(@NonNull final OemNetworkPreferences preference) {
+ // TODO http://b/176495594 track multiple default networks with networkPreferences
+ if (DBG) log("setOemNetworkPreference() called with: " + preference.toString());
+ }
}
diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java
index f2b63a6..88ce220 100644
--- a/services/core/java/com/android/server/DynamicSystemService.java
+++ b/services/core/java/com/android/server/DynamicSystemService.java
@@ -22,7 +22,6 @@
import android.gsi.GsiProgress;
import android.gsi.IGsiService;
import android.gsi.IGsiServiceCallback;
-import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -30,7 +29,7 @@
import android.os.UserHandle;
import android.os.image.IDynamicSystemService;
import android.os.storage.StorageManager;
-import android.os.storage.StorageVolume;
+import android.os.storage.VolumeInfo;
import android.util.Slog;
import java.io.File;
@@ -88,16 +87,17 @@
String path = SystemProperties.get("os.aot.path");
if (path.isEmpty()) {
final int userId = UserHandle.myUserId();
- final StorageVolume[] volumes =
- StorageManager.getVolumeList(userId, StorageManager.FLAG_FOR_WRITE);
- for (StorageVolume volume : volumes) {
- if (volume.isEmulated()) continue;
- if (!volume.isRemovable()) continue;
- if (!Environment.MEDIA_MOUNTED.equals(volume.getState())) continue;
- File sdCard = volume.getPathFile();
- if (sdCard.isDirectory()) {
- path = new File(sdCard, dsuSlot).getPath();
- break;
+ final StorageManager sm = mContext.getSystemService(StorageManager.class);
+ for (VolumeInfo volume : sm.getVolumes()) {
+ if (volume.getType() != volume.TYPE_PUBLIC) {
+ continue;
+ }
+ if (!volume.isMountedWritable()) {
+ continue;
+ }
+ File sd_internal = volume.getInternalPathForUser(userId);
+ if (sd_internal != null) {
+ path = new File(sd_internal, dsuSlot).getPath();
}
}
if (path.isEmpty()) {
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index e8687e5..96f832d 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -32,6 +32,7 @@
import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.NetworkProvider;
+import android.net.NetworkStack;
import android.net.RouteInfo;
import android.net.StringNetworkSpecifier;
import android.net.TestNetworkInterface;
@@ -48,6 +49,8 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.NetdUtils;
+import com.android.net.module.util.NetworkStackConstants;
import java.io.UncheckedIOException;
import java.net.Inet4Address;
@@ -242,6 +245,7 @@
nc.addTransportType(NetworkCapabilities.TRANSPORT_TEST);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+ nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
nc.setNetworkSpecifier(new StringNetworkSpecifier(iface));
nc.setAdministratorUids(administratorUids);
if (!isMetered) {
@@ -277,10 +281,12 @@
// Add global routes (but as non-default, non-internet providing network)
if (allowIPv4) {
- lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null, iface));
+ lp.addRoute(new RouteInfo(new IpPrefix(
+ NetworkStackConstants.IPV4_ADDR_ANY, 0), null, iface));
}
if (allowIPv6) {
- lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null, iface));
+ lp.addRoute(new RouteInfo(new IpPrefix(
+ NetworkStackConstants.IPV6_ADDR_ANY, 0), null, iface));
}
final TestNetworkAgent agent = new TestNetworkAgent(context, looper, nc, lp,
@@ -316,10 +322,10 @@
}
try {
- // This requires NETWORK_STACK privileges.
final long token = Binder.clearCallingIdentity();
try {
- mNMS.setInterfaceUp(iface);
+ NetworkStack.checkNetworkStackPermission(mContext);
+ NetdUtils.setInterfaceUp(mNetd, iface);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 76db019..4dce59f 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -25,9 +25,14 @@
import android.app.AppOpsManager;
import android.content.Context;
import android.net.ConnectivityManager;
+import android.net.LinkProperties;
+import android.net.NetworkCapabilities;
+import android.net.TelephonyNetworkSpecifier;
import android.net.vcn.IVcnManagementService;
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
import android.net.vcn.VcnConfig;
+import android.net.vcn.VcnUnderlyingNetworkPolicy;
+import android.net.wifi.WifiInfo;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
@@ -61,6 +66,7 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
@@ -285,8 +291,16 @@
public Vcn newVcn(
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
- @NonNull VcnConfig config) {
- return new Vcn(vcnContext, subscriptionGroup, config);
+ @NonNull VcnConfig config,
+ @NonNull TelephonySubscriptionSnapshot snapshot,
+ @NonNull VcnSafemodeCallback safemodeCallback) {
+ return new Vcn(vcnContext, subscriptionGroup, config, snapshot, safemodeCallback);
+ }
+
+ /** Gets the subId indicated by the given {@link WifiInfo}. */
+ public int getSubIdForWifiInfo(@NonNull WifiInfo wifiInfo) {
+ // TODO(b/178501049): use the subId indicated by WifiInfo#getSubscriptionId
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
}
}
@@ -371,6 +385,7 @@
// delay)
for (Entry<ParcelUuid, Vcn> entry : mVcns.entrySet()) {
final VcnConfig config = mConfigs.get(entry.getKey());
+
if (config == null
|| !snapshot.packageHasPermissionsForSubscriptionGroup(
entry.getKey(), config.getProvisioningPackageName())) {
@@ -384,10 +399,13 @@
// correct instance is torn down. This could happen as a result of a
// Carrier App manually removing/adding a VcnConfig.
if (mVcns.get(uuidToTeardown) == instanceToTeardown) {
- mVcns.remove(uuidToTeardown).teardownAsynchronously();
+ stopVcnLocked(uuidToTeardown);
}
}
}, instanceToTeardown, CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
+ } else {
+ // If this VCN's status has not changed, update it with the new snapshot
+ entry.getValue().updateSubscriptionSnapshot(mLastSnapshot);
}
}
}
@@ -395,14 +413,44 @@
}
@GuardedBy("mLock")
+ private void stopVcnLocked(@NonNull ParcelUuid uuidToTeardown) {
+ final Vcn vcnToTeardown = mVcns.remove(uuidToTeardown);
+ if (vcnToTeardown == null) {
+ return;
+ }
+
+ vcnToTeardown.teardownAsynchronously();
+
+ // Now that the VCN is removed, notify all registered listeners to refresh their
+ // UnderlyingNetworkPolicy.
+ notifyAllPolicyListenersLocked();
+ }
+
+ @GuardedBy("mLock")
+ private void notifyAllPolicyListenersLocked() {
+ for (final PolicyListenerBinderDeath policyListener : mRegisteredPolicyListeners.values()) {
+ Binder.withCleanCallingIdentity(() -> policyListener.mListener.onPolicyChanged());
+ }
+ }
+
+ @GuardedBy("mLock")
private void startVcnLocked(@NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config) {
Slog.v(TAG, "Starting VCN config for subGrp: " + subscriptionGroup);
// TODO(b/176939047): Support multiple VCNs active at the same time, or limit to one active
// VCN.
- final Vcn newInstance = mDeps.newVcn(mVcnContext, subscriptionGroup, config);
+ final VcnSafemodeCallbackImpl safemodeCallback =
+ new VcnSafemodeCallbackImpl(subscriptionGroup);
+
+ final Vcn newInstance =
+ mDeps.newVcn(
+ mVcnContext, subscriptionGroup, config, mLastSnapshot, safemodeCallback);
mVcns.put(subscriptionGroup, newInstance);
+
+ // Now that a new VCN has started, notify all registered listeners to refresh their
+ // UnderlyingNetworkPolicy.
+ notifyAllPolicyListenersLocked();
}
@GuardedBy("mLock")
@@ -465,9 +513,7 @@
synchronized (mLock) {
mConfigs.remove(subscriptionGroup);
- if (mVcns.containsKey(subscriptionGroup)) {
- mVcns.remove(subscriptionGroup).teardownAsynchronously();
- }
+ stopVcnLocked(subscriptionGroup);
writeConfigsToDiskLocked();
}
@@ -497,7 +543,7 @@
}
}
- /** Get current configuration list for testing purposes */
+ /** Get current VCNs for testing purposes */
@VisibleForTesting(visibility = Visibility.PRIVATE)
public Map<ParcelUuid, Vcn> getAllVcns() {
synchronized (mLock) {
@@ -527,7 +573,7 @@
@NonNull IVcnUnderlyingNetworkPolicyListener listener) {
requireNonNull(listener, "listener was null");
- mContext.enforceCallingPermission(
+ mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.NETWORK_FACTORY,
"Must have permission NETWORK_FACTORY to register a policy listener");
@@ -561,4 +607,82 @@
}
}
}
+
+ /**
+ * Gets the UnderlyingNetworkPolicy as determined by the provided NetworkCapabilities and
+ * LinkProperties.
+ */
+ @NonNull
+ @Override
+ public VcnUnderlyingNetworkPolicy getUnderlyingNetworkPolicy(
+ @NonNull NetworkCapabilities networkCapabilities,
+ @NonNull LinkProperties linkProperties) {
+ requireNonNull(networkCapabilities, "networkCapabilities was null");
+ requireNonNull(linkProperties, "linkProperties was null");
+
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.NETWORK_FACTORY,
+ "Must have permission NETWORK_FACTORY or be the SystemServer to get underlying"
+ + " Network policies");
+
+ // Defensive copy in case this call is in-process and the given NetworkCapabilities mutates
+ networkCapabilities = new NetworkCapabilities(networkCapabilities);
+
+ int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
+ && networkCapabilities.getNetworkSpecifier() instanceof TelephonyNetworkSpecifier) {
+ TelephonyNetworkSpecifier telephonyNetworkSpecifier =
+ (TelephonyNetworkSpecifier) networkCapabilities.getNetworkSpecifier();
+ subId = telephonyNetworkSpecifier.getSubscriptionId();
+ } else if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
+ && networkCapabilities.getTransportInfo() instanceof WifiInfo) {
+ WifiInfo wifiInfo = (WifiInfo) networkCapabilities.getTransportInfo();
+ subId = mDeps.getSubIdForWifiInfo(wifiInfo);
+ }
+
+ boolean isVcnManagedNetwork = false;
+ if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ synchronized (mLock) {
+ ParcelUuid subGroup = mLastSnapshot.getGroupForSubId(subId);
+
+ Vcn vcn = mVcns.get(subGroup);
+ if (vcn != null && vcn.isActive()) {
+ isVcnManagedNetwork = true;
+ }
+ }
+ }
+ if (isVcnManagedNetwork) {
+ networkCapabilities.removeCapability(
+ NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
+ }
+
+ return new VcnUnderlyingNetworkPolicy(false /* isTearDownRequested */, networkCapabilities);
+ }
+
+ /** Callback for signalling when a Vcn has entered Safemode. */
+ public interface VcnSafemodeCallback {
+ /** Called by a Vcn to signal that it has entered Safemode. */
+ void onEnteredSafemode();
+ }
+
+ /** VcnSafemodeCallback is used by Vcns to notify VcnManagementService on entering Safemode. */
+ private class VcnSafemodeCallbackImpl implements VcnSafemodeCallback {
+ @NonNull private final ParcelUuid mSubGroup;
+
+ private VcnSafemodeCallbackImpl(@NonNull final ParcelUuid subGroup) {
+ mSubGroup = Objects.requireNonNull(subGroup, "Missing subGroup");
+ }
+
+ @Override
+ public void onEnteredSafemode() {
+ synchronized (mLock) {
+ // Ignore if this subscription group doesn't exist anymore
+ if (!mVcns.containsKey(mSubGroup)) {
+ return;
+ }
+
+ notifyAllPolicyListenersLocked();
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 630548d..a847847 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -31,6 +31,7 @@
import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ServiceDebugInfo;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
@@ -109,7 +110,6 @@
};
public static final List<String> HAL_INTERFACES_OF_INTEREST = Arrays.asList(
- "android.hardware.audio@2.0::IDevicesFactory",
"android.hardware.audio@4.0::IDevicesFactory",
"android.hardware.audio@5.0::IDevicesFactory",
"android.hardware.audio@6.0::IDevicesFactory",
@@ -135,6 +135,11 @@
"android.system.suspend@1.0::ISystemSuspend"
);
+ public static final String[] AIDL_INTERFACE_PREFIXES_OF_INTEREST = new String[] {
+ "android.hardware.light.ILights/",
+ "android.hardware.power.stats.IPowerStats/",
+ };
+
private static Watchdog sWatchdog;
/* This handler will be used to post message back onto the main thread */
@@ -515,12 +520,11 @@
return builder.toString();
}
- private static ArrayList<Integer> getInterestingHalPids() {
+ private static void addInterestingHidlPids(HashSet<Integer> pids) {
try {
IServiceManager serviceManager = IServiceManager.getService();
ArrayList<IServiceManager.InstanceDebugInfo> dump =
serviceManager.debugDump();
- HashSet<Integer> pids = new HashSet<>();
for (IServiceManager.InstanceDebugInfo info : dump) {
if (info.pid == IServiceManager.PidConstant.NO_PID) {
continue;
@@ -532,24 +536,37 @@
pids.add(info.pid);
}
- return new ArrayList<Integer>(pids);
} catch (RemoteException e) {
- return new ArrayList<Integer>();
+ Log.w(TAG, e);
+ }
+ }
+
+ private static void addInterestingAidlPids(HashSet<Integer> pids) {
+ ServiceDebugInfo[] infos = ServiceManager.getServiceDebugInfo();
+ if (infos == null) return;
+
+ for (ServiceDebugInfo info : infos) {
+ for (String prefix : AIDL_INTERFACE_PREFIXES_OF_INTEREST) {
+ if (info.name.startsWith(prefix)) {
+ pids.add(info.debugPid);
+ }
+ }
}
}
static ArrayList<Integer> getInterestingNativePids() {
- ArrayList<Integer> pids = getInterestingHalPids();
+ HashSet<Integer> pids = new HashSet<>();
+ addInterestingAidlPids(pids);
+ addInterestingHidlPids(pids);
int[] nativePids = Process.getPidsForCommands(NATIVE_STACKS_OF_INTEREST);
if (nativePids != null) {
- pids.ensureCapacity(pids.size() + nativePids.length);
for (int i : nativePids) {
pids.add(i);
}
}
- return pids;
+ return new ArrayList<Integer>(pids);
}
@Override
@@ -704,7 +721,7 @@
WatchdogDiagnostics.diagnoseCheckers(blockedCheckers);
Slog.w(TAG, "*** GOODBYE!");
if (!Build.IS_USER && isCrashLoopFound()
- && !WatchdogProperties.is_fatal_ignore().orElse(false)) {
+ && !WatchdogProperties.should_ignore_fatal_count().orElse(false)) {
breakCrashLoop();
}
Process.killProcess(Process.myPid());
@@ -783,7 +800,7 @@
private boolean isCrashLoopFound() {
int fatalCount = WatchdogProperties.fatal_count().orElse(0);
long fatalWindowMs = TimeUnit.SECONDS.toMillis(
- WatchdogProperties.fatal_window_second().orElse(0));
+ WatchdogProperties.fatal_window_seconds().orElse(0));
if (fatalCount == 0 || fatalWindowMs == 0) {
if (fatalCount != fatalWindowMs) {
Slog.w(TAG, String.format("sysprops '%s' and '%s' should be set or unset together",
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 686adbb..8f1e4e9 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7144,67 +7144,68 @@
"getContentProviderImpl: after checkContentProviderPermission");
final long origId = Binder.clearCallingIdentity();
+ try {
+ checkTime(startTime, "getContentProviderImpl: incProviderCountLocked");
- checkTime(startTime, "getContentProviderImpl: incProviderCountLocked");
-
- // In this case the provider instance already exists, so we can
- // return it right away.
- conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag,
- stable);
- if (conn != null && (conn.stableCount+conn.unstableCount) == 1) {
- if (cpr.proc != null
- && r != null && r.setAdj <= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
- // If this is a perceptible app accessing the provider,
- // make sure to count it as being accessed and thus
- // back up on the LRU list. This is good because
- // content providers are often expensive to start.
- checkTime(startTime, "getContentProviderImpl: before updateLruProcess");
- mProcessList.updateLruProcessLocked(cpr.proc, false, null);
- checkTime(startTime, "getContentProviderImpl: after updateLruProcess");
+ // Return the provider instance right away since it already exists.
+ conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage,
+ callingTag, stable);
+ if (conn != null && (conn.stableCount+conn.unstableCount) == 1) {
+ if (cpr.proc != null
+ && r != null && r.setAdj <= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
+ // If this is a perceptible app accessing the provider,
+ // make sure to count it as being accessed and thus
+ // back up on the LRU list. This is good because
+ // content providers are often expensive to start.
+ checkTime(startTime, "getContentProviderImpl: before updateLruProcess");
+ mProcessList.updateLruProcessLocked(cpr.proc, false, null);
+ checkTime(startTime, "getContentProviderImpl: after updateLruProcess");
+ }
}
- }
- checkTime(startTime, "getContentProviderImpl: before updateOomAdj");
- final int verifiedAdj = cpr.proc.verifiedAdj;
- boolean success = updateOomAdjLocked(cpr.proc, true,
- OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER);
- // XXX things have changed so updateOomAdjLocked doesn't actually tell us
- // if the process has been successfully adjusted. So to reduce races with
- // it, we will check whether the process still exists. Note that this doesn't
- // completely get rid of races with LMK killing the process, but should make
- // them much smaller.
- if (success && verifiedAdj != cpr.proc.setAdj && !isProcessAliveLocked(cpr.proc)) {
- success = false;
- }
- maybeUpdateProviderUsageStatsLocked(r, cpr.info.packageName, name);
- checkTime(startTime, "getContentProviderImpl: after updateOomAdj");
- if (DEBUG_PROVIDER) Slog.i(TAG_PROVIDER, "Adjust success: " + success);
- // NOTE: there is still a race here where a signal could be
- // pending on the process even though we managed to update its
- // adj level. Not sure what to do about this, but at least
- // the race is now smaller.
- if (!success) {
- // Uh oh... it looks like the provider's process
- // has been killed on us. We need to wait for a new
- // process to be started, and make sure its death
- // doesn't kill our process.
- Slog.wtf(TAG, "Existing provider " + cpr.name.flattenToShortString()
- + " is crashing; detaching " + r);
- boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
- if (!lastRef) {
- // This wasn't the last ref our process had on
- // the provider... we will be killed during cleaning up, bail.
- return null;
+ checkTime(startTime, "getContentProviderImpl: before updateOomAdj");
+ final int verifiedAdj = cpr.proc.verifiedAdj;
+ boolean success = updateOomAdjLocked(cpr.proc, true,
+ OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER);
+ // XXX things have changed so updateOomAdjLocked doesn't actually tell us
+ // if the process has been successfully adjusted. So to reduce races with
+ // it, we will check whether the process still exists. Note that this doesn't
+ // completely get rid of races with LMK killing the process, but should make
+ // them much smaller.
+ if (success && verifiedAdj != cpr.proc.setAdj
+ && !isProcessAliveLocked(cpr.proc)) {
+ success = false;
}
- // We'll just start a new process to host the content provider
- providerRunning = false;
- conn = null;
- dyingProc = cpr.proc;
- } else {
- cpr.proc.verifiedAdj = cpr.proc.setAdj;
+ maybeUpdateProviderUsageStatsLocked(r, cpr.info.packageName, name);
+ checkTime(startTime, "getContentProviderImpl: after updateOomAdj");
+ if (DEBUG_PROVIDER) Slog.i(TAG_PROVIDER, "Adjust success: " + success);
+ // NOTE: there is still a race here where a signal could be
+ // pending on the process even though we managed to update its
+ // adj level. Not sure what to do about this, but at least
+ // the race is now smaller.
+ if (!success) {
+ // Uh oh... it looks like the provider's process
+ // has been killed on us. We need to wait for a new
+ // process to be started, and make sure its death
+ // doesn't kill our process.
+ Slog.wtf(TAG, "Existing provider " + cpr.name.flattenToShortString()
+ + " is crashing; detaching " + r);
+ boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
+ if (!lastRef) {
+ // This wasn't the last ref our process had on
+ // the provider... we will be killed during cleaning up, bail.
+ return null;
+ }
+ // We'll just start a new process to host the content provider
+ providerRunning = false;
+ conn = null;
+ dyingProc = cpr.proc;
+ } else {
+ cpr.proc.verifiedAdj = cpr.proc.setAdj;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
}
-
- Binder.restoreCallingIdentity(origId);
}
if (!providerRunning) {
@@ -8211,20 +8212,11 @@
false /* mountExtStorageFull */, abiOverride, zygotePolicyFlags);
}
+ // TODO: Move to ProcessList?
@GuardedBy("this")
final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
boolean disableHiddenApiChecks, boolean mountExtStorageFull, String abiOverride,
int zygotePolicyFlags) {
- return addAppLocked(info, customProcess, isolated, disableHiddenApiChecks,
- false /* disableTestApiChecks */, mountExtStorageFull, abiOverride,
- zygotePolicyFlags);
- }
-
- // TODO: Move to ProcessList?
- @GuardedBy("this")
- final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
- boolean disableHiddenApiChecks, boolean disableTestApiChecks,
- boolean mountExtStorageFull, String abiOverride, int zygotePolicyFlags) {
ProcessRecord app;
if (!isolated) {
app = getProcessRecordLocked(customProcess != null ? customProcess : info.processName,
@@ -8259,8 +8251,7 @@
mPersistentStartingProcesses.add(app);
mProcessList.startProcessLocked(app, new HostingRecord("added application",
customProcess != null ? customProcess : app.processName),
- zygotePolicyFlags, disableHiddenApiChecks, disableTestApiChecks,
- mountExtStorageFull, abiOverride);
+ zygotePolicyFlags, disableHiddenApiChecks, mountExtStorageFull, abiOverride);
}
return app;
@@ -16943,11 +16934,12 @@
|| (flags & INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS) != 0;
boolean disableTestApiChecks = disableHiddenApiChecks
|| (flags & INSTR_FLAG_DISABLE_TEST_API_CHECKS) != 0;
+
if (disableHiddenApiChecks || disableTestApiChecks) {
enforceCallingPermission(android.Manifest.permission.DISABLE_HIDDEN_API_CHECKS,
"disable hidden API checks");
- enableTestApiAccess(ii.packageName);
+ enableTestApiAccess(ai.packageName);
}
// TODO(b/158750470): remove
@@ -16965,8 +16957,7 @@
}
ProcessRecord app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks,
- disableTestApiChecks, mountExtStorageFull, abiOverride,
- ZYGOTE_POLICY_FLAG_EMPTY);
+ mountExtStorageFull, abiOverride, ZYGOTE_POLICY_FLAG_EMPTY);
app.setActiveInstrumentation(activeInstr);
activeInstr.mFinished = false;
activeInstr.mSourceUid = callingUid;
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 3c53880..9986085 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1066,9 +1066,9 @@
}
@Override
- public void noteNetworkInterfaceType(String iface, int networkType) {
+ public void noteNetworkInterfaceForTransports(final String iface, int[] transportTypes) {
enforceCallingPermission();
- mStats.noteNetworkInterfaceType(iface, networkType);
+ mStats.noteNetworkInterfaceForTransports(iface, transportTypes);
}
@Override
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index b6e632d..e2c020af 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1769,8 +1769,8 @@
*/
@GuardedBy("mService")
boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
- int zygotePolicyFlags, boolean disableHiddenApiChecks, boolean disableTestApiChecks,
- boolean mountExtStorageFull, String abiOverride) {
+ int zygotePolicyFlags, boolean disableHiddenApiChecks, boolean mountExtStorageFull,
+ String abiOverride) {
if (app.pendingStart) {
return true;
}
@@ -1914,10 +1914,6 @@
throw new IllegalStateException("Invalid API policy: " + policy);
}
runtimeFlags |= policyBits;
-
- if (disableTestApiChecks) {
- runtimeFlags |= Zygote.DISABLE_TEST_API_ENFORCEMENT_POLICY;
- }
}
String useAppImageCache = SystemProperties.get(
@@ -2360,8 +2356,7 @@
final boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
int zygotePolicyFlags, String abiOverride) {
return startProcessLocked(app, hostingRecord, zygotePolicyFlags,
- false /* disableHiddenApiChecks */, false /* disableTestApiChecks */,
- false /* mountExtStorageFull */, abiOverride);
+ false /* disableHiddenApiChecks */, false /* mountExtStorageFull */, abiOverride);
}
@GuardedBy("mService")
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 28afcbb..c20a01d 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -81,6 +81,7 @@
static final String[] sDeviceConfigScopes = new String[] {
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
DeviceConfig.NAMESPACE_CONFIGURATION,
+ DeviceConfig.NAMESPACE_CONNECTIVITY,
DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT,
DeviceConfig.NAMESPACE_INTELLIGENCE_CONTENT_SUGGESTIONS,
DeviceConfig.NAMESPACE_MEDIA_NATIVE,
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index e8e83cc..e97f0b4 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -16,23 +16,82 @@
package com.android.server.apphibernation;
+import static android.content.Intent.ACTION_PACKAGE_ADDED;
+import static android.content.Intent.ACTION_PACKAGE_REMOVED;
+import static android.content.Intent.EXTRA_REMOVED_FOR_ALL_USERS;
+import static android.content.Intent.EXTRA_REPLACING;
+import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.IActivityManager;
import android.apphibernation.IAppHibernationService;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Environment;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ServiceManager;
+import android.os.ShellCallback;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.DeviceConfig;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.util.SparseArray;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.SystemService;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+
/**
* System service that manages app hibernation state, a state apps can enter that means they are
* not being actively used and can be optimized for storage. The actual policy for determining
* if an app should hibernate is managed by PermissionController code.
*/
public final class AppHibernationService extends SystemService {
+ private static final String TAG = "AppHibernationService";
+ private static final int PACKAGE_MATCH_FLAGS =
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_DISABLED_COMPONENTS;
+ /**
+ * Lock for accessing any in-memory hibernation state
+ */
+ private final Object mLock = new Object();
private final Context mContext;
+ private final IPackageManager mIPackageManager;
+ private final IActivityManager mIActivityManager;
+ private final UserManager mUserManager;
+ @GuardedBy("mLock")
+ private final SparseArray<Map<String, UserLevelState>> mUserStates = new SparseArray<>();
+ private final SparseArray<HibernationStateDiskStore<UserLevelState>> mUserDiskStores =
+ new SparseArray<>();
+ @GuardedBy("mLock")
+ private final Map<String, GlobalLevelState> mGlobalHibernationStates = new ArrayMap<>();
+ private final HibernationStateDiskStore<GlobalLevelState> mGlobalLevelHibernationDiskStore;
+ private final Injector mInjector;
/**
* Initializes the system service.
@@ -44,8 +103,26 @@
* @param context The system server context.
*/
public AppHibernationService(@NonNull Context context) {
- super(context);
- mContext = context;
+ this(new InjectorImpl(context));
+ }
+
+ @VisibleForTesting
+ AppHibernationService(@NonNull Injector injector) {
+ super(injector.getContext());
+ mContext = injector.getContext();
+ mIPackageManager = injector.getPackageManager();
+ mIActivityManager = injector.getActivityManager();
+ mUserManager = injector.getUserManager();
+ mGlobalLevelHibernationDiskStore = injector.getGlobalLevelDiskStore();
+ mInjector = injector;
+
+ final Context userAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */);
+
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(ACTION_PACKAGE_ADDED);
+ intentFilter.addAction(ACTION_PACKAGE_REMOVED);
+ intentFilter.addDataScheme("package");
+ userAllContext.registerReceiver(mBroadcastReceiver, intentFilter);
}
@Override
@@ -53,6 +130,17 @@
publishBinderService(Context.APP_HIBERNATION_SERVICE, mServiceStub);
}
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == PHASE_BOOT_COMPLETED) {
+ List<GlobalLevelState> states =
+ mGlobalLevelHibernationDiskStore.readHibernationStates();
+ synchronized (mLock) {
+ initializeGlobalHibernationStates(states);
+ }
+ }
+ }
+
/**
* Whether a package is hibernating for a given user.
*
@@ -60,9 +148,40 @@
* @param userId the user to check
* @return true if package is hibernating for the user
*/
- public boolean isHibernating(String packageName, int userId) {
- // Stub
- throw new UnsupportedOperationException("Hibernation state management not implemented yet");
+ boolean isHibernatingForUser(String packageName, int userId) {
+ userId = handleIncomingUser(userId, "isHibernating");
+ if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
+ Slog.e(TAG, "Attempt to get hibernation state of stopped or nonexistent user "
+ + userId);
+ return false;
+ }
+ synchronized (mLock) {
+ final Map<String, UserLevelState> packageStates = mUserStates.get(userId);
+ final UserLevelState pkgState = packageStates.get(packageName);
+ if (pkgState == null) {
+ throw new IllegalArgumentException(
+ String.format("Package %s is not installed for user %s",
+ packageName, userId));
+ }
+ return pkgState.hibernated;
+ }
+ }
+
+ /**
+ * Whether a package is hibernated globally. This only occurs when a package is hibernating for
+ * all users and allows us to make optimizations at the package or APK level.
+ *
+ * @param packageName package to check
+ */
+ boolean isHibernatingGlobally(String packageName) {
+ synchronized (mLock) {
+ GlobalLevelState state = mGlobalHibernationStates.get(packageName);
+ if (state == null) {
+ throw new IllegalArgumentException(
+ String.format("Package %s is not installed", packageName));
+ }
+ return state.hibernated;
+ }
}
/**
@@ -72,9 +191,274 @@
* @param userId user
* @param isHibernating new hibernation state
*/
- public void setHibernating(String packageName, int userId, boolean isHibernating) {
- // Stub
- throw new UnsupportedOperationException("Hibernation state management not implemented yet");
+ void setHibernatingForUser(String packageName, int userId, boolean isHibernating) {
+ userId = handleIncomingUser(userId, "setHibernating");
+ if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
+ Slog.w(TAG, "Attempt to set hibernation state for a stopped or nonexistent user "
+ + userId);
+ return;
+ }
+ synchronized (mLock) {
+ final Map<String, UserLevelState> packageStates = mUserStates.get(userId);
+ final UserLevelState pkgState = packageStates.get(packageName);
+ if (pkgState == null) {
+ throw new IllegalArgumentException(
+ String.format("Package %s is not installed for user %s",
+ packageName, userId));
+ }
+
+ if (pkgState.hibernated == isHibernating) {
+ return;
+ }
+
+ if (isHibernating) {
+ hibernatePackageForUser(packageName, userId, pkgState);
+ } else {
+ unhibernatePackageForUser(packageName, userId, pkgState);
+ }
+ List<UserLevelState> states = new ArrayList<>(mUserStates.get(userId).values());
+ mUserDiskStores.get(userId).scheduleWriteHibernationStates(states);
+ }
+ }
+
+ /**
+ * Set whether the package should be hibernated globally at a package level, allowing the
+ * the system to make optimizations at the package or APK level.
+ *
+ * @param packageName package to hibernate globally
+ * @param isHibernating new hibernation state
+ */
+ void setHibernatingGlobally(String packageName, boolean isHibernating) {
+ synchronized (mLock) {
+ GlobalLevelState state = mGlobalHibernationStates.get(packageName);
+ if (state == null) {
+ throw new IllegalArgumentException(
+ String.format("Package %s is not installed for any user", packageName));
+ }
+ if (state.hibernated != isHibernating) {
+ if (isHibernating) {
+ hibernatePackageGlobally(packageName, state);
+ } else {
+ unhibernatePackageGlobally(packageName, state);
+ }
+ List<GlobalLevelState> states = new ArrayList<>(mGlobalHibernationStates.values());
+ mGlobalLevelHibernationDiskStore.scheduleWriteHibernationStates(states);
+ }
+ }
+ }
+
+ /**
+ * Put an app into hibernation for a given user, allowing user-level optimizations to occur.
+ *
+ * @param pkgState package hibernation state
+ */
+ @GuardedBy("mLock")
+ private void hibernatePackageForUser(@NonNull String packageName, int userId,
+ @NonNull UserLevelState pkgState) {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackage");
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ mIActivityManager.forceStopPackage(packageName, userId);
+ mIPackageManager.deleteApplicationCacheFilesAsUser(packageName, userId,
+ null /* observer */);
+ pkgState.hibernated = true;
+ } catch (RemoteException e) {
+ throw new IllegalStateException(
+ "Failed to hibernate due to manager not being available", e);
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ }
+ }
+
+ /**
+ * Remove a package from hibernation for a given user.
+ *
+ * @param pkgState package hibernation state
+ */
+ @GuardedBy("mLock")
+ private void unhibernatePackageForUser(@NonNull String packageName, int userId,
+ UserLevelState pkgState) {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "unhibernatePackage");
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ mIPackageManager.setPackageStoppedState(packageName, false, userId);
+ pkgState.hibernated = false;
+ } catch (RemoteException e) {
+ throw new IllegalStateException(
+ "Failed to unhibernate due to manager not being available", e);
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ }
+ }
+
+ /**
+ * Put a package into global hibernation, optimizing its storage at a package / APK level.
+ */
+ @GuardedBy("mLock")
+ private void hibernatePackageGlobally(@NonNull String packageName, GlobalLevelState state) {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackageGlobally");
+ // TODO(175830194): Delete vdex/odex when DexManager API is built out
+ state.hibernated = true;
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ }
+
+ /**
+ * Unhibernate a package from global hibernation.
+ */
+ @GuardedBy("mLock")
+ private void unhibernatePackageGlobally(@NonNull String packageName, GlobalLevelState state) {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "unhibernatePackageGlobally");
+ state.hibernated = false;
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ }
+
+ /**
+ * Initializes in-memory store of user-level hibernation states for the given user
+ *
+ * @param userId user id to add installed packages for
+ * @param diskStates states pulled from disk, if available
+ */
+ @GuardedBy("mLock")
+ private void initializeUserHibernationStates(int userId,
+ @Nullable List<UserLevelState> diskStates) {
+ List<PackageInfo> packages;
+ try {
+ packages = mIPackageManager.getInstalledPackages(PACKAGE_MATCH_FLAGS, userId).getList();
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Package manager not available", e);
+ }
+
+ Map<String, UserLevelState> userLevelStates = new ArrayMap<>();
+
+ for (int i = 0, size = packages.size(); i < size; i++) {
+ String packageName = packages.get(i).packageName;
+ UserLevelState state = new UserLevelState();
+ state.packageName = packageName;
+ userLevelStates.put(packageName, state);
+ }
+
+ if (diskStates != null) {
+ Set<String> installedPackages = new ArraySet<>();
+ for (int i = 0, size = packages.size(); i < size; i++) {
+ installedPackages.add(packages.get(i).packageName);
+ }
+ for (int i = 0, size = diskStates.size(); i < size; i++) {
+ String packageName = diskStates.get(i).packageName;
+ if (!installedPackages.contains(packageName)) {
+ Slog.w(TAG, String.format(
+ "No hibernation state associated with package %s user %d. Maybe"
+ + "the package was uninstalled? ", packageName, userId));
+ continue;
+ }
+ userLevelStates.put(packageName, diskStates.get(i));
+ }
+ }
+ mUserStates.put(userId, userLevelStates);
+ }
+
+ /**
+ * Initialize in-memory store of global level hibernation states.
+ *
+ * @param diskStates global level hibernation states pulled from disk, if available
+ */
+ @GuardedBy("mLock")
+ private void initializeGlobalHibernationStates(@Nullable List<GlobalLevelState> diskStates) {
+ List<PackageInfo> packages;
+ try {
+ packages = mIPackageManager.getInstalledPackages(
+ PACKAGE_MATCH_FLAGS | MATCH_ANY_USER, 0 /* userId */).getList();
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Package manager not available", e);
+ }
+
+ for (int i = 0, size = packages.size(); i < size; i++) {
+ String packageName = packages.get(i).packageName;
+ GlobalLevelState state = new GlobalLevelState();
+ state.packageName = packageName;
+ mGlobalHibernationStates.put(packageName, state);
+ }
+ if (diskStates != null) {
+ Set<String> installedPackages = new ArraySet<>();
+ for (int i = 0, size = packages.size(); i < size; i++) {
+ installedPackages.add(packages.get(i).packageName);
+ }
+ for (int i = 0, size = diskStates.size(); i < size; i++) {
+ GlobalLevelState state = diskStates.get(i);
+ if (!installedPackages.contains(state.packageName)) {
+ Slog.w(TAG, String.format(
+ "No hibernation state associated with package %s. Maybe the "
+ + "package was uninstalled? ", state.packageName));
+ continue;
+ }
+ mGlobalHibernationStates.put(state.packageName, state);
+ }
+ }
+ }
+
+ @Override
+ public void onUserUnlocking(@NonNull TargetUser user) {
+ int userId = user.getUserIdentifier();
+ HibernationStateDiskStore<UserLevelState> diskStore =
+ mInjector.getUserLevelDiskStore(userId);
+ mUserDiskStores.put(userId, diskStore);
+ List<UserLevelState> storedStates = diskStore.readHibernationStates();
+ synchronized (mLock) {
+ initializeUserHibernationStates(userId, storedStates);
+ }
+ }
+
+ @Override
+ public void onUserStopping(@NonNull TargetUser user) {
+ int userId = user.getUserIdentifier();
+ // TODO: Flush any scheduled writes to disk immediately on user stopping / power off.
+ synchronized (mLock) {
+ mUserDiskStores.remove(userId);
+ mUserStates.remove(userId);
+ }
+ }
+
+ private void onPackageAdded(@NonNull String packageName, int userId) {
+ synchronized (mLock) {
+ UserLevelState userState = new UserLevelState();
+ userState.packageName = packageName;
+ mUserStates.get(userId).put(packageName, userState);
+ if (!mGlobalHibernationStates.containsKey(packageName)) {
+ GlobalLevelState globalState = new GlobalLevelState();
+ globalState.packageName = packageName;
+ mGlobalHibernationStates.put(packageName, globalState);
+ }
+ }
+ }
+
+ private void onPackageRemoved(@NonNull String packageName, int userId) {
+ synchronized (mLock) {
+ mUserStates.get(userId).remove(packageName);
+ }
+ }
+
+ private void onPackageRemovedForAllUsers(@NonNull String packageName) {
+ synchronized (mLock) {
+ mGlobalHibernationStates.remove(packageName);
+ }
+ }
+
+ /**
+ * Private helper method to get the real user id and enforce permission checks.
+ *
+ * @param userId user id to handle
+ * @param name name to use for exceptions
+ * @return real user id
+ */
+ private int handleIncomingUser(int userId, @NonNull String name) {
+ int callingUid = Binder.getCallingUid();
+ try {
+ return mIActivityManager.handleIncomingUser(Binder.getCallingPid(), callingUid, userId,
+ false /* allowAll */, true /* requireFull */, name, null);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
}
private final AppHibernationServiceStub mServiceStub = new AppHibernationServiceStub(this);
@@ -87,16 +471,63 @@
}
@Override
- public boolean isHibernating(String packageName, int userId) {
- return mService.isHibernating(packageName, userId);
+ public boolean isHibernatingForUser(String packageName, int userId) {
+ return mService.isHibernatingForUser(packageName, userId);
}
@Override
- public void setHibernating(String packageName, int userId, boolean isHibernating) {
- mService.setHibernating(packageName, userId, isHibernating);
+ public void setHibernatingForUser(String packageName, int userId, boolean isHibernating) {
+ mService.setHibernatingForUser(packageName, userId, isHibernating);
+ }
+
+ @Override
+ public void setHibernatingGlobally(String packageName, boolean isHibernating) {
+ mService.setHibernatingGlobally(packageName, isHibernating);
+ }
+
+ @Override
+ public boolean isHibernatingGlobally(String packageName) {
+ return mService.isHibernatingGlobally(packageName);
+ }
+
+ @Override
+ public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+ @Nullable FileDescriptor err, @NonNull String[] args,
+ @Nullable ShellCallback callback, @NonNull ResultReceiver resultReceiver) {
+ new AppHibernationShellCommand(mService).exec(this, in, out, err, args, callback,
+ resultReceiver);
}
}
+ // Broadcast receiver for package add/removal events
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+ if (userId == UserHandle.USER_NULL) {
+ return;
+ }
+
+ final String action = intent.getAction();
+ if (ACTION_PACKAGE_ADDED.equals(action) || ACTION_PACKAGE_REMOVED.equals(action)) {
+ final String packageName = intent.getData().getSchemeSpecificPart();
+ if (intent.getBooleanExtra(EXTRA_REPLACING, false)) {
+ // Package removal/add is part of an update, so no need to modify package state.
+ return;
+ }
+
+ if (ACTION_PACKAGE_ADDED.equals(action)) {
+ onPackageAdded(packageName, userId);
+ } else if (ACTION_PACKAGE_REMOVED.equals(action)) {
+ onPackageRemoved(packageName, userId);
+ if (intent.getBooleanExtra(EXTRA_REMOVED_FOR_ALL_USERS, false)) {
+ onPackageRemovedForAllUsers(packageName);
+ }
+ }
+ }
+ }
+ };
+
/**
* Whether app hibernation is enabled on this device.
*
@@ -108,4 +539,68 @@
AppHibernationConstants.KEY_APP_HIBERNATION_ENABLED,
false /* defaultValue */);
}
+
+ /**
+ * Dependency injector for {@link #AppHibernationService)}.
+ */
+ interface Injector {
+ Context getContext();
+
+ IPackageManager getPackageManager();
+
+ IActivityManager getActivityManager();
+
+ UserManager getUserManager();
+
+ HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore();
+
+ HibernationStateDiskStore<UserLevelState> getUserLevelDiskStore(int userId);
+ }
+
+ private static final class InjectorImpl implements Injector {
+ private static final String HIBERNATION_DIR_NAME = "hibernation";
+ private final Context mContext;
+ private final ScheduledExecutorService mScheduledExecutorService;
+ private final UserLevelHibernationProto mUserLevelHibernationProto;
+
+ InjectorImpl(Context context) {
+ mContext = context;
+ mScheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
+ mUserLevelHibernationProto = new UserLevelHibernationProto();
+ }
+
+ @Override
+ public Context getContext() {
+ return mContext;
+ }
+
+ @Override
+ public IPackageManager getPackageManager() {
+ return IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
+ }
+
+ @Override
+ public IActivityManager getActivityManager() {
+ return ActivityManager.getService();
+ }
+
+ @Override
+ public UserManager getUserManager() {
+ return mContext.getSystemService(UserManager.class);
+ }
+
+ @Override
+ public HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore() {
+ File dir = new File(Environment.getDataSystemDirectory(), HIBERNATION_DIR_NAME);
+ return new HibernationStateDiskStore<>(
+ dir, new GlobalLevelHibernationProto(), mScheduledExecutorService);
+ }
+
+ @Override
+ public HibernationStateDiskStore<UserLevelState> getUserLevelDiskStore(int userId) {
+ File dir = new File(Environment.getDataSystemCeDirectory(userId), HIBERNATION_DIR_NAME);
+ return new HibernationStateDiskStore<>(
+ dir, mUserLevelHibernationProto, mScheduledExecutorService);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationShellCommand.java b/services/core/java/com/android/server/apphibernation/AppHibernationShellCommand.java
new file mode 100644
index 0000000..7d6eea2
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationShellCommand.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.apphibernation;
+
+import android.os.ShellCommand;
+import android.os.UserHandle;
+
+import java.io.PrintWriter;
+
+/**
+ * Shell command implementation for {@link AppHibernationService}.
+ */
+final class AppHibernationShellCommand extends ShellCommand {
+ private static final String USER_OPT = "--user";
+ private static final String GLOBAL_OPT = "--global";
+ private static final int SUCCESS = 0;
+ private static final int ERROR = -1;
+ private final AppHibernationService mService;
+
+ AppHibernationShellCommand(AppHibernationService service) {
+ mService = service;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+ switch (cmd) {
+ case "set-state":
+ return runSetState();
+ case "get-state":
+ return runGetState();
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ }
+
+ private int runSetState() {
+ String opt;
+ boolean setsGlobal = false;
+ int userId = UserHandle.USER_CURRENT;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case USER_OPT:
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ break;
+ case GLOBAL_OPT:
+ setsGlobal = true;
+ break;
+ default:
+ getErrPrintWriter().println("Error: Unknown option: " + opt);
+ }
+ }
+
+ String pkg = getNextArgRequired();
+ if (pkg == null) {
+ getErrPrintWriter().println("Error: no package specified");
+ return ERROR;
+ }
+
+ String newStateRaw = getNextArgRequired();
+ if (newStateRaw == null) {
+ getErrPrintWriter().println("Error: No state to set specified");
+ return ERROR;
+ }
+ boolean newState = Boolean.parseBoolean(newStateRaw);
+
+ if (setsGlobal) {
+ mService.setHibernatingGlobally(pkg, newState);
+ } else {
+ mService.setHibernatingForUser(pkg, userId, newState);
+ }
+ return SUCCESS;
+ }
+
+ private int runGetState() {
+ String opt;
+ boolean requestsGlobal = false;
+ int userId = UserHandle.USER_CURRENT;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case USER_OPT:
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ break;
+ case GLOBAL_OPT:
+ requestsGlobal = true;
+ break;
+ default:
+ getErrPrintWriter().println("Error: Unknown option: " + opt);
+ }
+ }
+
+ String pkg = getNextArgRequired();
+ if (pkg == null) {
+ getErrPrintWriter().println("Error: No package specified");
+ return ERROR;
+ }
+ boolean isHibernating = requestsGlobal
+ ? mService.isHibernatingGlobally(pkg) : mService.isHibernatingForUser(pkg, userId);
+ final PrintWriter pw = getOutPrintWriter();
+ pw.println(isHibernating);
+ return SUCCESS;
+ }
+
+ @Override
+ public void onHelp() {
+ final PrintWriter pw = getOutPrintWriter();
+ pw.println("App hibernation (app_hibernation) commands: ");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println("");
+ pw.println(" set-state [--user USER_ID] [--global] PACKAGE true|false");
+ pw.println(" Sets the hibernation state of the package to value specified. Optionally");
+ pw.println(" may specify a user id or set global hibernation state.");
+ pw.println("");
+ pw.println(" get-state [--user USER_ID] [--global] PACKAGE");
+ pw.println(" Gets the hibernation state of the package. Optionally may specify a user");
+ pw.println(" id or request global hibernation state.");
+ pw.println("");
+ }
+}
diff --git a/services/core/java/com/android/server/apphibernation/GlobalLevelHibernationProto.java b/services/core/java/com/android/server/apphibernation/GlobalLevelHibernationProto.java
new file mode 100644
index 0000000..79e995b
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/GlobalLevelHibernationProto.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.apphibernation;
+
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Slog;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Reads and writes protos for {@link GlobalLevelState} hiberation states.
+ */
+final class GlobalLevelHibernationProto implements ProtoReadWriter<List<GlobalLevelState>> {
+ private static final String TAG = "GlobalLevelHibernationProtoReadWriter";
+
+ @Override
+ public void writeToProto(@NonNull ProtoOutputStream stream,
+ @NonNull List<GlobalLevelState> data) {
+ for (int i = 0, size = data.size(); i < size; i++) {
+ long token = stream.start(GlobalLevelHibernationStatesProto.HIBERNATION_STATE);
+ GlobalLevelState state = data.get(i);
+ stream.write(GlobalLevelHibernationStateProto.PACKAGE_NAME, state.packageName);
+ stream.write(GlobalLevelHibernationStateProto.HIBERNATED, state.hibernated);
+ stream.end(token);
+ }
+ }
+
+ @Override
+ public @Nullable List<GlobalLevelState> readFromProto(@NonNull ProtoInputStream stream)
+ throws IOException {
+ List<GlobalLevelState> list = new ArrayList<>();
+ while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ if (stream.getFieldNumber()
+ != (int) GlobalLevelHibernationStatesProto.HIBERNATION_STATE) {
+ continue;
+ }
+ GlobalLevelState state = new GlobalLevelState();
+ long token = stream.start(GlobalLevelHibernationStatesProto.HIBERNATION_STATE);
+ while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (stream.getFieldNumber()) {
+ case (int) GlobalLevelHibernationStateProto.PACKAGE_NAME:
+ state.packageName =
+ stream.readString(GlobalLevelHibernationStateProto.PACKAGE_NAME);
+ break;
+ case (int) GlobalLevelHibernationStateProto.HIBERNATED:
+ state.hibernated =
+ stream.readBoolean(GlobalLevelHibernationStateProto.HIBERNATED);
+ break;
+ default:
+ Slog.w(TAG, "Undefined field in proto: " + stream.getFieldNumber());
+ }
+ }
+ stream.end(token);
+ list.add(state);
+ }
+ return list;
+ }
+}
diff --git a/apex/permission/framework/java/android/permission/PermissionState.java b/services/core/java/com/android/server/apphibernation/GlobalLevelState.java
similarity index 68%
rename from apex/permission/framework/java/android/permission/PermissionState.java
rename to services/core/java/com/android/server/apphibernation/GlobalLevelState.java
index e810db8..4f75675 100644
--- a/apex/permission/framework/java/android/permission/PermissionState.java
+++ b/services/core/java/com/android/server/apphibernation/GlobalLevelState.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,9 +14,12 @@
* limitations under the License.
*/
-package android.permission;
+package com.android.server.apphibernation;
/**
- * @hide
+ * Data class that contains global hibernation state for a package.
*/
-public class PermissionState {}
+final class GlobalLevelState {
+ public String packageName;
+ public boolean hibernated;
+}
diff --git a/services/core/java/com/android/server/apphibernation/HibernationStateDiskStore.java b/services/core/java/com/android/server/apphibernation/HibernationStateDiskStore.java
new file mode 100644
index 0000000..c83659d
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/HibernationStateDiskStore.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.apphibernation;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.WorkerThread;
+import android.text.format.DateUtils;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Disk store utility class for hibernation states.
+ *
+ * @param <T> the type of hibernation state data
+ */
+class HibernationStateDiskStore<T> {
+ private static final String TAG = "HibernationStateDiskStore";
+
+ // Time to wait before actually writing. Saves extra writes if data changes come in batches.
+ private static final long DISK_WRITE_DELAY = 1L * DateUtils.MINUTE_IN_MILLIS;
+ private static final String STATES_FILE_NAME = "states";
+
+ private final File mHibernationFile;
+ private final ScheduledExecutorService mExecutorService;
+ private final ProtoReadWriter<List<T>> mProtoReadWriter;
+ private List<T> mScheduledStatesToWrite = new ArrayList<>();
+ private ScheduledFuture<?> mFuture;
+
+ /**
+ * Initialize a disk store for hibernation states in the given directory.
+ *
+ * @param hibernationDir directory to write/read states file
+ * @param readWriter writer/reader of states proto
+ * @param executorService scheduled executor for writing data
+ */
+ HibernationStateDiskStore(@NonNull File hibernationDir,
+ @NonNull ProtoReadWriter<List<T>> readWriter,
+ @NonNull ScheduledExecutorService executorService) {
+ this(hibernationDir, readWriter, executorService, STATES_FILE_NAME);
+ }
+
+ @VisibleForTesting
+ HibernationStateDiskStore(@NonNull File hibernationDir,
+ @NonNull ProtoReadWriter<List<T>> readWriter,
+ @NonNull ScheduledExecutorService executorService,
+ @NonNull String fileName) {
+ mHibernationFile = new File(hibernationDir, fileName);
+ mExecutorService = executorService;
+ mProtoReadWriter = readWriter;
+ }
+
+ /**
+ * Schedule a full write of all the hibernation states to the file on disk. Does not run
+ * immediately and subsequent writes override previous ones.
+ *
+ * @param hibernationStates list of hibernation states to write to disk
+ */
+ void scheduleWriteHibernationStates(@NonNull List<T> hibernationStates) {
+ synchronized (this) {
+ mScheduledStatesToWrite = hibernationStates;
+ if (mExecutorService.isShutdown()) {
+ Slog.e(TAG, "Scheduled executor service is shut down.");
+ return;
+ }
+
+ // Already have write scheduled
+ if (mFuture != null) {
+ Slog.i(TAG, "Write already scheduled. Skipping schedule.");
+ return;
+ }
+
+ mFuture = mExecutorService.schedule(this::writeHibernationStates, DISK_WRITE_DELAY,
+ TimeUnit.MILLISECONDS);
+ }
+ }
+
+ /**
+ * Read hibernation states from disk.
+ *
+ * @return the parsed list of hibernation states, null if file does not exist
+ */
+ @Nullable
+ List<T> readHibernationStates() {
+ synchronized (this) {
+ if (!mHibernationFile.exists()) {
+ Slog.i(TAG, "No hibernation file on disk for file " + mHibernationFile.getPath());
+ return null;
+ }
+ AtomicFile atomicFile = new AtomicFile(mHibernationFile);
+
+ try {
+ FileInputStream inputStream = atomicFile.openRead();
+ ProtoInputStream protoInputStream = new ProtoInputStream(inputStream);
+ return mProtoReadWriter.readFromProto(protoInputStream);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to read states protobuf.", e);
+ return null;
+ }
+ }
+ }
+
+ @WorkerThread
+ private void writeHibernationStates() {
+ synchronized (this) {
+ writeStateProto(mScheduledStatesToWrite);
+ mScheduledStatesToWrite.clear();
+ mFuture = null;
+ }
+ }
+
+ @WorkerThread
+ private void writeStateProto(List<T> states) {
+ AtomicFile atomicFile = new AtomicFile(mHibernationFile);
+
+ FileOutputStream fileOutputStream;
+ try {
+ fileOutputStream = atomicFile.startWrite();
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to start write to states protobuf.", e);
+ return;
+ }
+
+ try {
+ ProtoOutputStream protoOutputStream = new ProtoOutputStream(fileOutputStream);
+ mProtoReadWriter.writeToProto(protoOutputStream, states);
+ protoOutputStream.flush();
+ atomicFile.finishWrite(fileOutputStream);
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to finish write to states protobuf.", e);
+ atomicFile.failWrite(fileOutputStream);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/apphibernation/ProtoReadWriter.java b/services/core/java/com/android/server/apphibernation/ProtoReadWriter.java
new file mode 100644
index 0000000..0cbc09a
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/ProtoReadWriter.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.apphibernation;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.IOException;
+
+/**
+ * Proto utility that reads and writes proto for some data.
+ *
+ * @param <T> data that can be written and read from a proto
+ */
+interface ProtoReadWriter<T> {
+
+ /**
+ * Write data to a proto stream
+ */
+ void writeToProto(@NonNull ProtoOutputStream stream, @NonNull T data);
+
+ /**
+ * Parse data from the proto stream and return
+ */
+ @Nullable T readFromProto(@NonNull ProtoInputStream stream) throws IOException;
+}
diff --git a/services/core/java/com/android/server/apphibernation/UserLevelHibernationProto.java b/services/core/java/com/android/server/apphibernation/UserLevelHibernationProto.java
new file mode 100644
index 0000000..a24c4c5
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/UserLevelHibernationProto.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.apphibernation;
+
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Slog;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Reads and writes protos for {@link UserLevelState} hiberation states.
+ */
+final class UserLevelHibernationProto implements ProtoReadWriter<List<UserLevelState>> {
+ private static final String TAG = "UserLevelHibernationProtoReadWriter";
+
+ @Override
+ public void writeToProto(@NonNull ProtoOutputStream stream,
+ @NonNull List<UserLevelState> data) {
+ for (int i = 0, size = data.size(); i < size; i++) {
+ long token = stream.start(UserLevelHibernationStatesProto.HIBERNATION_STATE);
+ UserLevelState state = data.get(i);
+ stream.write(UserLevelHibernationStateProto.PACKAGE_NAME, state.packageName);
+ stream.write(UserLevelHibernationStateProto.HIBERNATED, state.hibernated);
+ stream.end(token);
+ }
+ }
+
+ @Override
+ public @Nullable List<UserLevelState> readFromProto(@NonNull ProtoInputStream stream)
+ throws IOException {
+ List<UserLevelState> list = new ArrayList<>();
+ while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ if (stream.getFieldNumber()
+ != (int) UserLevelHibernationStatesProto.HIBERNATION_STATE) {
+ continue;
+ }
+ UserLevelState state = new UserLevelState();
+ long token = stream.start(UserLevelHibernationStatesProto.HIBERNATION_STATE);
+ while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (stream.getFieldNumber()) {
+ case (int) UserLevelHibernationStateProto.PACKAGE_NAME:
+ state.packageName =
+ stream.readString(UserLevelHibernationStateProto.PACKAGE_NAME);
+ break;
+ case (int) UserLevelHibernationStateProto.HIBERNATED:
+ state.hibernated =
+ stream.readBoolean(UserLevelHibernationStateProto.HIBERNATED);
+ break;
+ default:
+ Slog.w(TAG, "Undefined field in proto: " + stream.getFieldNumber());
+ }
+ }
+ stream.end(token);
+ list.add(state);
+ }
+ return list;
+ }
+}
diff --git a/apex/permission/framework/java/android/permission/PermissionState.java b/services/core/java/com/android/server/apphibernation/UserLevelState.java
similarity index 67%
copy from apex/permission/framework/java/android/permission/PermissionState.java
copy to services/core/java/com/android/server/apphibernation/UserLevelState.java
index e810db8..c66dad8 100644
--- a/apex/permission/framework/java/android/permission/PermissionState.java
+++ b/services/core/java/com/android/server/apphibernation/UserLevelState.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,9 +14,12 @@
* limitations under the License.
*/
-package android.permission;
+package com.android.server.apphibernation;
/**
- * @hide
+ * Data class that contains hibernation state info of a package for a user.
*/
-public class PermissionState {}
+final class UserLevelState {
+ public String packageName;
+ public boolean hibernated;
+}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 024dca7..4c69704 100755
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -32,6 +32,7 @@
import android.Manifest;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -166,6 +167,7 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
@@ -173,6 +175,7 @@
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.BooleanSupplier;
import java.util.stream.Collectors;
/**
@@ -563,6 +566,117 @@
private boolean mDockAudioMediaEnabled = true;
+ /**
+ * RestorableParameters is a thread-safe class used to store a
+ * first-in first-out history of parameters for replay / restoration.
+ *
+ * The idealized implementation of restoration would have a list of setting methods and
+ * values to be called for restoration. Explicitly managing such setters and
+ * values would be tedious - a simpler method is to store the values and the
+ * method implicitly by lambda capture (the values must be immutable or synchronization
+ * needs to be taken).
+ *
+ * We provide queueRestoreWithRemovalIfTrue() to allow
+ * the caller to provide a BooleanSupplier lambda, which conveniently packages
+ * the setter and its parameters needed for restoration. If during restoration,
+ * the BooleanSupplier returns true, it is removed from the mMap.
+ *
+ * We provide a setParameters() method as an example helper method.
+ */
+ private static class RestorableParameters {
+ /**
+ * Sets a parameter and queues for restoration if successful.
+ *
+ * @param id a string handle associated with this parameter.
+ * @param parameter the actual parameter string.
+ * @return the result of AudioSystem.setParameters
+ */
+ public int setParameters(@NonNull String id, @NonNull String parameter) {
+ Objects.requireNonNull(id, "id must not be null");
+ Objects.requireNonNull(parameter, "parameter must not be null");
+ synchronized (mMap) {
+ final int status = AudioSystem.setParameters(parameter);
+ if (status == AudioSystem.AUDIO_STATUS_OK) { // Java uses recursive mutexes.
+ queueRestoreWithRemovalIfTrue(id, () -> { // remove me if set fails.
+ return AudioSystem.setParameters(parameter) != AudioSystem.AUDIO_STATUS_OK;
+ });
+ }
+ // Implementation detail: We do not mMap.remove(id); on failure.
+ return status;
+ }
+ }
+
+ /**
+ * Queues a restore method which is executed on restoreAll().
+ *
+ * If the supplier null, the id is removed from the restore map.
+ *
+ * Note: When the BooleanSupplier restore method is executed
+ * during restoreAll, if it returns true, it is removed from the
+ * restore map.
+ *
+ * @param id a unique tag associated with the restore method.
+ * @param supplier is a BooleanSupplier lambda.
+ */
+ public void queueRestoreWithRemovalIfTrue(
+ @NonNull String id, @Nullable BooleanSupplier supplier) {
+ Objects.requireNonNull(id, "id must not be null");
+ synchronized (mMap) {
+ if (supplier != null) {
+ mMap.put(id, supplier);
+ } else {
+ mMap.remove(id);
+ }
+ }
+ }
+
+ /**
+ * Restore all parameters
+ *
+ * During restoration after audioserver death, any BooleanSupplier that returns
+ * true will be removed from mMap.
+ */
+ public void restoreAll() {
+ synchronized (mMap) {
+ // Note: removing from values() also removes from the backing map.
+ // TODO: Consider catching exceptions?
+ mMap.values().removeIf(v -> {
+ return v.getAsBoolean();
+ });
+ }
+ }
+
+ /**
+ * mMap is a LinkedHashMap<Key, Value> of parameters restored by restore().
+ * The Key is a unique id tag for identification.
+ * The Value is a lambda expression which returns true if the entry is to
+ * be removed.
+ *
+ * 1) For memory limitation purposes, mMap keeps the latest MAX_ENTRIES
+ * accessed in the map.
+ * 2) Parameters are restored in order of queuing, first in first out,
+ * from earliest to latest.
+ */
+ @GuardedBy("mMap")
+ private Map</* @NonNull */ String, /* @NonNull */ BooleanSupplier> mMap =
+ new LinkedHashMap<>() {
+ // TODO: do we need this memory limitation?
+ private static final int MAX_ENTRIES = 1000; // limit our memory for now.
+ @Override
+ protected boolean removeEldestEntry(Map.Entry eldest) {
+ if (size() <= MAX_ENTRIES) return false;
+ Log.w(TAG, "Parameter map exceeds "
+ + MAX_ENTRIES + " removing " + eldest.getKey()); // don't silently remove.
+ return true;
+ }
+ };
+ }
+
+ // We currently have one instance for mRestorableParameters used for
+ // setAdditionalOutputDeviceDelay(). Other methods requiring restoration could share this
+ // or use their own instance.
+ private RestorableParameters mRestorableParameters = new RestorableParameters();
+
private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
// Used when safe volume warning message display is requested by setStreamVolume(). In this
@@ -1095,6 +1209,9 @@
RotationHelper.updateOrientation();
}
+ // Restore setParameters and other queued setters.
+ mRestorableParameters.restoreAll();
+
synchronized (mSettingsLock) {
final int forDock = mDockAudioMediaEnabled ?
AudioSystem.FORCE_ANALOG_DOCK : AudioSystem.FORCE_NONE;
@@ -9303,6 +9420,95 @@
}
}
+ /**
+ * @hide
+ * Sets an additional audio output device delay in milliseconds.
+ *
+ * The additional output delay is a request to the output device to
+ * delay audio presentation (generally with respect to video presentation for better
+ * synchronization).
+ * It may not be supported by all output devices,
+ * and typically increases the audio latency by the amount of additional
+ * audio delay requested.
+ *
+ * If additional audio delay is supported by an audio output device,
+ * it is expected to be supported for all output streams (and configurations)
+ * opened on that device.
+ *
+ * @param deviceType
+ * @param address
+ * @param delayMillis delay in milliseconds desired. This should be in range of {@code 0}
+ * to the value returned by {@link #getMaxAdditionalOutputDeviceDelay()}.
+ * @return true if successful, false if the device does not support output device delay
+ * or the delay is not in range of {@link #getMaxAdditionalOutputDeviceDelay()}.
+ */
+ @Override
+ //@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public boolean setAdditionalOutputDeviceDelay(
+ @NonNull AudioDeviceAttributes device, @IntRange(from = 0) long delayMillis) {
+ Objects.requireNonNull(device, "device must not be null");
+ enforceModifyAudioRoutingPermission();
+ final String getterKey = "additional_output_device_delay="
+ + AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType())
+ + "," + device.getAddress(); // "getter" key as an id.
+ final String setterKey = getterKey + "," + delayMillis; // append the delay for setter
+ return mRestorableParameters.setParameters(getterKey, setterKey)
+ == AudioSystem.AUDIO_STATUS_OK;
+ }
+
+ /**
+ * @hide
+ * Returns the current additional audio output device delay in milliseconds.
+ *
+ * @param deviceType
+ * @param address
+ * @return the additional output device delay. This is a non-negative number.
+ * {@code 0} is returned if unsupported.
+ */
+ @Override
+ @IntRange(from = 0)
+ public long getAdditionalOutputDeviceDelay(@NonNull AudioDeviceAttributes device) {
+ Objects.requireNonNull(device, "device must not be null");
+ final String key = "additional_output_device_delay";
+ final String reply = AudioSystem.getParameters(
+ key + "=" + AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType())
+ + "," + device.getAddress());
+ long delayMillis;
+ try {
+ delayMillis = Long.parseLong(reply.substring(key.length() + 1));
+ } catch (NullPointerException e) {
+ delayMillis = 0;
+ }
+ return delayMillis;
+ }
+
+ /**
+ * @hide
+ * Returns the maximum additional audio output device delay in milliseconds.
+ *
+ * @param deviceType
+ * @param address
+ * @return the maximum output device delay in milliseconds that can be set.
+ * This is a non-negative number
+ * representing the additional audio delay supported for the device.
+ * {@code 0} is returned if unsupported.
+ */
+ @Override
+ @IntRange(from = 0)
+ public long getMaxAdditionalOutputDeviceDelay(@NonNull AudioDeviceAttributes device) {
+ Objects.requireNonNull(device, "device must not be null");
+ final String key = "max_additional_output_device_delay";
+ final String reply = AudioSystem.getParameters(
+ key + "=" + AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType())
+ + "," + device.getAddress());
+ long delayMillis;
+ try {
+ delayMillis = Long.parseLong(reply.substring(key.length() + 1));
+ } catch (NullPointerException e) {
+ delayMillis = 0;
+ }
+ return delayMillis;
+ }
//======================
// misc
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index 9ba957e..e3757df 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -23,8 +23,11 @@
import com.android.internal.compat.CompatibilityChangeInfo;
import com.android.server.compat.config.Change;
+import com.android.server.compat.overrides.ChangeOverrides;
+import com.android.server.compat.overrides.OverrideValue;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
/**
@@ -253,6 +256,71 @@
return mDeferredOverrides != null && mDeferredOverrides.containsKey(packageName);
}
+ /**
+ * Checks whether a change has any package overrides.
+ * @return true if the change has at least one deferred override
+ */
+ boolean hasAnyPackageOverride() {
+ return mDeferredOverrides != null && !mDeferredOverrides.isEmpty();
+ }
+
+ /**
+ * Checks whether a change has any deferred overrides.
+ * @return true if the change has at least one deferred override
+ */
+ boolean hasAnyDeferredOverride() {
+ return mPackageOverrides != null && !mPackageOverrides.isEmpty();
+ }
+
+ void loadOverrides(ChangeOverrides changeOverrides) {
+ if (mDeferredOverrides == null) {
+ mDeferredOverrides = new HashMap<>();
+ }
+ mDeferredOverrides.clear();
+ for (OverrideValue override : changeOverrides.getDeferred().getOverrideValue()) {
+ mDeferredOverrides.put(override.getPackageName(), override.getEnabled());
+ }
+
+ if (mPackageOverrides == null) {
+ mPackageOverrides = new HashMap<>();
+ }
+ mPackageOverrides.clear();
+ for (OverrideValue override : changeOverrides.getValidated().getOverrideValue()) {
+ mPackageOverrides.put(override.getPackageName(), override.getEnabled());
+ }
+ }
+
+ ChangeOverrides saveOverrides() {
+ if (!hasAnyDeferredOverride() && !hasAnyPackageOverride()) {
+ return null;
+ }
+ ChangeOverrides changeOverrides = new ChangeOverrides();
+ changeOverrides.setChangeId(getId());
+ ChangeOverrides.Deferred deferredOverrides = new ChangeOverrides.Deferred();
+ List<OverrideValue> deferredList = deferredOverrides.getOverrideValue();
+ if (mDeferredOverrides != null) {
+ for (Map.Entry<String, Boolean> entry : mDeferredOverrides.entrySet()) {
+ OverrideValue override = new OverrideValue();
+ override.setPackageName(entry.getKey());
+ override.setEnabled(entry.getValue());
+ deferredList.add(override);
+ }
+ }
+ changeOverrides.setDeferred(deferredOverrides);
+ ChangeOverrides.Validated validatedOverrides = new ChangeOverrides.Validated();
+ List<OverrideValue> validatedList = validatedOverrides.getOverrideValue();
+ if (mPackageOverrides != null) {
+ for (Map.Entry<String, Boolean> entry : mPackageOverrides.entrySet()) {
+ OverrideValue override = new OverrideValue();
+ override.setPackageName(entry.getKey());
+ override.setEnabled(entry.getValue());
+ validatedList.add(override);
+ }
+ }
+ changeOverrides.setValidated(validatedOverrides);
+ return changeOverrides;
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder("ChangeId(")
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 69686a2..6b77b9d 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -34,7 +34,10 @@
import com.android.internal.compat.IOverrideValidator;
import com.android.internal.compat.OverrideAllowedState;
import com.android.server.compat.config.Change;
-import com.android.server.compat.config.XmlParser;
+import com.android.server.compat.config.Config;
+import com.android.server.compat.overrides.ChangeOverrides;
+import com.android.server.compat.overrides.Overrides;
+import com.android.server.compat.overrides.XmlWriter;
import com.android.server.pm.ApexManager;
import org.xmlpull.v1.XmlPullParserException;
@@ -60,11 +63,14 @@
final class CompatConfig {
private static final String TAG = "CompatConfig";
+ private static final String APP_COMPAT_DATA_DIR = "/data/misc/appcompat";
+ private static final String OVERRIDES_FILE = "compat_framework_overrides.xml";
@GuardedBy("mChanges")
private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>();
private final OverrideValidatorImpl mOverrideValidator;
+ private File mOverridesFile;
@VisibleForTesting
CompatConfig(AndroidBuildClassifier androidBuildClassifier, Context context) {
@@ -83,6 +89,8 @@
config.initConfigFromLib(Environment.buildPath(
apex.apexDirectory, "etc", "compatconfig"));
}
+ File overridesFile = new File(APP_COMPAT_DATA_DIR, OVERRIDES_FILE);
+ config.initOverrides(overridesFile);
config.invalidateCache();
return config;
}
@@ -202,6 +210,17 @@
* @throws IllegalStateException if overriding is not allowed
*/
boolean addOverride(long changeId, String packageName, boolean enabled) {
+ boolean alreadyKnown = addOverrideUnsafe(changeId, packageName, enabled);
+ saveOverrides();
+ invalidateCache();
+ return alreadyKnown;
+ }
+
+ /**
+ * Unsafe version of {@link #addOverride(long, String, boolean)}.
+ * It does not invalidate the cache nor save the overrides.
+ */
+ private boolean addOverrideUnsafe(long changeId, String packageName, boolean enabled) {
boolean alreadyKnown = true;
OverrideAllowedState allowedState =
mOverrideValidator.getOverrideAllowedState(changeId, packageName);
@@ -224,7 +243,6 @@
throw new IllegalStateException("Should only be able to override changes that "
+ "are allowed or can be deferred.");
}
- invalidateCache();
}
return alreadyKnown;
}
@@ -282,6 +300,17 @@
* @return {@code true} if an override existed;
*/
boolean removeOverride(long changeId, String packageName) {
+ boolean overrideExists = removeOverrideUnsafe(changeId, packageName);
+ saveOverrides();
+ invalidateCache();
+ return overrideExists;
+ }
+
+ /**
+ * Unsafe version of {@link #removeOverride(long, String)}.
+ * It does not invalidate the cache nor save the overrides.
+ */
+ private boolean removeOverrideUnsafe(long changeId, String packageName) {
boolean overrideExists = false;
synchronized (mChanges) {
CompatChange c = mChanges.get(changeId);
@@ -300,7 +329,6 @@
}
}
}
- invalidateCache();
return overrideExists;
}
@@ -315,12 +343,13 @@
void addOverrides(CompatibilityChangeConfig overrides, String packageName) {
synchronized (mChanges) {
for (Long changeId : overrides.enabledChanges()) {
- addOverride(changeId, packageName, true);
+ addOverrideUnsafe(changeId, packageName, true);
}
for (Long changeId : overrides.disabledChanges()) {
- addOverride(changeId, packageName, false);
+ addOverrideUnsafe(changeId, packageName, false);
}
+ saveOverrides();
invalidateCache();
}
}
@@ -337,8 +366,9 @@
synchronized (mChanges) {
for (int i = 0; i < mChanges.size(); ++i) {
CompatChange change = mChanges.valueAt(i);
- removeOverride(change.getId(), packageName);
+ removeOverrideUnsafe(change.getId(), packageName);
}
+ saveOverrides();
invalidateCache();
}
}
@@ -372,8 +402,10 @@
int enableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) {
long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion);
for (long changeId : changes) {
- addOverride(changeId, packageName, true);
+ addOverrideUnsafe(changeId, packageName, true);
}
+ saveOverrides();
+ invalidateCache();
return changes.length;
}
@@ -386,8 +418,10 @@
int disableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) {
long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion);
for (long changeId : changes) {
- addOverride(changeId, packageName, false);
+ addOverrideUnsafe(changeId, packageName, false);
}
+ saveOverrides();
+ invalidateCache();
return changes.length;
}
@@ -494,7 +528,8 @@
private void readConfig(File configFile) {
try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) {
- for (Change change : XmlParser.read(in).getCompatChange()) {
+ Config config = com.android.server.compat.config.XmlParser.read(in);
+ for (Change change : config.getCompatChange()) {
Slog.d(TAG, "Adding: " + change.toString());
addChange(new CompatChange(change));
}
@@ -503,6 +538,65 @@
}
}
+ void initOverrides(File overridesFile) {
+ if (!overridesFile.exists()) {
+ mOverridesFile = overridesFile;
+ // There have not been any overrides added yet.
+ return;
+ }
+
+ try (InputStream in = new BufferedInputStream(new FileInputStream(overridesFile))) {
+ Overrides overrides = com.android.server.compat.overrides.XmlParser.read(in);
+ for (ChangeOverrides changeOverrides : overrides.getChangeOverrides()) {
+ long changeId = changeOverrides.getChangeId();
+ CompatChange compatChange = mChanges.get(changeId);
+ if (compatChange == null) {
+ Slog.w(TAG, "Change ID " + changeId + " not found. "
+ + "Skipping overrides for it.");
+ continue;
+ }
+ compatChange.loadOverrides(changeOverrides);
+ }
+ } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
+ Slog.w(TAG, "Error processing " + overridesFile + " " + e.toString());
+ return;
+ }
+ mOverridesFile = overridesFile;
+ }
+
+ /**
+ * Persist compat framework overrides to /data/misc/appcompat/compat_framework_overrides.xml
+ */
+ void saveOverrides() {
+ if (mOverridesFile == null) {
+ return;
+ }
+ synchronized (mChanges) {
+ // Create the file if it doesn't already exist
+ try {
+ mOverridesFile.createNewFile();
+ } catch (IOException e) {
+ Slog.e(TAG, "Could not create override config file: " + e.toString());
+ return;
+ }
+ try (PrintWriter out = new PrintWriter(mOverridesFile)) {
+ XmlWriter writer = new XmlWriter(out);
+ Overrides overrides = new Overrides();
+ List<ChangeOverrides> changeOverridesList = overrides.getChangeOverrides();
+ for (int idx = 0; idx < mChanges.size(); ++idx) {
+ CompatChange c = mChanges.valueAt(idx);
+ ChangeOverrides changeOverrides = c.saveOverrides();
+ if (changeOverrides != null) {
+ changeOverridesList.add(changeOverrides);
+ }
+ }
+ XmlWriter.write(writer, overrides);
+ } catch (IOException e) {
+ Slog.e(TAG, e.toString());
+ }
+ }
+ }
+
IOverrideValidator getOverrideValidator() {
return mOverrideValidator;
}
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
index c70bb08..43d9ade 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -32,6 +32,7 @@
import android.content.Context;
import android.content.Intent;
import android.net.IDnsResolver;
+import android.net.InetAddresses;
import android.net.LinkProperties;
import android.net.Network;
import android.net.ResolverOptionsParcel;
@@ -190,7 +191,7 @@
for (String ipAddress : ipAddresses) {
try {
latestDnses.add(new Pair(hostname,
- InetAddress.parseNumericAddress(ipAddress)));
+ InetAddresses.parseNumericAddress(ipAddress)));
} catch (IllegalArgumentException e) {}
}
// Remove <hostname, ipAddress> pairs that should not be tracked.
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index 952193b..46c49e7 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -34,9 +34,9 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
+import com.android.net.module.util.NetworkStackConstants;
import com.android.server.net.BaseNetworkObserver;
-import java.net.Inet4Address;
import java.net.Inet6Address;
import java.util.Objects;
@@ -433,7 +433,7 @@
// clat IPv4 address itself (for those apps, it doesn't matter what
// the IP of the gateway is, only that there is one).
RouteInfo ipv4Default = new RouteInfo(
- new LinkAddress(Inet4Address.ANY, 0),
+ new LinkAddress(NetworkStackConstants.IPV4_ADDR_ANY, 0),
clatAddress.getAddress(), mIface);
stacked.addRoute(ipv4Default);
stacked.addLinkAddress(clatAddress);
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index ab0360b..bff1a5c 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -57,6 +57,7 @@
import com.android.server.ConnectivityService;
import java.io.PrintWriter;
+import java.util.Arrays;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
@@ -121,6 +122,13 @@
//
// When ConnectivityService disconnects a network:
// -----------------------------------------------
+// If a network is just connected, ConnectivityService will think it will be used soon, but might
+// not be used. Thus, a 5s timer will be held to prevent the network being torn down immediately.
+// This "nascent" state is implemented by the "lingering" logic below without relating to any
+// request, and is used in some cases where network requests race with network establishment. The
+// nascent state ends when the 5-second timer fires, or as soon as the network satisfies a
+// request, whichever is earlier. In this state, the network is considered in the background.
+//
// If a network has no chance of satisfying any requests (even if it were to become validated
// and enter state #5), ConnectivityService will disconnect the NetworkAgent's AsyncChannel.
//
@@ -209,23 +217,23 @@
// network is taken down. This usually only happens to the default network. Lingering ends with
// either the linger timeout expiring and the network being taken down, or the network
// satisfying a request again.
- public static class LingerTimer implements Comparable<LingerTimer> {
+ public static class InactivityTimer implements Comparable<InactivityTimer> {
public final int requestId;
public final long expiryMs;
- public LingerTimer(int requestId, long expiryMs) {
+ public InactivityTimer(int requestId, long expiryMs) {
this.requestId = requestId;
this.expiryMs = expiryMs;
}
public boolean equals(Object o) {
- if (!(o instanceof LingerTimer)) return false;
- LingerTimer other = (LingerTimer) o;
+ if (!(o instanceof InactivityTimer)) return false;
+ InactivityTimer other = (InactivityTimer) o;
return (requestId == other.requestId) && (expiryMs == other.expiryMs);
}
public int hashCode() {
return Objects.hash(requestId, expiryMs);
}
- public int compareTo(LingerTimer other) {
+ public int compareTo(InactivityTimer other) {
return (expiryMs != other.expiryMs) ?
Long.compare(expiryMs, other.expiryMs) :
Integer.compare(requestId, other.requestId);
@@ -268,30 +276,32 @@
*/
public static final int ARG_AGENT_SUCCESS = 1;
- // All linger timers for this network, sorted by expiry time. A linger timer is added whenever
+ // All inactivity timers for this network, sorted by expiry time. A timer is added whenever
// a request is moved to a network with a better score, regardless of whether the network is or
- // was lingering or not.
+ // was lingering or not. An inactivity timer is also added when a network connects
+ // without immediately satisfying any requests.
// TODO: determine if we can replace this with a smaller or unsorted data structure. (e.g.,
// SparseLongArray) combined with the timestamp of when the last timer is scheduled to fire.
- private final SortedSet<LingerTimer> mLingerTimers = new TreeSet<>();
+ private final SortedSet<InactivityTimer> mInactivityTimers = new TreeSet<>();
- // For fast lookups. Indexes into mLingerTimers by request ID.
- private final SparseArray<LingerTimer> mLingerTimerForRequest = new SparseArray<>();
+ // For fast lookups. Indexes into mInactivityTimers by request ID.
+ private final SparseArray<InactivityTimer> mInactivityTimerForRequest = new SparseArray<>();
- // Linger expiry timer. Armed whenever mLingerTimers is non-empty, regardless of whether the
- // network is lingering or not. Always set to the expiry of the LingerTimer that expires last.
- // When the timer fires, all linger state is cleared, and if the network has no requests, it is
- // torn down.
- private WakeupMessage mLingerMessage;
+ // Inactivity expiry timer. Armed whenever mInactivityTimers is non-empty, regardless of
+ // whether the network is inactive or not. Always set to the expiry of the mInactivityTimers
+ // that expires last. When the timer fires, all inactivity state is cleared, and if the network
+ // has no requests, it is torn down.
+ private WakeupMessage mInactivityMessage;
- // Linger expiry. Holds the expiry time of the linger timer, or 0 if the timer is not armed.
- private long mLingerExpiryMs;
+ // Inactivity expiry. Holds the expiry time of the inactivity timer, or 0 if the timer is not
+ // armed.
+ private long mInactivityExpiryMs;
- // Whether the network is lingering or not. Must be maintained separately from the above because
+ // Whether the network is inactive or not. Must be maintained separately from the above because
// it depends on the state of other networks and requests, which only ConnectivityService knows.
// (Example: we don't linger a network if it would become the best for a NetworkRequest if it
// validated).
- private boolean mLingering;
+ private boolean mInactive;
// This represents the quality of the network with no clear scale.
private int mScore;
@@ -329,7 +339,7 @@
private final QosCallbackTracker mQosCallbackTracker;
public NetworkAgentInfo(INetworkAgent na, Network net, NetworkInfo info,
- LinkProperties lp, NetworkCapabilities nc, int score, Context context,
+ @NonNull LinkProperties lp, @NonNull NetworkCapabilities nc, int score, Context context,
Handler handler, NetworkAgentConfig config, ConnectivityService connService, INetd netd,
IDnsResolver dnsResolver, INetworkManagementService nms, int factorySerialNumber,
int creatorUid, QosCallbackTracker qosCallbackTracker) {
@@ -894,20 +904,25 @@
/**
* Sets the specified requestId to linger on this network for the specified time. Called by
- * ConnectivityService when the request is moved to another network with a higher score.
+ * ConnectivityService when the request is moved to another network with a higher score, or
+ * when a network is newly created.
+ *
+ * @param requestId The requestId of the request that no longer need to be served by this
+ * network. Or {@link NetworkRequest.REQUEST_ID_NONE} if this is the
+ * {@code LingerTimer} for a newly created network.
*/
public void lingerRequest(int requestId, long now, long duration) {
- if (mLingerTimerForRequest.get(requestId) != null) {
+ if (mInactivityTimerForRequest.get(requestId) != null) {
// Cannot happen. Once a request is lingering on a particular network, we cannot
// re-linger it unless that network becomes the best for that request again, in which
// case we should have unlingered it.
Log.wtf(TAG, toShortString() + ": request " + requestId + " already lingered");
}
final long expiryMs = now + duration;
- LingerTimer timer = new LingerTimer(requestId, expiryMs);
- if (VDBG) Log.d(TAG, "Adding LingerTimer " + timer + " to " + toShortString());
- mLingerTimers.add(timer);
- mLingerTimerForRequest.put(requestId, timer);
+ InactivityTimer timer = new InactivityTimer(requestId, expiryMs);
+ if (VDBG) Log.d(TAG, "Adding InactivityTimer " + timer + " to " + toShortString());
+ mInactivityTimers.add(timer);
+ mInactivityTimerForRequest.put(requestId, timer);
}
/**
@@ -915,23 +930,25 @@
* Returns true if the given requestId was lingering on this network, false otherwise.
*/
public boolean unlingerRequest(int requestId) {
- LingerTimer timer = mLingerTimerForRequest.get(requestId);
+ InactivityTimer timer = mInactivityTimerForRequest.get(requestId);
if (timer != null) {
- if (VDBG) Log.d(TAG, "Removing LingerTimer " + timer + " from " + toShortString());
- mLingerTimers.remove(timer);
- mLingerTimerForRequest.remove(requestId);
+ if (VDBG) {
+ Log.d(TAG, "Removing InactivityTimer " + timer + " from " + toShortString());
+ }
+ mInactivityTimers.remove(timer);
+ mInactivityTimerForRequest.remove(requestId);
return true;
}
return false;
}
- public long getLingerExpiry() {
- return mLingerExpiryMs;
+ public long getInactivityExpiry() {
+ return mInactivityExpiryMs;
}
- public void updateLingerTimer() {
- long newExpiry = mLingerTimers.isEmpty() ? 0 : mLingerTimers.last().expiryMs;
- if (newExpiry == mLingerExpiryMs) return;
+ public void updateInactivityTimer() {
+ long newExpiry = mInactivityTimers.isEmpty() ? 0 : mInactivityTimers.last().expiryMs;
+ if (newExpiry == mInactivityExpiryMs) return;
// Even if we're going to reschedule the timer, cancel it first. This is because the
// semantics of WakeupMessage guarantee that if cancel is called then the alarm will
@@ -939,49 +956,65 @@
// WakeupMessage makes no such guarantees about rescheduling a message, so if mLingerMessage
// has already been dispatched, rescheduling to some time in the future won't stop it
// from calling its callback immediately.
- if (mLingerMessage != null) {
- mLingerMessage.cancel();
- mLingerMessage = null;
+ if (mInactivityMessage != null) {
+ mInactivityMessage.cancel();
+ mInactivityMessage = null;
}
if (newExpiry > 0) {
- mLingerMessage = new WakeupMessage(
+ mInactivityMessage = new WakeupMessage(
mContext, mHandler,
"NETWORK_LINGER_COMPLETE." + network.getNetId() /* cmdName */,
EVENT_NETWORK_LINGER_COMPLETE /* cmd */,
0 /* arg1 (unused) */, 0 /* arg2 (unused) */,
this /* obj (NetworkAgentInfo) */);
- mLingerMessage.schedule(newExpiry);
+ mInactivityMessage.schedule(newExpiry);
}
- mLingerExpiryMs = newExpiry;
+ mInactivityExpiryMs = newExpiry;
}
- public void linger() {
- mLingering = true;
+ public void setInactive() {
+ mInactive = true;
}
- public void unlinger() {
- mLingering = false;
+ public void unsetInactive() {
+ mInactive = false;
+ }
+
+ public boolean isInactive() {
+ return mInactive;
}
public boolean isLingering() {
- return mLingering;
+ return mInactive && !isNascent();
}
- public void clearLingerState() {
- if (mLingerMessage != null) {
- mLingerMessage.cancel();
- mLingerMessage = null;
+ /**
+ * Return whether the network is just connected and about to be torn down because of not
+ * satisfying any request.
+ */
+ public boolean isNascent() {
+ return mInactive && mInactivityTimers.size() == 1
+ && mInactivityTimers.first().requestId == NetworkRequest.REQUEST_ID_NONE;
+ }
+
+ public void clearInactivityState() {
+ if (mInactivityMessage != null) {
+ mInactivityMessage.cancel();
+ mInactivityMessage = null;
}
- mLingerTimers.clear();
- mLingerTimerForRequest.clear();
- updateLingerTimer(); // Sets mLingerExpiryMs, cancels and nulls out mLingerMessage.
- mLingering = false;
+ mInactivityTimers.clear();
+ mInactivityTimerForRequest.clear();
+ // Sets mInactivityExpiryMs, cancels and nulls out mInactivityMessage.
+ updateInactivityTimer();
+ mInactive = false;
}
- public void dumpLingerTimers(PrintWriter pw) {
- for (LingerTimer timer : mLingerTimers) { pw.println(timer); }
+ public void dumpInactivityTimers(PrintWriter pw) {
+ for (InactivityTimer timer : mInactivityTimers) {
+ pw.println(timer);
+ }
}
/**
@@ -1015,7 +1048,7 @@
+ "network{" + network + "} handle{" + network.getNetworkHandle() + "} ni{"
+ networkInfo.toShortString() + "} "
+ " Score{" + getCurrentScore() + "} "
- + (isLingering() ? " lingering" : "")
+ + (isNascent() ? " nascent" : (isLingering() ? " lingering" : ""))
+ (everValidated ? " everValidated" : "")
+ (lastValidated ? " lastValidated" : "")
+ (partialConnectivity ? " partialConnectivity" : "")
@@ -1025,6 +1058,8 @@
+ (networkAgentConfig.acceptUnvalidated ? " acceptUnvalidated" : "")
+ (networkAgentConfig.acceptPartialConnectivity ? " acceptPartialConnectivity" : "")
+ (clatd.isStarted() ? " clat{" + clatd + "} " : "")
+ + (declaredUnderlyingNetworks != null
+ ? " underlying{" + Arrays.toString(declaredUnderlyingNetworks) + "}" : "")
+ " lp{" + linkProperties + "}"
+ " nc{" + networkCapabilities + "}"
+ "}";
diff --git a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
index a7be657..5e6b9f3 100644
--- a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
+++ b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
@@ -686,7 +686,7 @@
mHostname = hostname;
mMeasurement.description = "DNS TLS dst{" + mTarget.getHostAddress() + "} hostname{"
- + TextUtils.emptyIfNull(mHostname) + "}";
+ + (mHostname == null ? "" : mHostname) + "}";
}
private SSLSocket setupSSLSocket() throws IOException {
diff --git a/services/core/java/com/android/server/connectivity/PacProxyInstaller.java b/services/core/java/com/android/server/connectivity/PacProxyInstaller.java
index 5dc8c1a..aadaf4d 100644
--- a/services/core/java/com/android/server/connectivity/PacProxyInstaller.java
+++ b/services/core/java/com/android/server/connectivity/PacProxyInstaller.java
@@ -16,7 +16,6 @@
package com.android.server.connectivity;
-import android.annotation.NonNull;
import android.annotation.WorkerThread;
import android.app.AlarmManager;
import android.app.PendingIntent;
@@ -72,6 +71,10 @@
private static final int DELAY_LONG = 4;
private static final long MAX_PAC_SIZE = 20 * 1000 * 1000;
+ // Return values for #setCurrentProxyScriptUrl
+ public static final boolean DONT_SEND_BROADCAST = false;
+ public static final boolean DO_SEND_BROADCAST = true;
+
private String mCurrentPac;
@GuardedBy("mProxyLock")
private volatile Uri mPacUrl = Uri.EMPTY;
@@ -90,7 +93,7 @@
private volatile boolean mHasSentBroadcast;
private volatile boolean mHasDownloaded;
- private final Handler mConnectivityHandler;
+ private Handler mConnectivityHandler;
private final int mProxyMessage;
/**
@@ -99,13 +102,6 @@
private final Object mProxyLock = new Object();
/**
- * Lock ensuring consistency between the values of mHasSentBroadcast, mHasDownloaded, the
- * last URL and port, and the broadcast message being sent with the correct arguments.
- * TODO : this should probably protect all instances of these variables
- */
- private final Object mBroadcastStateLock = new Object();
-
- /**
* Runnable to download PAC script.
* The behavior relies on the assumption it always runs on mNetThread to guarantee that the
* latest data fetched from mPacUrl is stored in mProxyService.
@@ -150,7 +146,7 @@
}
}
- public PacProxyInstaller(@NonNull Context context, @NonNull Handler handler, int proxyMessage) {
+ public PacProxyInstaller(Context context, Handler handler, int proxyMessage) {
mContext = context;
mLastPort = -1;
final HandlerThread netThread = new HandlerThread("android.pacproxyinstaller",
@@ -180,27 +176,31 @@
* PacProxyInstaller will trigger a new broadcast when it is ready.
*
* @param proxy Proxy information that is about to be broadcast.
+ * @return Returns whether the broadcast should be sent : either DO_ or DONT_SEND_BROADCAST
*/
- public void setCurrentProxyScriptUrl(@NonNull ProxyInfo proxy) {
- synchronized (mBroadcastStateLock) {
- if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) {
- if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) return;
- mPacUrl = proxy.getPacFileUrl();
- mCurrentDelay = DELAY_1;
- mHasSentBroadcast = false;
- mHasDownloaded = false;
- getAlarmManager().cancel(mPacRefreshIntent);
- bind();
- } else {
- getAlarmManager().cancel(mPacRefreshIntent);
- synchronized (mProxyLock) {
- mPacUrl = Uri.EMPTY;
- mCurrentPac = null;
- if (mProxyService != null) {
- unbind();
- }
+ public synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) {
+ if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) {
+ if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) {
+ // Allow to send broadcast, nothing to do.
+ return DO_SEND_BROADCAST;
+ }
+ mPacUrl = proxy.getPacFileUrl();
+ mCurrentDelay = DELAY_1;
+ mHasSentBroadcast = false;
+ mHasDownloaded = false;
+ getAlarmManager().cancel(mPacRefreshIntent);
+ bind();
+ return DONT_SEND_BROADCAST;
+ } else {
+ getAlarmManager().cancel(mPacRefreshIntent);
+ synchronized (mProxyLock) {
+ mPacUrl = Uri.EMPTY;
+ mCurrentPac = null;
+ if (mProxyService != null) {
+ unbind();
}
}
+ return DO_SEND_BROADCAST;
}
}
@@ -275,7 +275,6 @@
getAlarmManager().set(AlarmManager.ELAPSED_REALTIME, timeTillTrigger, mPacRefreshIntent);
}
- @GuardedBy("mProxyLock")
private void setCurrentProxyScript(String script) {
if (mProxyService == null) {
Log.e(TAG, "setCurrentProxyScript: no proxy service");
@@ -348,9 +347,6 @@
public void setProxyPort(int port) {
if (mLastPort != -1) {
// Always need to send if port changed
- // TODO: Here lacks synchronization because this write cannot
- // guarantee that it's visible from sendProxyIfNeeded() when
- // it's called by a Runnable which is post by mNetThread.
mHasSentBroadcast = false;
}
mLastPort = port;
@@ -390,15 +386,13 @@
mConnectivityHandler.sendMessage(mConnectivityHandler.obtainMessage(mProxyMessage, proxy));
}
- private void sendProxyIfNeeded() {
- synchronized (mBroadcastStateLock) {
- if (!mHasDownloaded || (mLastPort == -1)) {
- return;
- }
- if (!mHasSentBroadcast) {
- sendPacBroadcast(ProxyInfo.buildPacProxy(mPacUrl, mLastPort));
- mHasSentBroadcast = true;
- }
+ private synchronized void sendProxyIfNeeded() {
+ if (!mHasDownloaded || (mLastPort == -1)) {
+ return;
+ }
+ if (!mHasSentBroadcast) {
+ sendPacBroadcast(ProxyInfo.buildPacProxy(mPacUrl, mLastPort));
+ mHasSentBroadcast = true;
}
}
}
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index d507b5f..8d21f6f 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -265,7 +265,10 @@
for (Entry<Integer, Boolean> app : apps.entrySet()) {
List<Integer> list = app.getValue() ? system : network;
for (int user : users) {
- list.add(UserHandle.getUid(user, app.getKey()));
+ final UserHandle handle = UserHandle.of(user);
+ if (handle == null) continue;
+
+ list.add(UserHandle.getUid(handle, app.getKey()));
}
}
try {
@@ -550,7 +553,10 @@
for (UidRange range : ranges) {
for (int userId = range.getStartUser(); userId <= range.getEndUser(); userId++) {
for (int appId : appIds) {
- final int uid = UserHandle.getUid(userId, appId);
+ final UserHandle handle = UserHandle.of(userId);
+ if (handle == null) continue;
+
+ final int uid = UserHandle.getUid(handle, appId);
if (range.contains(uid)) {
result.add(uid);
}
diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java
index b618d2b..d83ff83 100644
--- a/services/core/java/com/android/server/connectivity/ProxyTracker.java
+++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java
@@ -226,9 +226,9 @@
final ProxyInfo defaultProxy = getDefaultProxy();
final ProxyInfo proxyInfo = null != defaultProxy ?
defaultProxy : ProxyInfo.buildDirectProxy("", 0, Collections.emptyList());
- mPacProxyInstaller.setCurrentProxyScriptUrl(proxyInfo);
- if (!shouldSendBroadcast(proxyInfo)) {
+ if (mPacProxyInstaller.setCurrentProxyScriptUrl(proxyInfo)
+ == PacProxyInstaller.DONT_SEND_BROADCAST) {
return;
}
if (DBG) Log.d(TAG, "sending Proxy Broadcast for " + proxyInfo);
@@ -244,13 +244,6 @@
}
}
- private boolean shouldSendBroadcast(ProxyInfo proxy) {
- if (Uri.EMPTY.equals(proxy.getPacFileUrl())) return false;
- if (proxy.getPacFileUrl().equals(proxy.getPacFileUrl())
- && (proxy.getPort() > 0)) return true;
- return true;
- }
-
/**
* Sets the global proxy in memory. Also writes the values to the global settings of the device.
*
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index fb1e819..fc2c7e0 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -51,6 +51,7 @@
import android.net.INetd;
import android.net.INetworkManagementEventObserver;
import android.net.Ikev2VpnProfile;
+import android.net.InetAddresses;
import android.net.IpPrefix;
import android.net.IpSecManager;
import android.net.IpSecManager.IpSecTunnelInterface;
@@ -70,6 +71,7 @@
import android.net.RouteInfo;
import android.net.UidRange;
import android.net.UidRangeParcel;
+import android.net.UnderlyingNetworkInfo;
import android.net.VpnManager;
import android.net.VpnService;
import android.net.ipsec.ike.ChildSessionCallback;
@@ -109,8 +111,8 @@
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
-import com.android.internal.net.VpnInfo;
import com.android.internal.net.VpnProfile;
+import com.android.net.module.util.NetworkStackConstants;
import com.android.server.DeviceIdleInternal;
import com.android.server.LocalServices;
import com.android.server.net.BaseNetworkObserver;
@@ -203,6 +205,7 @@
protected final NetworkCapabilities mNetworkCapabilities;
private final SystemServices mSystemServices;
private final Ikev2SessionCreator mIkev2SessionCreator;
+ private final UserManager mUserManager;
/**
* Whether to keep the connection active after rebooting, or upgrading or reinstalling. This
@@ -277,6 +280,10 @@
return LocalServices.getService(DeviceIdleInternal.class);
}
+ public PendingIntent getIntentForStatusPanel(Context context) {
+ return VpnConfig.getIntentForStatusPanel(context);
+ }
+
public void sendArgumentsToDaemon(
final String daemon, final LocalSocket socket, final String[] arguments,
final RetryScheduler retryScheduler) throws IOException, InterruptedException {
@@ -327,7 +334,7 @@
public InetAddress resolve(final String endpoint)
throws ExecutionException, InterruptedException {
try {
- return InetAddress.parseNumericAddress(endpoint);
+ return InetAddresses.parseNumericAddress(endpoint);
} catch (IllegalArgumentException e) {
// Endpoint is not numeric : fall through and resolve
}
@@ -405,6 +412,7 @@
mLooper = looper;
mSystemServices = systemServices;
mIkev2SessionCreator = ikev2SessionCreator;
+ mUserManager = mContext.getSystemService(UserManager.class);
mPackage = VpnConfig.LEGACY_VPN;
mOwnerUID = getAppUid(mPackage, mUserId);
@@ -426,6 +434,7 @@
mNetworkCapabilities = new NetworkCapabilities();
mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN);
mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
+ mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
loadAlwaysOnPackage(keyStore);
}
@@ -1118,7 +1127,7 @@
if (mConfig.dnsServers != null) {
for (String dnsServer : mConfig.dnsServers) {
- InetAddress address = InetAddress.parseNumericAddress(dnsServer);
+ InetAddress address = InetAddresses.parseNumericAddress(dnsServer);
lp.addDnsServer(address);
allowIPv4 |= address instanceof Inet4Address;
allowIPv6 |= address instanceof Inet6Address;
@@ -1128,10 +1137,12 @@
lp.setHttpProxy(mConfig.proxyInfo);
if (!allowIPv4) {
- lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
+ lp.addRoute(new RouteInfo(new IpPrefix(
+ NetworkStackConstants.IPV4_ADDR_ANY, 0), RTN_UNREACHABLE));
}
if (!allowIPv6) {
- lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
+ lp.addRoute(new RouteInfo(new IpPrefix(
+ NetworkStackConstants.IPV6_ADDR_ANY, 0), RTN_UNREACHABLE));
}
// Concatenate search domains into a string.
@@ -1430,7 +1441,7 @@
final long token = Binder.clearCallingIdentity();
List<UserInfo> users;
try {
- users = UserManager.get(mContext).getAliveUsers();
+ users = mUserManager.getAliveUsers();
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1514,7 +1525,7 @@
*/
public void onUserAdded(int userId) {
// If the user is restricted tie them to the parent user's VPN
- UserInfo user = UserManager.get(mContext).getUserInfo(userId);
+ UserInfo user = mUserManager.getUserInfo(userId);
if (user.isRestricted() && user.restrictedProfileParentId == mUserId) {
synchronized(Vpn.this) {
final Set<UidRange> existingRanges = mNetworkCapabilities.getUids();
@@ -1542,7 +1553,7 @@
*/
public void onUserRemoved(int userId) {
// clean up if restricted
- UserInfo user = UserManager.get(mContext).getUserInfo(userId);
+ UserInfo user = mUserManager.getUserInfo(userId);
if (user.isRestricted() && user.restrictedProfileParentId == mUserId) {
synchronized(Vpn.this) {
final Set<UidRange> existingRanges = mNetworkCapabilities.getUids();
@@ -1767,7 +1778,7 @@
private void prepareStatusIntent() {
final long token = Binder.clearCallingIdentity();
try {
- mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext);
+ mStatusIntent = mDeps.getIntentForStatusPanel(mContext);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1816,18 +1827,15 @@
}
/**
- * This method should only be called by ConnectivityService because it doesn't
- * have enough data to fill VpnInfo.primaryUnderlyingIface field.
+ * This method should not be called if underlying interfaces field is needed, because it doesn't
+ * have enough data to fill VpnInfo.underlyingIfaces field.
*/
- public synchronized VpnInfo getVpnInfo() {
+ public synchronized UnderlyingNetworkInfo getUnderlyingNetworkInfo() {
if (!isRunningLocked()) {
return null;
}
- VpnInfo info = new VpnInfo();
- info.ownerUid = mOwnerUID;
- info.vpnIface = mInterface;
- return info;
+ return new UnderlyingNetworkInfo(mOwnerUID, mInterface, new ArrayList<>());
}
public synchronized boolean appliesToUid(int uid) {
@@ -1970,8 +1978,7 @@
private void enforceNotRestrictedUser() {
Binder.withCleanCallingIdentity(() -> {
- final UserManager mgr = UserManager.get(mContext);
- final UserInfo user = mgr.getUserInfo(mUserId);
+ final UserInfo user = mUserManager.getUserInfo(mUserId);
if (user.isRestricted()) {
throw new SecurityException("Restricted users cannot configure VPNs");
@@ -1984,30 +1991,30 @@
* secondary thread to perform connection work, returning quickly.
*
* Should only be called to respond to Binder requests as this enforces caller permission. Use
- * {@link #startLegacyVpnPrivileged(VpnProfile, KeyStore, LinkProperties)} to skip the
+ * {@link #startLegacyVpnPrivileged(VpnProfile, KeyStore, Network, LinkProperties)} to skip the
* permission check only when the caller is trusted (or the call is initiated by the system).
*/
- public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, LinkProperties egress) {
+ public void startLegacyVpn(VpnProfile profile, KeyStore keyStore, @Nullable Network underlying,
+ LinkProperties egress) {
enforceControlPermission();
final long token = Binder.clearCallingIdentity();
try {
- startLegacyVpnPrivileged(profile, keyStore, egress);
+ startLegacyVpnPrivileged(profile, keyStore, underlying, egress);
} finally {
Binder.restoreCallingIdentity(token);
}
}
/**
- * Like {@link #startLegacyVpn(VpnProfile, KeyStore, LinkProperties)}, but does not check
- * permissions under the assumption that the caller is the system.
+ * Like {@link #startLegacyVpn(VpnProfile, KeyStore, Network, LinkProperties)}, but does not
+ * check permissions under the assumption that the caller is the system.
*
* Callers are responsible for checking permissions if needed.
*/
public void startLegacyVpnPrivileged(VpnProfile profile, KeyStore keyStore,
- LinkProperties egress) {
- UserManager mgr = UserManager.get(mContext);
- UserInfo user = mgr.getUserInfo(mUserId);
- if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
+ @Nullable Network underlying, @NonNull LinkProperties egress) {
+ UserInfo user = mUserManager.getUserInfo(mUserId);
+ if (user.isRestricted() || mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
new UserHandle(mUserId))) {
throw new SecurityException("Restricted users cannot establish VPNs");
}
@@ -2130,6 +2137,9 @@
config.session = profile.name;
config.isMetered = false;
config.proxyInfo = profile.proxy;
+ if (underlying != null) {
+ config.underlyingNetworks = new Network[] { underlying };
+ }
config.addLegacyRoutes(profile.routes);
if (!profile.dnsServers.isEmpty()) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 8e50bb4..5d1c4e6 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -621,7 +621,14 @@
mWakeUpMessageReceived = false;
if (isTvDeviceEnabled()) {
- mCecController.setOption(OptionKey.WAKEUP, tv().getAutoWakeup());
+ boolean autoWakeupEnabled =
+ readBooleanSetting(Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED, true);
+ boolean autoDeviceOffEnabled =
+ readBooleanSetting(Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, true);
+
+ mCecController.setOption(OptionKey.WAKEUP, autoWakeupEnabled);
+ tv().setAutoWakeup(autoWakeupEnabled);
+ tv().setAutoDeviceOff(autoDeviceOffEnabled);
}
int reason = -1;
switch (initiatedBy) {
diff --git a/services/core/java/com/android/server/hdmi/RoutingControlAction.java b/services/core/java/com/android/server/hdmi/RoutingControlAction.java
index 6c8694e..9a52c19 100644
--- a/services/core/java/com/android/server/hdmi/RoutingControlAction.java
+++ b/services/core/java/com/android/server/hdmi/RoutingControlAction.java
@@ -17,8 +17,8 @@
package com.android.server.hdmi;
import android.annotation.Nullable;
-import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
import android.os.RemoteException;
import android.util.Slog;
diff --git a/services/core/java/com/android/server/location/contexthub/OWNERS b/services/core/java/com/android/server/location/contexthub/OWNERS
index d4393d6..90c2330 100644
--- a/services/core/java/com/android/server/location/contexthub/OWNERS
+++ b/services/core/java/com/android/server/location/contexthub/OWNERS
@@ -1,2 +1,3 @@
arthuri@google.com
bduddie@google.com
+stange@google.com
diff --git a/services/core/java/com/android/server/location/timezone/OWNERS b/services/core/java/com/android/server/location/timezone/OWNERS
deleted file mode 100644
index 28aff18..0000000
--- a/services/core/java/com/android/server/location/timezone/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-# Bug component: 847766
-nfuller@google.com
-include /core/java/android/app/timedetector/OWNERS
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index 81d07cc..c4225ed 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -88,6 +88,7 @@
private static final String CHILD_PROFILE_LOCK_FILE = "gatekeeper.profile.key";
private static final String REBOOT_ESCROW_FILE = "reboot.escrow.key";
+ private static final String REBOOT_ESCROW_SERVER_BLOB = "reboot.escrow.server.blob.key";
private static final String SYNTHETIC_PASSWORD_DIRECTORY = "spblob/";
@@ -317,6 +318,22 @@
deleteFile(getRebootEscrowFile(userId));
}
+ public void writeRebootEscrowServerBlob(byte[] serverBlob) {
+ writeFile(getRebootEscrowServerBlob(), serverBlob);
+ }
+
+ public byte[] readRebootEscrowServerBlob() {
+ return readFile(getRebootEscrowServerBlob());
+ }
+
+ public boolean hasRebootEscrowServerBlob() {
+ return hasFile(getRebootEscrowServerBlob());
+ }
+
+ public void removeRebootEscrowServerBlob() {
+ deleteFile(getRebootEscrowServerBlob());
+ }
+
public boolean hasPassword(int userId) {
return hasFile(getLockPasswordFilename(userId));
}
@@ -445,6 +462,12 @@
return getLockCredentialFilePathForUser(userId, REBOOT_ESCROW_FILE);
}
+ @VisibleForTesting
+ String getRebootEscrowServerBlob() {
+ // There is a single copy of server blob for all users.
+ return getLockCredentialFilePathForUser(UserHandle.USER_SYSTEM, REBOOT_ESCROW_SERVER_BLOB);
+ }
+
private String getLockCredentialFilePathForUser(int userId, String basename) {
String dataSystemDirectory = Environment.getDataDirectory().getAbsolutePath() +
SYSTEM_DIRECTORY;
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index fbec915..06962d4 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -124,26 +124,28 @@
static class Injector {
protected Context mContext;
private final RebootEscrowKeyStoreManager mKeyStoreManager;
- private final RebootEscrowProviderInterface mRebootEscrowProvider;
+ private final LockSettingsStorage mStorage;
+ private RebootEscrowProviderInterface mRebootEscrowProvider;
- Injector(Context context) {
+ Injector(Context context, LockSettingsStorage storage) {
mContext = context;
+ mStorage = storage;
mKeyStoreManager = new RebootEscrowKeyStoreManager();
+ }
- RebootEscrowProviderInterface rebootEscrowProvider = null;
- // TODO(xunchang) add implementation for server based ror.
+ private RebootEscrowProviderInterface createRebootEscrowProvider() {
+ RebootEscrowProviderInterface rebootEscrowProvider;
if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_OTA,
"server_based_ror_enabled", false)) {
- Slog.e(TAG, "Server based ror isn't implemented yet.");
+ rebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(mContext, mStorage);
} else {
rebootEscrowProvider = new RebootEscrowProviderHalImpl();
}
- if (rebootEscrowProvider != null && rebootEscrowProvider.hasRebootEscrowSupport()) {
- mRebootEscrowProvider = rebootEscrowProvider;
- } else {
- mRebootEscrowProvider = null;
+ if (rebootEscrowProvider.hasRebootEscrowSupport()) {
+ return rebootEscrowProvider;
}
+ return null;
}
public Context getContext() {
@@ -159,6 +161,12 @@
}
public RebootEscrowProviderInterface getRebootEscrowProvider() {
+ // Initialize for the provider lazily. Because the device_config and service
+ // implementation apps may change when system server is running.
+ if (mRebootEscrowProvider == null) {
+ mRebootEscrowProvider = createRebootEscrowProvider();
+ }
+
return mRebootEscrowProvider;
}
@@ -177,7 +185,7 @@
}
RebootEscrowManager(Context context, Callbacks callbacks, LockSettingsStorage storage) {
- this(new Injector(context), callbacks, storage);
+ this(new Injector(context, storage), callbacks, storage);
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java
new file mode 100644
index 0000000..ba1a680
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.RemoteException;
+import android.provider.DeviceConfig;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.locksettings.ResumeOnRebootServiceProvider.ResumeOnRebootServiceConnection;
+
+import java.io.IOException;
+import java.util.concurrent.TimeoutException;
+
+import javax.crypto.SecretKey;
+
+/**
+ * An implementation of the {@link RebootEscrowProviderInterface} by communicating with server to
+ * encrypt & decrypt the blob.
+ */
+class RebootEscrowProviderServerBasedImpl implements RebootEscrowProviderInterface {
+ private static final String TAG = "RebootEscrowProvider";
+
+ // Timeout for service binding
+ private static final long DEFAULT_SERVICE_TIMEOUT_IN_SECONDS = 10;
+
+ /**
+ * Use the default lifetime of 10 minutes. The lifetime covers the following activities:
+ * Server wrap secret -> device reboot -> server unwrap blob.
+ */
+ private static final long DEFAULT_SERVER_BLOB_LIFETIME_IN_MILLIS = 600_1000;
+
+ private final LockSettingsStorage mStorage;
+
+ private final Injector mInjector;
+
+ static class Injector {
+ private ResumeOnRebootServiceConnection mServiceConnection = null;
+
+ Injector(Context context) {
+ mServiceConnection = new ResumeOnRebootServiceProvider(context).getServiceConnection();
+ if (mServiceConnection == null) {
+ Slog.e(TAG, "Failed to resolve resume on reboot server service.");
+ }
+ }
+
+ Injector(ResumeOnRebootServiceConnection serviceConnection) {
+ mServiceConnection = serviceConnection;
+ }
+
+ @Nullable
+ private ResumeOnRebootServiceConnection getServiceConnection() {
+ return mServiceConnection;
+ }
+
+ long getServiceTimeoutInSeconds() {
+ return DeviceConfig.getLong(DeviceConfig.NAMESPACE_OTA,
+ "server_based_service_timeout_in_seconds",
+ DEFAULT_SERVICE_TIMEOUT_IN_SECONDS);
+ }
+
+ long getServerBlobLifetimeInMillis() {
+ return DeviceConfig.getLong(DeviceConfig.NAMESPACE_OTA,
+ "server_based_server_blob_lifetime_in_millis",
+ DEFAULT_SERVER_BLOB_LIFETIME_IN_MILLIS);
+ }
+ }
+
+ RebootEscrowProviderServerBasedImpl(Context context, LockSettingsStorage storage) {
+ this(storage, new Injector(context));
+ }
+
+ @VisibleForTesting
+ RebootEscrowProviderServerBasedImpl(LockSettingsStorage storage, Injector injector) {
+ mStorage = storage;
+ mInjector = injector;
+ }
+
+ @Override
+ public boolean hasRebootEscrowSupport() {
+ return mInjector.getServiceConnection() != null;
+ }
+
+ private byte[] unwrapServerBlob(byte[] serverBlob, SecretKey decryptionKey) throws
+ TimeoutException, RemoteException, IOException {
+ ResumeOnRebootServiceConnection serviceConnection = mInjector.getServiceConnection();
+ if (serviceConnection == null) {
+ Slog.w(TAG, "Had reboot escrow data for users, but resume on reboot server"
+ + " service is unavailable");
+ return null;
+ }
+
+ // Decrypt with k_k from the key store first.
+ byte[] decryptedBlob = AesEncryptionUtil.decrypt(decryptionKey, serverBlob);
+ if (decryptedBlob == null) {
+ Slog.w(TAG, "Decrypted server blob should not be null");
+ return null;
+ }
+
+ // Ask the server connection service to decrypt the inner layer, to get the reboot
+ // escrow key (k_s).
+ serviceConnection.bindToService(mInjector.getServiceTimeoutInSeconds());
+ byte[] escrowKeyBytes = serviceConnection.unwrap(decryptedBlob,
+ mInjector.getServiceTimeoutInSeconds());
+ serviceConnection.unbindService();
+
+ return escrowKeyBytes;
+ }
+
+ @Override
+ public RebootEscrowKey getAndClearRebootEscrowKey(SecretKey decryptionKey) {
+ byte[] serverBlob = mStorage.readRebootEscrowServerBlob();
+ // Delete the server blob in storage.
+ mStorage.removeRebootEscrowServerBlob();
+ if (serverBlob == null) {
+ Slog.w(TAG, "Failed to read reboot escrow server blob from storage");
+ return null;
+ }
+
+ try {
+ byte[] escrowKeyBytes = unwrapServerBlob(serverBlob, decryptionKey);
+ if (escrowKeyBytes == null) {
+ Slog.w(TAG, "Decrypted reboot escrow key bytes should not be null");
+ return null;
+ } else if (escrowKeyBytes.length != 32) {
+ Slog.e(TAG, "Decrypted reboot escrow key has incorrect size "
+ + escrowKeyBytes.length);
+ return null;
+ }
+
+ return RebootEscrowKey.fromKeyBytes(escrowKeyBytes);
+ } catch (TimeoutException | RemoteException | IOException e) {
+ Slog.w(TAG, "Failed to decrypt the server blob ", e);
+ return null;
+ }
+ }
+
+ @Override
+ public void clearRebootEscrowKey() {
+ mStorage.removeRebootEscrowServerBlob();
+ }
+
+ private byte[] wrapEscrowKey(byte[] escrowKeyBytes, SecretKey encryptionKey) throws
+ TimeoutException, RemoteException, IOException {
+ ResumeOnRebootServiceConnection serviceConnection = mInjector.getServiceConnection();
+ if (serviceConnection == null) {
+ Slog.w(TAG, "Failed to encrypt the reboot escrow key: resume on reboot server"
+ + " service is unavailable");
+ return null;
+ }
+
+ serviceConnection.bindToService(mInjector.getServiceTimeoutInSeconds());
+ // Ask the server connection service to encrypt the reboot escrow key.
+ byte[] serverEncryptedBlob = serviceConnection.wrapBlob(escrowKeyBytes,
+ mInjector.getServerBlobLifetimeInMillis(), mInjector.getServiceTimeoutInSeconds());
+ serviceConnection.unbindService();
+
+ if (serverEncryptedBlob == null) {
+ Slog.w(TAG, "Server encrypted reboot escrow key cannot be null");
+ return null;
+ }
+
+ // Additionally wrap the server blob with a local key.
+ return AesEncryptionUtil.encrypt(encryptionKey, serverEncryptedBlob);
+ }
+
+ @Override
+ public boolean storeRebootEscrowKey(RebootEscrowKey escrowKey, SecretKey encryptionKey) {
+ mStorage.removeRebootEscrowServerBlob();
+ try {
+ byte[] wrappedBlob = wrapEscrowKey(escrowKey.getKeyBytes(), encryptionKey);
+ if (wrappedBlob == null) {
+ Slog.w(TAG, "Failed to encrypt the reboot escrow key");
+ return false;
+ }
+ mStorage.writeRebootEscrowServerBlob(wrappedBlob);
+
+ Slog.i(TAG, "Reboot escrow key encrypted and stored.");
+ return true;
+ } catch (TimeoutException | RemoteException | IOException e) {
+ Slog.w(TAG, "Failed to encrypt the reboot escrow key ", e);
+ }
+
+ return false;
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java b/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java
index 8399f54..a1e18bd 100644
--- a/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java
+++ b/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java
@@ -106,6 +106,8 @@
private final Context mContext;
private final ComponentName mComponentName;
private IResumeOnRebootService mBinder;
+ @Nullable
+ ServiceConnection mServiceConnection;
private ResumeOnRebootServiceConnection(Context context,
@NonNull ComponentName componentName) {
@@ -115,17 +117,9 @@
/** Unbind from the service */
public void unbindService() {
- mContext.unbindService(new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- mBinder = null;
-
- }
- });
+ if (mServiceConnection != null) {
+ mContext.unbindService(mServiceConnection);
+ }
}
/** Bind to the service */
@@ -134,17 +128,19 @@
CountDownLatch connectionLatch = new CountDownLatch(1);
Intent intent = new Intent();
intent.setComponent(mComponentName);
- final boolean success = mContext.bindServiceAsUser(intent, new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- mBinder = IResumeOnRebootService.Stub.asInterface(service);
- connectionLatch.countDown();
- }
+ mServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mBinder = IResumeOnRebootService.Stub.asInterface(service);
+ connectionLatch.countDown();
+ }
- @Override
- public void onServiceDisconnected(ComponentName name) {
- }
- },
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mBinder = null;
+ }
+ };
+ final boolean success = mContext.bindServiceAsUser(intent, mServiceConnection,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
BackgroundThread.getHandler(), UserHandle.SYSTEM);
diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java
index ea2788c..6d1c680 100644
--- a/services/core/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java
@@ -155,7 +155,7 @@
try {
// Use the privileged method because Lockdown VPN is initiated by the system, so
// no additional permission checks are necessary.
- mVpn.startLegacyVpnPrivileged(mProfile, mKeyStore, egressProp);
+ mVpn.startLegacyVpnPrivileged(mProfile, mKeyStore, null, egressProp);
} catch (IllegalStateException e) {
mAcceptedEgressIface = null;
Log.e(TAG, "Failed to start VPN", e);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index f92f3dc..39ed7e8 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -16,8 +16,6 @@
package com.android.server.net;
-import static com.android.server.net.NetworkPolicyManagerService.isUidNetworkingBlockedInternal;
-
import android.annotation.NonNull;
import android.net.Network;
import android.net.NetworkTemplate;
@@ -39,28 +37,6 @@
public abstract void resetUserState(int userId);
/**
- * Figure out if networking is blocked for a given set of conditions.
- *
- * This is used by ConnectivityService via passing stale copies of conditions, so it must not
- * take any locks.
- *
- * @param uid The target uid.
- * @param uidRules The uid rules which are obtained from NetworkPolicyManagerService.
- * @param isNetworkMetered True if the network is metered.
- * @param isBackgroundRestricted True if data saver is enabled.
- *
- * @return true if networking is blocked for the UID under the specified conditions.
- */
- public static boolean isUidNetworkingBlocked(int uid, int uidRules, boolean isNetworkMetered,
- boolean isBackgroundRestricted) {
- // Log of invoking internal function is disabled because it will be called very
- // frequently. And metrics are unlikely needed on this method because the callers are
- // external and this method doesn't take any locks or perform expensive operations.
- return isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
- isBackgroundRestricted, null);
- }
-
- /**
* Informs that an appId has been added or removed from the temp-powersave-allowlist so that
* that network rules for that appId can be updated.
*
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 01d4faf..6c67cba 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -70,6 +70,7 @@
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
import static android.net.NetworkPolicyManager.RULE_REJECT_RESTRICTED_MODE;
import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED;
+import static android.net.NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_UNMETERED;
import static android.net.NetworkPolicyManager.isProcStateAllowedWhileIdleOrPowerSaveMode;
import static android.net.NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground;
import static android.net.NetworkPolicyManager.resolveNetworkId;
@@ -231,6 +232,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.ConcurrentUtils;
@@ -239,6 +241,7 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.StatLogger;
import com.android.internal.util.XmlUtils;
+import com.android.net.module.util.NetworkIdentityUtils;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
@@ -1252,7 +1255,7 @@
// identified carrier, which may want to manage their own notifications. This method
// should be called every time the carrier config changes anyways, and there's no
// reason to alert if there isn't a carrier.
- return;
+ continue;
}
final boolean notifyWarning = getBooleanDefeatingNullable(config,
@@ -2162,13 +2165,14 @@
if (template.matches(probeIdent)) {
if (LOGD) {
Slog.d(TAG, "Found template " + template + " which matches subscriber "
- + NetworkIdentity.scrubSubscriberId(subscriberId));
+ + NetworkIdentityUtils.scrubSubscriberId(subscriberId));
}
return false;
}
}
- Slog.i(TAG, "No policy for subscriber " + NetworkIdentity.scrubSubscriberId(subscriberId)
+ Slog.i(TAG, "No policy for subscriber "
+ + NetworkIdentityUtils.scrubSubscriberId(subscriberId)
+ "; generating default policy");
final NetworkPolicy policy = buildDefaultMobilePolicy(subId, subscriberId);
addNetworkPolicyAL(policy);
@@ -3486,13 +3490,27 @@
@Override
public void setSubscriptionOverride(int subId, int overrideMask, int overrideValue,
- long timeoutMillis, String callingPackage) {
+ int[] networkTypes, long timeoutMillis, String callingPackage) {
enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage);
- // We can only override when carrier told us about plans
+ final ArraySet<Integer> allNetworksSet = new ArraySet<>();
+ addAll(allNetworksSet, TelephonyManager.getAllNetworkTypes());
+ final IntArray applicableNetworks = new IntArray();
+
+ // ensure all network types are valid
+ for (int networkType : networkTypes) {
+ if (allNetworksSet.contains(networkType)) {
+ applicableNetworks.add(networkType);
+ } else {
+ Log.d(TAG, "setSubscriptionOverride removing invalid network type: " + networkType);
+ }
+ }
+
+ // We can only override when carrier told us about plans. For the unmetered case,
+ // allow override without having plans defined.
synchronized (mNetworkPoliciesSecondLock) {
final SubscriptionPlan plan = getPrimarySubscriptionPlanLocked(subId);
- if (plan == null
+ if (overrideMask != SUBSCRIPTION_OVERRIDE_UNMETERED && plan == null
|| plan.getDataLimitBehavior() == SubscriptionPlan.LIMIT_BEHAVIOR_UNKNOWN) {
throw new IllegalStateException(
"Must provide valid SubscriptionPlan to enable overriding");
@@ -3504,11 +3522,16 @@
final boolean overrideEnabled = Settings.Global.getInt(mContext.getContentResolver(),
NETPOLICY_OVERRIDE_ENABLED, 1) != 0;
if (overrideEnabled || overrideValue == 0) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE,
- overrideMask, overrideValue, subId));
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = subId;
+ args.arg2 = overrideMask;
+ args.arg3 = overrideValue;
+ args.arg4 = applicableNetworks.toArray();
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE, args));
if (timeoutMillis > 0) {
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE,
- overrideMask, 0, subId), timeoutMillis);
+ args.arg3 = 0;
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE, args),
+ timeoutMillis);
}
}
}
@@ -3596,14 +3619,15 @@
final int subId = mSubIdToSubscriberId.keyAt(i);
final String subscriberId = mSubIdToSubscriberId.valueAt(i);
- fout.println(subId + "=" + NetworkIdentity.scrubSubscriberId(subscriberId));
+ fout.println(subId + "="
+ + NetworkIdentityUtils.scrubSubscriberId(subscriberId));
}
fout.decreaseIndent();
fout.println();
for (String[] mergedSubscribers : mMergedSubscriberIds) {
fout.println("Merged subscriptions: " + Arrays.toString(
- NetworkIdentity.scrubSubscriberId(mergedSubscribers)));
+ NetworkIdentityUtils.scrubSubscriberIds(mergedSubscribers)));
}
fout.println();
@@ -4775,10 +4799,10 @@
}
private void dispatchSubscriptionOverride(INetworkPolicyListener listener, int subId,
- int overrideMask, int overrideValue) {
+ int overrideMask, int overrideValue, int[] networkTypes) {
if (listener != null) {
try {
- listener.onSubscriptionOverride(subId, overrideMask, overrideValue);
+ listener.onSubscriptionOverride(subId, overrideMask, overrideValue, networkTypes);
} catch (RemoteException ignored) {
}
}
@@ -4910,13 +4934,16 @@
return true;
}
case MSG_SUBSCRIPTION_OVERRIDE: {
- final int overrideMask = msg.arg1;
- final int overrideValue = msg.arg2;
- final int subId = (int) msg.obj;
+ final SomeArgs args = (SomeArgs) msg.obj;
+ final int subId = (int) args.arg1;
+ final int overrideMask = (int) args.arg2;
+ final int overrideValue = (int) args.arg3;
+ final int[] networkTypes = (int[]) args.arg4;
final int length = mListeners.beginBroadcast();
for (int i = 0; i < length; i++) {
final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
- dispatchSubscriptionOverride(listener, subId, overrideMask, overrideValue);
+ dispatchSubscriptionOverride(listener, subId, overrideMask, overrideValue,
+ networkTypes);
}
mListeners.finishBroadcast();
return true;
@@ -5377,6 +5404,17 @@
}
@Override
+ public boolean checkUidNetworkingBlocked(int uid, int uidRules,
+ boolean isNetworkMetered, boolean isBackgroundRestricted) {
+ mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG);
+ // Log of invoking this function is disabled because it will be called very frequently. And
+ // metrics are unlikely needed on this method because the callers are external and this
+ // method doesn't take any locks or perform expensive operations.
+ return isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
+ isBackgroundRestricted, null);
+ }
+
+ @Override
public boolean isUidRestrictedOnMeteredNetworks(int uid) {
mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG);
final int uidRules;
@@ -5385,9 +5423,9 @@
uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
isBackgroundRestricted = mRestrictBackground;
}
- //TODO(b/177490332): The logic here might not be correct because it doesn't consider
- // RULE_REJECT_METERED condition. And it could be replaced by
- // isUidNetworkingBlockedInternal().
+ // TODO(b/177490332): The logic here might not be correct because it doesn't consider
+ // RULE_REJECT_METERED condition. And it could be replaced by
+ // isUidNetworkingBlockedInternal().
return isBackgroundRestricted
&& !hasRule(uidRules, RULE_ALLOW_METERED)
&& !hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED);
diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/services/core/java/com/android/server/net/NetworkStatsFactory.java
index e9868fd..d042b88 100644
--- a/services/core/java/com/android/server/net/NetworkStatsFactory.java
+++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java
@@ -27,6 +27,7 @@
import android.annotation.Nullable;
import android.net.INetd;
import android.net.NetworkStats;
+import android.net.UnderlyingNetworkInfo;
import android.net.util.NetdService;
import android.os.RemoteException;
import android.os.StrictMode;
@@ -34,7 +35,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.net.VpnInfo;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ProcFileReader;
@@ -81,7 +81,7 @@
private final Object mPersistentDataLock = new Object();
/** Set containing info about active VPNs and their underlying networks. */
- private volatile VpnInfo[] mVpnInfos = new VpnInfo[0];
+ private volatile UnderlyingNetworkInfo[] mUnderlyingNetworkInfos = new UnderlyingNetworkInfo[0];
// A persistent snapshot of cumulative stats since device start
@GuardedBy("mPersistentDataLock")
@@ -116,8 +116,8 @@
*
* @param vpnArray The snapshot of the currently-running VPNs.
*/
- public void updateVpnInfos(VpnInfo[] vpnArray) {
- mVpnInfos = vpnArray.clone();
+ public void updateUnderlyingNetworkInfos(UnderlyingNetworkInfo[] vpnArray) {
+ mUnderlyingNetworkInfos = vpnArray.clone();
}
/**
@@ -319,7 +319,7 @@
// code that will acquire other locks within the system server. See b/134244752.
synchronized (mPersistentDataLock) {
// Take a reference. If this gets swapped out, we still have the old reference.
- final VpnInfo[] vpnArray = mVpnInfos;
+ final UnderlyingNetworkInfo[] vpnArray = mUnderlyingNetworkInfos;
// Take a defensive copy. mPersistSnapshot is mutated in some cases below
final NetworkStats prev = mPersistSnapshot.clone();
@@ -369,8 +369,8 @@
}
@GuardedBy("mPersistentDataLock")
- private NetworkStats adjustForTunAnd464Xlat(
- NetworkStats uidDetailStats, NetworkStats previousStats, VpnInfo[] vpnArray) {
+ private NetworkStats adjustForTunAnd464Xlat(NetworkStats uidDetailStats,
+ NetworkStats previousStats, UnderlyingNetworkInfo[] vpnArray) {
// Calculate delta from last snapshot
final NetworkStats delta = uidDetailStats.subtract(previousStats);
@@ -381,8 +381,9 @@
delta.apply464xlatAdjustments(mStackedIfaces);
// Migrate data usage over a VPN to the TUN network.
- for (VpnInfo info : vpnArray) {
- delta.migrateTun(info.ownerUid, info.vpnIface, info.underlyingIfaces);
+ for (UnderlyingNetworkInfo info : vpnArray) {
+ delta.migrateTun(info.ownerUid, info.iface,
+ info.underlyingIfaces.toArray(new String[0]));
// Filter out debug entries as that may lead to over counting.
delta.filterDebugEntries();
}
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 81a6641..0ab35a9 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -104,6 +104,7 @@
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.TrafficStats;
+import android.net.UnderlyingNetworkInfo;
import android.net.Uri;
import android.net.netstats.provider.INetworkStatsProvider;
import android.net.netstats.provider.INetworkStatsProviderCallback;
@@ -143,7 +144,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.net.VpnInfo;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FileRotator;
@@ -973,7 +973,7 @@
Network[] defaultNetworks,
NetworkState[] networkStates,
String activeIface,
- VpnInfo[] vpnInfos) {
+ UnderlyingNetworkInfo[] underlyingNetworkInfos) {
checkNetworkStackPermission(mContext);
final long token = Binder.clearCallingIdentity();
@@ -986,7 +986,7 @@
// Update the VPN underlying interfaces only after the poll is made and tun data has been
// migrated. Otherwise the migration would use the new interfaces instead of the ones that
// were current when the polled data was transferred.
- mStatsFactory.updateVpnInfos(vpnInfos);
+ mStatsFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos);
}
@Override
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index c3cb42f..45419fe 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -22,8 +22,8 @@
import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
-import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES;
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 5417275..2067fd0 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -21,8 +21,8 @@
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_REMOVED;
import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY;
import static android.service.notification.DNDModeProto.ROOT_CONFIG;
+import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
-import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID;
import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
import android.app.AppOpsManager;
diff --git a/services/core/java/com/android/server/os/NativeTombstoneManager.java b/services/core/java/com/android/server/os/NativeTombstoneManager.java
new file mode 100644
index 0000000..a83edb7
--- /dev/null
+++ b/services/core/java/com/android/server/os/NativeTombstoneManager.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.os;
+
+import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
+import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
+
+import android.annotation.AppIdInt;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.os.FileObserver;
+import android.os.Handler;
+import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.proto.ProtoInputStream;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.BootReceiver;
+import com.android.server.ServiceThread;
+import com.android.server.os.TombstoneProtos.Tombstone;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Optional;
+
+/**
+ * A class to manage native tombstones.
+ */
+public final class NativeTombstoneManager {
+ private static final String TAG = NativeTombstoneManager.class.getSimpleName();
+
+ private static final File TOMBSTONE_DIR = new File("/data/tombstones");
+
+ private final Context mContext;
+ private final Handler mHandler;
+ private final TombstoneWatcher mWatcher;
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private final SparseArray<TombstoneFile> mTombstones;
+
+ NativeTombstoneManager(Context context) {
+ mTombstones = new SparseArray<TombstoneFile>();
+ mContext = context;
+
+ final ServiceThread thread = new ServiceThread(TAG + ":tombstoneWatcher",
+ THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
+ thread.start();
+ mHandler = thread.getThreadHandler();
+
+ mWatcher = new TombstoneWatcher();
+ mWatcher.startWatching();
+ }
+
+ void onSystemReady() {
+ // Scan existing tombstones.
+ mHandler.post(() -> {
+ final File[] tombstoneFiles = TOMBSTONE_DIR.listFiles();
+ for (int i = 0; tombstoneFiles != null && i < tombstoneFiles.length; i++) {
+ if (tombstoneFiles[i].isFile()) {
+ handleTombstone(tombstoneFiles[i]);
+ }
+ }
+ });
+ }
+
+ private void handleTombstone(File path) {
+ final String filename = path.getName();
+ if (!filename.startsWith("tombstone_")) {
+ return;
+ }
+
+ if (filename.endsWith(".pb")) {
+ handleProtoTombstone(path);
+ } else {
+ BootReceiver.addTombstoneToDropBox(mContext, path);
+ }
+ }
+
+ private void handleProtoTombstone(File path) {
+ final String filename = path.getName();
+ if (!filename.endsWith(".pb")) {
+ Slog.w(TAG, "unexpected tombstone name: " + path);
+ return;
+ }
+
+ final String suffix = filename.substring("tombstone_".length());
+ final String numberStr = suffix.substring(0, suffix.length() - 3);
+
+ int number;
+ try {
+ number = Integer.parseInt(numberStr);
+ if (number < 0 || number > 99) {
+ Slog.w(TAG, "unexpected tombstone name: " + path);
+ return;
+ }
+ } catch (NumberFormatException ex) {
+ Slog.w(TAG, "unexpected tombstone name: " + path);
+ return;
+ }
+
+ ParcelFileDescriptor pfd;
+ try {
+ pfd = ParcelFileDescriptor.open(path, MODE_READ_WRITE);
+ } catch (FileNotFoundException ex) {
+ Slog.w(TAG, "failed to open " + path, ex);
+ return;
+ }
+
+ final Optional<TombstoneFile> parsedTombstone = TombstoneFile.parse(pfd);
+ if (!parsedTombstone.isPresent()) {
+ IoUtils.closeQuietly(pfd);
+ return;
+ }
+
+ synchronized (mLock) {
+ TombstoneFile previous = mTombstones.get(number);
+ if (previous != null) {
+ previous.dispose();
+ }
+
+ mTombstones.put(number, parsedTombstone.get());
+ }
+ }
+
+ static class TombstoneFile {
+ final ParcelFileDescriptor mPfd;
+
+ final @UserIdInt int mUserId;
+ final @AppIdInt int mAppId;
+
+ boolean mPurged = false;
+
+ TombstoneFile(ParcelFileDescriptor pfd, @UserIdInt int userId, @AppIdInt int appId) {
+ mPfd = pfd;
+ mUserId = userId;
+ mAppId = appId;
+ }
+
+ public boolean matches(Optional<Integer> userId, Optional<Integer> appId) {
+ if (mPurged) {
+ return false;
+ }
+
+ if (userId.isPresent() && userId.get() != mUserId) {
+ return false;
+ }
+
+ if (appId.isPresent() && appId.get() != mAppId) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public void dispose() {
+ IoUtils.closeQuietly(mPfd);
+ }
+
+ static Optional<TombstoneFile> parse(ParcelFileDescriptor pfd) {
+ final FileInputStream is = new FileInputStream(pfd.getFileDescriptor());
+ final ProtoInputStream stream = new ProtoInputStream(is);
+
+ int uid = 0;
+ String selinuxLabel = "";
+
+ try {
+ while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (stream.getFieldNumber()) {
+ case (int) Tombstone.UID:
+ uid = stream.readInt(Tombstone.UID);
+ break;
+
+ case (int) Tombstone.SELINUX_LABEL:
+ selinuxLabel = stream.readString(Tombstone.SELINUX_LABEL);
+ break;
+
+ default:
+ break;
+ }
+ }
+ } catch (IOException ex) {
+ Slog.e(TAG, "Failed to parse tombstone", ex);
+ return Optional.empty();
+ }
+
+ if (!UserHandle.isApp(uid)) {
+ Slog.e(TAG, "Tombstone's UID (" + uid + ") not an app, ignoring");
+ return Optional.empty();
+ }
+
+ final int userId = UserHandle.getUserId(uid);
+ final int appId = UserHandle.getAppId(uid);
+
+ if (!selinuxLabel.startsWith("u:r:untrusted_app")) {
+ Slog.e(TAG, "Tombstone has invalid selinux label (" + selinuxLabel + "), ignoring");
+ return Optional.empty();
+ }
+
+ return Optional.of(new TombstoneFile(pfd, userId, appId));
+ }
+ }
+
+ class TombstoneWatcher extends FileObserver {
+ TombstoneWatcher() {
+ // Tombstones can be created either by linking an O_TMPFILE temporary file (CREATE),
+ // or by moving a named temporary file in the same directory on kernels where O_TMPFILE
+ // isn't supported (MOVED_TO).
+ super(TOMBSTONE_DIR, FileObserver.CREATE | FileObserver.MOVED_TO);
+ }
+
+ @Override
+ public void onEvent(int event, @Nullable String path) {
+ mHandler.post(() -> {
+ handleTombstone(new File(TOMBSTONE_DIR, path));
+ });
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/os/NativeTombstoneManagerService.java b/services/core/java/com/android/server/os/NativeTombstoneManagerService.java
new file mode 100644
index 0000000..cb3c7ff0
--- /dev/null
+++ b/services/core/java/com/android/server/os/NativeTombstoneManagerService.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.os;
+
+import android.content.Context;
+
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+
+/**
+ * Service that tracks and manages native tombstones.
+ *
+ * @hide
+ */
+public class NativeTombstoneManagerService extends SystemService {
+ private static final String TAG = "NativeTombstoneManagerService";
+
+ private NativeTombstoneManager mManager;
+
+ public NativeTombstoneManagerService(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ mManager = new NativeTombstoneManager(getContext());
+ LocalServices.addService(NativeTombstoneManager.class, mManager);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+ mManager.onSystemReady();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 330f995..9f0efa5 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -299,6 +299,10 @@
final ArraySet<File> unclaimedStages = newArraySet(
stagingDir.listFiles(sStageFilter));
+ // We also need to clean up orphaned staging directory for staged sessions
+ final File stagedSessionStagingDir = Environment.getDataStagingDirectory(volumeUuid);
+ unclaimedStages.addAll(newArraySet(stagedSessionStagingDir.listFiles()));
+
// Ignore stages claimed by active sessions
for (int i = 0; i < mSessions.size(); i++) {
final PackageInstallerSession session = mSessions.valueAt(i);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 6594a90..ae2e58f 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1598,6 +1598,10 @@
destroyInternal();
// Dispatch message to remove session from PackageInstallerService.
dispatchSessionFinished(error, detailMessage, null);
+ // TODO(b/173194203): clean up staged session in destroyInternal() call instead
+ if (isStaged() && stageDir != null) {
+ cleanStageDir();
+ }
}
private void onStorageUnhealthy() {
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 3dfb99e..bba5dcb 100755
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -976,7 +976,7 @@
AudioPortConfig sourceConfig = mAudioSource.activeConfig();
List<AudioPortConfig> sinkConfigs = new ArrayList<>();
AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch };
- boolean shouldRecreateAudioPatch = sourceUpdated || sinkUpdated;
+ boolean shouldRecreateAudioPatch = sourceUpdated || sinkUpdated || mAudioPatch == null;
for (AudioDevicePort audioSink : mAudioSink) {
AudioPortConfig sinkConfig = audioSink.activeConfig();
diff --git a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
index 6427ae2..b6ddd93 100644
--- a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
+++ b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
@@ -18,16 +18,27 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
+import android.net.NetworkCapabilities.NetCapability;
+import android.net.NetworkRequest;
+import android.net.TelephonyNetworkSpecifier;
import android.os.Handler;
import android.os.ParcelUuid;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import java.util.Map;
import java.util.Objects;
+import java.util.Set;
/**
* Tracks a set of Networks underpinning a VcnGatewayConnection.
@@ -38,53 +49,401 @@
*
* @hide
*/
-public class UnderlyingNetworkTracker extends Handler {
+public class UnderlyingNetworkTracker {
@NonNull private static final String TAG = UnderlyingNetworkTracker.class.getSimpleName();
@NonNull private final VcnContext mVcnContext;
@NonNull private final ParcelUuid mSubscriptionGroup;
+ @NonNull private final Set<Integer> mRequiredUnderlyingNetworkCapabilities;
@NonNull private final UnderlyingNetworkTrackerCallback mCb;
@NonNull private final Dependencies mDeps;
+ @NonNull private final Handler mHandler;
+ @NonNull private final ConnectivityManager mConnectivityManager;
+
+ @NonNull private final Map<Integer, NetworkCallback> mCellBringupCallbacks = new ArrayMap<>();
+ @NonNull private final NetworkCallback mWifiBringupCallback = new NetworkBringupCallback();
+ @NonNull private final NetworkCallback mRouteSelectionCallback = new RouteSelectionCallback();
+
+ @NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
+ private boolean mIsRunning = true;
+
+ @Nullable private UnderlyingNetworkRecord mCurrentRecord;
+ @Nullable private UnderlyingNetworkRecord.Builder mRecordInProgress;
public UnderlyingNetworkTracker(
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
+ @NonNull TelephonySubscriptionSnapshot snapshot,
+ @NonNull Set<Integer> requiredUnderlyingNetworkCapabilities,
@NonNull UnderlyingNetworkTrackerCallback cb) {
- this(vcnContext, subscriptionGroup, cb, new Dependencies());
+ this(
+ vcnContext,
+ subscriptionGroup,
+ snapshot,
+ requiredUnderlyingNetworkCapabilities,
+ cb,
+ new Dependencies());
}
private UnderlyingNetworkTracker(
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
+ @NonNull TelephonySubscriptionSnapshot snapshot,
+ @NonNull Set<Integer> requiredUnderlyingNetworkCapabilities,
@NonNull UnderlyingNetworkTrackerCallback cb,
@NonNull Dependencies deps) {
- super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
- mVcnContext = vcnContext;
+ mVcnContext = Objects.requireNonNull(vcnContext, "Missing vcnContext");
mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
+ mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
+ mRequiredUnderlyingNetworkCapabilities =
+ Objects.requireNonNull(
+ requiredUnderlyingNetworkCapabilities,
+ "Missing requiredUnderlyingNetworkCapabilities");
mCb = Objects.requireNonNull(cb, "Missing cb");
mDeps = Objects.requireNonNull(deps, "Missing deps");
+
+ mHandler = new Handler(mVcnContext.getLooper());
+
+ mConnectivityManager = mVcnContext.getContext().getSystemService(ConnectivityManager.class);
+
+ registerNetworkRequests();
+ }
+
+ private void registerNetworkRequests() {
+ // register bringup requests for underlying Networks
+ mConnectivityManager.requestBackgroundNetwork(
+ getWifiNetworkRequest(), mHandler, mWifiBringupCallback);
+ updateSubIdsAndCellularRequests();
+
+ // register Network-selection request used to decide selected underlying Network
+ mConnectivityManager.requestBackgroundNetwork(
+ getNetworkRequestBase().build(), mHandler, mRouteSelectionCallback);
+ }
+
+ private NetworkRequest getWifiNetworkRequest() {
+ return getNetworkRequestBase().addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build();
+ }
+
+ private NetworkRequest getCellNetworkRequestForSubId(int subId) {
+ return getNetworkRequestBase()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .setNetworkSpecifier(new TelephonyNetworkSpecifier(subId))
+ .build();
+ }
+
+ private NetworkRequest.Builder getNetworkRequestBase() {
+ NetworkRequest.Builder requestBase = new NetworkRequest.Builder();
+ for (@NetCapability int capability : mRequiredUnderlyingNetworkCapabilities) {
+ requestBase.addCapability(capability);
+ }
+
+ return requestBase
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
+ .addUnwantedCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
+ }
+
+ /**
+ * Update the current subIds and Cellular bringup requests for this UnderlyingNetworkTracker.
+ */
+ private void updateSubIdsAndCellularRequests() {
+ mVcnContext.ensureRunningOnLooperThread();
+
+ // Don't bother re-filing NetworkRequests if this Tracker has been torn down.
+ if (!mIsRunning) {
+ return;
+ }
+
+ final Set<Integer> subIdsInSubGroup = mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup);
+
+ // new subIds to track = (updated list of subIds) - (currently tracked subIds)
+ final Set<Integer> subIdsToRegister = new ArraySet<>(subIdsInSubGroup);
+ subIdsToRegister.removeAll(mCellBringupCallbacks.keySet());
+
+ // subIds to stop tracking = (currently tracked subIds) - (updated list of subIds)
+ final Set<Integer> subIdsToUnregister = new ArraySet<>(mCellBringupCallbacks.keySet());
+ subIdsToUnregister.removeAll(subIdsInSubGroup);
+
+ for (final int subId : subIdsToRegister) {
+ final NetworkBringupCallback cb = new NetworkBringupCallback();
+ mCellBringupCallbacks.put(subId, cb);
+
+ mConnectivityManager.requestBackgroundNetwork(
+ getCellNetworkRequestForSubId(subId), mHandler, cb);
+ }
+
+ for (final int subId : subIdsToUnregister) {
+ final NetworkCallback cb = mCellBringupCallbacks.remove(subId);
+ mConnectivityManager.unregisterNetworkCallback(cb);
+ }
+ }
+
+ /**
+ * Update this UnderlyingNetworkTracker's TelephonySubscriptionSnapshot.
+ *
+ * <p>Updating the TelephonySubscriptionSnapshot will cause this UnderlyingNetworkTracker to
+ * reevaluate its NetworkBringupCallbacks. This may result in NetworkRequests being registered
+ * or unregistered if the subIds mapped to the this Tracker's SubscriptionGroup change.
+ */
+ public void updateSubscriptionSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot) {
+ Objects.requireNonNull(snapshot, "Missing snapshot");
+
+ mLastSnapshot = snapshot;
+ updateSubIdsAndCellularRequests();
}
/** Tears down this Tracker, and releases all underlying network requests. */
- public void teardown() {}
+ public void teardown() {
+ mVcnContext.ensureRunningOnLooperThread();
- /** An record of a single underlying network, caching relevant fields. */
+ mConnectivityManager.unregisterNetworkCallback(mWifiBringupCallback);
+ mConnectivityManager.unregisterNetworkCallback(mRouteSelectionCallback);
+
+ for (final NetworkCallback cb : mCellBringupCallbacks.values()) {
+ mConnectivityManager.unregisterNetworkCallback(cb);
+ }
+ mCellBringupCallbacks.clear();
+
+ mIsRunning = false;
+ }
+
+ /** Returns whether the currently selected Network matches the given network. */
+ private static boolean isSameNetwork(
+ @Nullable UnderlyingNetworkRecord.Builder recordInProgress, @NonNull Network network) {
+ return recordInProgress != null && recordInProgress.getNetwork().equals(network);
+ }
+
+ /** Notify the Callback if a full UnderlyingNetworkRecord exists. */
+ private void maybeNotifyCallback() {
+ // Only forward this update if a complete record has been received
+ if (!mRecordInProgress.isValid()) {
+ return;
+ }
+
+ // Only forward this update if the updated record differs form the current record
+ UnderlyingNetworkRecord updatedRecord = mRecordInProgress.build();
+ if (!updatedRecord.equals(mCurrentRecord)) {
+ mCurrentRecord = updatedRecord;
+
+ mCb.onSelectedUnderlyingNetworkChanged(mCurrentRecord);
+ }
+ }
+
+ private void handleNetworkAvailable(@NonNull Network network) {
+ mVcnContext.ensureRunningOnLooperThread();
+
+ mRecordInProgress = new UnderlyingNetworkRecord.Builder(network);
+ }
+
+ private void handleNetworkLost(@NonNull Network network) {
+ mVcnContext.ensureRunningOnLooperThread();
+
+ if (!isSameNetwork(mRecordInProgress, network)) {
+ Slog.wtf(TAG, "Non-underlying Network lost");
+ return;
+ }
+
+ mRecordInProgress = null;
+ mCurrentRecord = null;
+ mCb.onSelectedUnderlyingNetworkChanged(null /* underlyingNetworkRecord */);
+ }
+
+ private void handleCapabilitiesChanged(
+ @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) {
+ mVcnContext.ensureRunningOnLooperThread();
+
+ if (!isSameNetwork(mRecordInProgress, network)) {
+ Slog.wtf(TAG, "Invalid update to NetworkCapabilities");
+ return;
+ }
+
+ mRecordInProgress.setNetworkCapabilities(networkCapabilities);
+
+ maybeNotifyCallback();
+ }
+
+ private void handleNetworkSuspended(@NonNull Network network, boolean isSuspended) {
+ mVcnContext.ensureRunningOnLooperThread();
+
+ if (!isSameNetwork(mRecordInProgress, network)) {
+ Slog.wtf(TAG, "Invalid update to isSuspended");
+ return;
+ }
+
+ final NetworkCapabilities newCaps =
+ new NetworkCapabilities(mRecordInProgress.getNetworkCapabilities());
+ if (isSuspended) {
+ newCaps.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
+ } else {
+ newCaps.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
+ }
+
+ handleCapabilitiesChanged(network, newCaps);
+ }
+
+ private void handlePropertiesChanged(
+ @NonNull Network network, @NonNull LinkProperties linkProperties) {
+ mVcnContext.ensureRunningOnLooperThread();
+
+ if (!isSameNetwork(mRecordInProgress, network)) {
+ Slog.wtf(TAG, "Invalid update to LinkProperties");
+ return;
+ }
+
+ mRecordInProgress.setLinkProperties(linkProperties);
+
+ maybeNotifyCallback();
+ }
+
+ private void handleNetworkBlocked(@NonNull Network network, boolean isBlocked) {
+ mVcnContext.ensureRunningOnLooperThread();
+
+ if (!isSameNetwork(mRecordInProgress, network)) {
+ Slog.wtf(TAG, "Invalid update to isBlocked");
+ return;
+ }
+
+ mRecordInProgress.setIsBlocked(isBlocked);
+
+ maybeNotifyCallback();
+ }
+
+ /**
+ * NetworkBringupCallback is used to keep background, VCN-managed Networks from being reaped.
+ *
+ * <p>NetworkBringupCallback only exists to prevent matching (VCN-managed) Networks from being
+ * reaped, and no action is taken on any events firing.
+ */
+ @VisibleForTesting
+ class NetworkBringupCallback extends NetworkCallback {}
+
+ /**
+ * RouteSelectionCallback is used to select the "best" underlying Network.
+ *
+ * <p>The "best" network is determined by ConnectivityService, which is treated as a source of
+ * truth.
+ */
+ @VisibleForTesting
+ class RouteSelectionCallback extends NetworkCallback {
+ @Override
+ public void onAvailable(@NonNull Network network) {
+ handleNetworkAvailable(network);
+ }
+
+ @Override
+ public void onLost(@NonNull Network network) {
+ handleNetworkLost(network);
+ }
+
+ @Override
+ public void onCapabilitiesChanged(
+ @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) {
+ handleCapabilitiesChanged(network, networkCapabilities);
+ }
+
+ @Override
+ public void onNetworkSuspended(@NonNull Network network) {
+ handleNetworkSuspended(network, true /* isSuspended */);
+ }
+
+ @Override
+ public void onNetworkResumed(@NonNull Network network) {
+ handleNetworkSuspended(network, false /* isSuspended */);
+ }
+
+ @Override
+ public void onLinkPropertiesChanged(
+ @NonNull Network network, @NonNull LinkProperties linkProperties) {
+ handlePropertiesChanged(network, linkProperties);
+ }
+
+ @Override
+ public void onBlockedStatusChanged(@NonNull Network network, boolean isBlocked) {
+ handleNetworkBlocked(network, isBlocked);
+ }
+ }
+
+ /** A record of a single underlying network, caching relevant fields. */
public static class UnderlyingNetworkRecord {
@NonNull public final Network network;
@NonNull public final NetworkCapabilities networkCapabilities;
@NonNull public final LinkProperties linkProperties;
- public final boolean blocked;
+ public final boolean isBlocked;
@VisibleForTesting(visibility = Visibility.PRIVATE)
UnderlyingNetworkRecord(
@NonNull Network network,
@NonNull NetworkCapabilities networkCapabilities,
@NonNull LinkProperties linkProperties,
- boolean blocked) {
+ boolean isBlocked) {
this.network = network;
this.networkCapabilities = networkCapabilities;
this.linkProperties = linkProperties;
- this.blocked = blocked;
+ this.isBlocked = isBlocked;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof UnderlyingNetworkRecord)) return false;
+ final UnderlyingNetworkRecord that = (UnderlyingNetworkRecord) o;
+
+ return network.equals(that.network)
+ && networkCapabilities.equals(that.networkCapabilities)
+ && linkProperties.equals(that.linkProperties)
+ && isBlocked == that.isBlocked;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(network, networkCapabilities, linkProperties, isBlocked);
+ }
+
+ /** Builder to incrementally construct an UnderlyingNetworkRecord. */
+ private static class Builder {
+ @NonNull private final Network mNetwork;
+
+ @Nullable private NetworkCapabilities mNetworkCapabilities;
+ @Nullable private LinkProperties mLinkProperties;
+ boolean mIsBlocked;
+ boolean mWasIsBlockedSet;
+
+ private Builder(@NonNull Network network) {
+ mNetwork = network;
+ }
+
+ @NonNull
+ private Network getNetwork() {
+ return mNetwork;
+ }
+
+ private void setNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) {
+ mNetworkCapabilities = networkCapabilities;
+ }
+
+ @Nullable
+ private NetworkCapabilities getNetworkCapabilities() {
+ return mNetworkCapabilities;
+ }
+
+ private void setLinkProperties(@NonNull LinkProperties linkProperties) {
+ mLinkProperties = linkProperties;
+ }
+
+ private void setIsBlocked(boolean isBlocked) {
+ mIsBlocked = isBlocked;
+ mWasIsBlockedSet = true;
+ }
+
+ private boolean isValid() {
+ return mNetworkCapabilities != null && mLinkProperties != null && mWasIsBlockedSet;
+ }
+
+ private UnderlyingNetworkRecord build() {
+ return new UnderlyingNetworkRecord(
+ mNetwork, mNetworkCapabilities, mLinkProperties, mIsBlocked);
+ }
}
}
@@ -95,9 +454,10 @@
*
* <p>This callback does NOT signal a mobility event.
*
- * @param underlying The details of the new underlying network
+ * @param underlyingNetworkRecord The details of the new underlying network
*/
- void onSelectedUnderlyingNetworkChanged(@Nullable UnderlyingNetworkRecord underlying);
+ void onSelectedUnderlyingNetworkChanged(
+ @Nullable UnderlyingNetworkRecord underlyingNetworkRecord);
}
private static class Dependencies {}
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index 9d21b92..5ec527a 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -27,9 +27,18 @@
import android.os.ParcelUuid;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.server.VcnManagementService.VcnSafemodeCallback;
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+
+import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* Represents an single instance of a VCN.
@@ -63,41 +72,86 @@
*/
private static final int MSG_EVENT_NETWORK_REQUESTED = MSG_EVENT_BASE + 1;
+ /**
+ * The TelephonySubscriptionSnapshot tracked by VcnManagementService has changed.
+ *
+ * <p>This updated snapshot should be cached locally and passed to all VcnGatewayConnections.
+ *
+ * @param obj TelephonySubscriptionSnapshot
+ */
+ private static final int MSG_EVENT_SUBSCRIPTIONS_CHANGED = MSG_EVENT_BASE + 2;
+
/** Triggers an immediate teardown of the entire Vcn, including GatewayConnections. */
private static final int MSG_CMD_TEARDOWN = MSG_CMD_BASE;
+ /**
+ * Causes this VCN to immediately enter Safemode.
+ *
+ * <p>Upon entering Safemode, the VCN will unregister its RequestListener, tear down all of its
+ * VcnGatewayConnections, and notify VcnManagementService that it is in Safemode.
+ */
+ private static final int MSG_CMD_ENTER_SAFEMODE = MSG_CMD_BASE + 1;
+
@NonNull private final VcnContext mVcnContext;
@NonNull private final ParcelUuid mSubscriptionGroup;
@NonNull private final Dependencies mDeps;
@NonNull private final VcnNetworkRequestListener mRequestListener;
+ @NonNull private final VcnSafemodeCallback mVcnSafemodeCallback;
@NonNull
private final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> mVcnGatewayConnections =
new HashMap<>();
@NonNull private VcnConfig mConfig;
+ @NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
- private boolean mIsRunning = true;
+ /**
+ * Whether this Vcn instance is active and running.
+ *
+ * <p>The value will be {@code true} while running. It will be {@code false} if the VCN has been
+ * shut down or has entered safe mode.
+ *
+ * <p>This AtomicBoolean is required in order to ensure consistency and correctness across
+ * multiple threads. Unlike the rest of the Vcn, this is queried synchronously on Binder threads
+ * from VcnManagementService, and therefore cannot rely on guarantees of running on the VCN
+ * Looper.
+ */
+ // TODO(b/179429339): update when exiting safemode (when a new VcnConfig is provided)
+ private final AtomicBoolean mIsActive = new AtomicBoolean(true);
public Vcn(
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
- @NonNull VcnConfig config) {
- this(vcnContext, subscriptionGroup, config, new Dependencies());
+ @NonNull VcnConfig config,
+ @NonNull TelephonySubscriptionSnapshot snapshot,
+ @NonNull VcnSafemodeCallback vcnSafemodeCallback) {
+ this(
+ vcnContext,
+ subscriptionGroup,
+ config,
+ snapshot,
+ vcnSafemodeCallback,
+ new Dependencies());
}
- private Vcn(
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public Vcn(
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
@NonNull VcnConfig config,
+ @NonNull TelephonySubscriptionSnapshot snapshot,
+ @NonNull VcnSafemodeCallback vcnSafemodeCallback,
@NonNull Dependencies deps) {
super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
mVcnContext = vcnContext;
mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
+ mVcnSafemodeCallback =
+ Objects.requireNonNull(vcnSafemodeCallback, "Missing vcnSafemodeCallback");
mDeps = Objects.requireNonNull(deps, "Missing deps");
mRequestListener = new VcnNetworkRequestListener();
mConfig = Objects.requireNonNull(config, "Missing config");
+ mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
// Register to receive cached and future NetworkRequests
mVcnContext.getVcnNetworkProvider().registerListener(mRequestListener);
@@ -110,11 +164,29 @@
sendMessage(obtainMessage(MSG_EVENT_CONFIG_UPDATED, config));
}
+ /** Asynchronously updates the Subscription snapshot for this VCN. */
+ public void updateSubscriptionSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot) {
+ Objects.requireNonNull(snapshot, "Missing snapshot");
+
+ sendMessage(obtainMessage(MSG_EVENT_SUBSCRIPTIONS_CHANGED, snapshot));
+ }
+
/** Asynchronously tears down this Vcn instance, including VcnGatewayConnection(s) */
public void teardownAsynchronously() {
sendMessageAtFrontOfQueue(obtainMessage(MSG_CMD_TEARDOWN));
}
+ /** Synchronously checks whether this Vcn is active. */
+ public boolean isActive() {
+ return mIsActive.get();
+ }
+
+ /** Get current Gateways for testing purposes */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public Set<VcnGatewayConnection> getVcnGatewayConnections() {
+ return Collections.unmodifiableSet(new HashSet<>(mVcnGatewayConnections.values()));
+ }
+
private class VcnNetworkRequestListener implements VcnNetworkProvider.NetworkRequestListener {
@Override
public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) {
@@ -126,7 +198,7 @@
@Override
public void handleMessage(@NonNull Message msg) {
- if (!mIsRunning) {
+ if (!isActive()) {
return;
}
@@ -137,9 +209,15 @@
case MSG_EVENT_NETWORK_REQUESTED:
handleNetworkRequested((NetworkRequest) msg.obj, msg.arg1, msg.arg2);
break;
+ case MSG_EVENT_SUBSCRIPTIONS_CHANGED:
+ handleSubscriptionsChanged((TelephonySubscriptionSnapshot) msg.obj);
+ break;
case MSG_CMD_TEARDOWN:
handleTeardown();
break;
+ case MSG_CMD_ENTER_SAFEMODE:
+ handleEnterSafemode();
+ break;
default:
Slog.wtf(getLogTag(), "Unknown msg.what: " + msg.what);
}
@@ -161,15 +239,21 @@
gatewayConnection.teardownAsynchronously();
}
- mIsRunning = false;
+ mIsActive.set(false);
+ }
+
+ private void handleEnterSafemode() {
+ handleTeardown();
+
+ mVcnSafemodeCallback.onEnteredSafemode();
}
private void handleNetworkRequested(
@NonNull NetworkRequest request, int score, int providerId) {
if (score > getNetworkScore()) {
Slog.v(getLogTag(),
- "Request " + request.requestId + " already satisfied by higher-scoring ("
- + score + ") network from provider " + providerId);
+ "Request already satisfied by higher-scoring (" + score + ") network from "
+ + "provider " + providerId + ": " + request);
return;
}
@@ -177,8 +261,7 @@
for (VcnGatewayConnectionConfig gatewayConnectionConfig : mVcnGatewayConnections.keySet()) {
if (requestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
Slog.v(getLogTag(),
- "Request " + request.requestId
- + " satisfied by existing VcnGatewayConnection");
+ "Request already satisfied by existing VcnGatewayConnection: " + request);
return;
}
}
@@ -193,21 +276,35 @@
"Bringing up new VcnGatewayConnection for request " + request.requestId);
final VcnGatewayConnection vcnGatewayConnection =
- new VcnGatewayConnection(
- mVcnContext, mSubscriptionGroup, gatewayConnectionConfig);
+ mDeps.newVcnGatewayConnection(
+ mVcnContext,
+ mSubscriptionGroup,
+ mLastSnapshot,
+ gatewayConnectionConfig,
+ new VcnGatewayStatusCallbackImpl());
mVcnGatewayConnections.put(gatewayConnectionConfig, vcnGatewayConnection);
}
}
}
+ private void handleSubscriptionsChanged(@NonNull TelephonySubscriptionSnapshot snapshot) {
+ mLastSnapshot = snapshot;
+
+ if (isActive()) {
+ for (VcnGatewayConnection gatewayConnection : mVcnGatewayConnections.values()) {
+ gatewayConnection.updateSubscriptionSnapshot(mLastSnapshot);
+ }
+ }
+ }
+
private boolean requestSatisfiedByGatewayConnectionConfig(
@NonNull NetworkRequest request, @NonNull VcnGatewayConnectionConfig config) {
- final NetworkCapabilities configCaps = new NetworkCapabilities();
+ final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder();
for (int cap : config.getAllExposedCapabilities()) {
- configCaps.addCapability(cap);
+ builder.addCapability(cap);
}
- return request.networkCapabilities.satisfiedByNetworkCapabilities(configCaps);
+ return request.canBeSatisfiedBy(builder.build());
}
private String getLogTag() {
@@ -215,11 +312,43 @@
}
/** Retrieves the network score for a VCN Network */
- private int getNetworkScore() {
+ // Package visibility for use in VcnGatewayConnection
+ static int getNetworkScore() {
// TODO: STOPSHIP (b/173549607): Make this use new NetworkSelection, or some magic "max in
// subGrp" value
return 52;
}
- private static class Dependencies {}
+ /** Callback used for passing status signals from a VcnGatewayConnection to its managing Vcn. */
+ @VisibleForTesting(visibility = Visibility.PACKAGE)
+ public interface VcnGatewayStatusCallback {
+ /** Called by a VcnGatewayConnection to indicate that it has entered Safemode. */
+ void onEnteredSafemode();
+ }
+
+ private class VcnGatewayStatusCallbackImpl implements VcnGatewayStatusCallback {
+ @Override
+ public void onEnteredSafemode() {
+ sendMessage(obtainMessage(MSG_CMD_ENTER_SAFEMODE));
+ }
+ }
+
+ /** External dependencies used by Vcn, for injection in tests */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public static class Dependencies {
+ /** Builds a new VcnGatewayConnection */
+ public VcnGatewayConnection newVcnGatewayConnection(
+ VcnContext vcnContext,
+ ParcelUuid subscriptionGroup,
+ TelephonySubscriptionSnapshot snapshot,
+ VcnGatewayConnectionConfig connectionConfig,
+ VcnGatewayStatusCallback gatewayStatusCallback) {
+ return new VcnGatewayConnection(
+ vcnContext,
+ subscriptionGroup,
+ snapshot,
+ connectionConfig,
+ gatewayStatusCallback);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/vcn/VcnContext.java b/services/core/java/com/android/server/vcn/VcnContext.java
index dba59bd..7399e56 100644
--- a/services/core/java/com/android/server/vcn/VcnContext.java
+++ b/services/core/java/com/android/server/vcn/VcnContext.java
@@ -55,4 +55,15 @@
public VcnNetworkProvider getVcnNetworkProvider() {
return mVcnNetworkProvider;
}
+
+ /**
+ * Verifies that the caller is running on the VcnContext Thread.
+ *
+ * @throwsIllegalStateException if the caller is not running on the VcnContext Thread.
+ */
+ public void ensureRunningOnLooperThread() {
+ if (getLooper().getThread() != Thread.currentThread()) {
+ throw new IllegalStateException("Not running on VcnMgmtSvc thread");
+ }
+ }
}
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 0fa97a2..9ecdf1b 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -17,8 +17,11 @@
package com.android.server.vcn;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static com.android.server.VcnManagementService.VDBG;
@@ -34,8 +37,10 @@
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkAgent;
+import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.RouteInfo;
+import android.net.TelephonyNetworkSpecifier;
import android.net.annotations.PolicyDirection;
import android.net.ipsec.ike.ChildSessionCallback;
import android.net.ipsec.ike.ChildSessionConfiguration;
@@ -47,24 +52,31 @@
import android.net.ipsec.ike.exceptions.IkeException;
import android.net.ipsec.ike.exceptions.IkeProtocolException;
import android.net.vcn.VcnGatewayConnectionConfig;
+import android.net.vcn.VcnTransportInfo;
+import android.net.wifi.WifiInfo;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Message;
import android.os.ParcelUuid;
+import android.util.ArraySet;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback;
+import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
+import java.util.Arrays;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
@@ -112,6 +124,9 @@
public class VcnGatewayConnection extends StateMachine {
private static final String TAG = VcnGatewayConnection.class.getSimpleName();
+ private static final int[] MERGED_CAPABILITIES =
+ new int[] {NET_CAPABILITY_NOT_METERED, NET_CAPABILITY_NOT_ROAMING};
+
private static final InetAddress DUMMY_ADDR = InetAddresses.parseNumericAddress("192.0.2.0");
private static final int ARG_NOT_PRESENT = Integer.MIN_VALUE;
@@ -122,7 +137,9 @@
private static final int TOKEN_ALL = Integer.MIN_VALUE;
private static final int NETWORK_LOSS_DISCONNECT_TIMEOUT_SECONDS = 30;
- private static final int TEARDOWN_TIMEOUT_SECONDS = 5;
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final int TEARDOWN_TIMEOUT_SECONDS = 5;
private interface EventInfo {}
@@ -356,6 +373,16 @@
*/
private static final int EVENT_TEARDOWN_TIMEOUT_EXPIRED = 8;
+ /**
+ * Sent when this VcnGatewayConnection is notified of a change in TelephonySubscriptions.
+ *
+ * <p>Relevant in all states.
+ *
+ * @param arg1 The "all" token; this signal is always honored.
+ */
+ // TODO(b/178426520): implement handling of this event
+ private static final int EVENT_SUBSCRIPTIONS_CHANGED = 9;
+
@VisibleForTesting(visibility = Visibility.PRIVATE)
@NonNull
final DisconnectedState mDisconnectedState = new DisconnectedState();
@@ -376,10 +403,13 @@
@NonNull
final RetryTimeoutState mRetryTimeoutState = new RetryTimeoutState();
+ @NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
+
@NonNull private final VcnContext mVcnContext;
@NonNull private final ParcelUuid mSubscriptionGroup;
@NonNull private final UnderlyingNetworkTracker mUnderlyingNetworkTracker;
@NonNull private final VcnGatewayConnectionConfig mConnectionConfig;
+ @NonNull private final VcnGatewayStatusCallback mGatewayStatusCallback;
@NonNull private final Dependencies mDeps;
@NonNull private final VcnUnderlyingNetworkTrackerCallback mUnderlyingNetworkTrackerCallback;
@@ -413,13 +443,6 @@
private int mCurrentToken = -1;
/**
- * The next usable token.
- *
- * <p>A new token MUST be used for all new IKE sessions.
- */
- private int mNextToken = 0;
-
- /**
* The number of unsuccessful attempts since the last successful connection.
*
* <p>This number MUST be incremented each time the RetryTimeout state is entered, and cleared
@@ -440,7 +463,7 @@
* <p>Set in Connecting or Migrating States, always @NonNull in Connecting, Connected, and
* Migrating states, null otherwise.
*/
- private IkeSession mIkeSession;
+ private VcnIkeSession mIkeSession;
/**
* The last known child configuration.
@@ -461,27 +484,45 @@
public VcnGatewayConnection(
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
- @NonNull VcnGatewayConnectionConfig connectionConfig) {
- this(vcnContext, subscriptionGroup, connectionConfig, new Dependencies());
+ @NonNull TelephonySubscriptionSnapshot snapshot,
+ @NonNull VcnGatewayConnectionConfig connectionConfig,
+ @NonNull VcnGatewayStatusCallback gatewayStatusCallback) {
+ this(
+ vcnContext,
+ subscriptionGroup,
+ snapshot,
+ connectionConfig,
+ gatewayStatusCallback,
+ new Dependencies());
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
VcnGatewayConnection(
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
+ @NonNull TelephonySubscriptionSnapshot snapshot,
@NonNull VcnGatewayConnectionConfig connectionConfig,
+ @NonNull VcnGatewayStatusCallback gatewayStatusCallback,
@NonNull Dependencies deps) {
super(TAG, Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
mVcnContext = vcnContext;
mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
mConnectionConfig = Objects.requireNonNull(connectionConfig, "Missing connectionConfig");
+ mGatewayStatusCallback =
+ Objects.requireNonNull(gatewayStatusCallback, "Missing gatewayStatusCallback");
mDeps = Objects.requireNonNull(deps, "Missing deps");
+ mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
+
mUnderlyingNetworkTrackerCallback = new VcnUnderlyingNetworkTrackerCallback();
mUnderlyingNetworkTracker =
mDeps.newUnderlyingNetworkTracker(
- mVcnContext, subscriptionGroup, mUnderlyingNetworkTrackerCallback);
+ mVcnContext,
+ subscriptionGroup,
+ mLastSnapshot,
+ mConnectionConfig.getAllUnderlyingCapabilities(),
+ mUnderlyingNetworkTrackerCallback);
mIpSecManager = mVcnContext.getContext().getSystemService(IpSecManager.class);
IpSecTunnelInterface iface;
@@ -534,10 +575,28 @@
mUnderlyingNetworkTracker.teardown();
}
+ /**
+ * Notify this Gateway that subscriptions have changed.
+ *
+ * <p>This snapshot should be used to update any keepalive requests necessary for potential
+ * underlying Networks in this Gateway's subscription group.
+ */
+ public void updateSubscriptionSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot) {
+ Objects.requireNonNull(snapshot, "Missing snapshot");
+ mVcnContext.ensureRunningOnLooperThread();
+
+ mLastSnapshot = snapshot;
+ mUnderlyingNetworkTracker.updateSubscriptionSnapshot(mLastSnapshot);
+
+ sendMessage(EVENT_SUBSCRIPTIONS_CHANGED, TOKEN_ALL);
+ }
+
private class VcnUnderlyingNetworkTrackerCallback implements UnderlyingNetworkTrackerCallback {
@Override
public void onSelectedUnderlyingNetworkChanged(
@Nullable UnderlyingNetworkRecord underlying) {
+ // TODO(b/179091925): Move the delayed-message handling to BaseState
+
// If underlying is null, all underlying networks have been lost. Disconnect VCN after a
// timeout.
if (underlying == null) {
@@ -643,6 +702,22 @@
protected abstract void processStateMsg(Message msg) throws Exception;
+ @Override
+ public void exit() {
+ try {
+ exitState();
+ } catch (Exception e) {
+ Slog.wtf(TAG, "Uncaught exception", e);
+ sendMessage(
+ EVENT_DISCONNECT_REQUESTED,
+ TOKEN_ALL,
+ new EventDisconnectRequestedInfo(
+ DISCONNECT_REASON_INTERNAL_ERROR + e.toString()));
+ }
+ }
+
+ protected void exitState() throws Exception {}
+
protected void logUnhandledMessage(Message msg) {
// Log as unexpected all known messages, and log all else as unknown.
switch (msg.what) {
@@ -653,7 +728,8 @@
case EVENT_TRANSFORM_CREATED: // Fallthrough
case EVENT_SETUP_COMPLETED: // Fallthrough
case EVENT_DISCONNECT_REQUESTED: // Fallthrough
- case EVENT_TEARDOWN_TIMEOUT_EXPIRED:
+ case EVENT_TEARDOWN_TIMEOUT_EXPIRED: // Fallthrough
+ case EVENT_SUBSCRIPTIONS_CHANGED:
logUnexpectedEvent(msg.what);
break;
default:
@@ -669,18 +745,11 @@
}
}
- protected void teardownIke() {
- if (mIkeSession != null) {
- mIkeSession.close();
- }
- }
-
protected void handleDisconnectRequested(String msg) {
Slog.v(TAG, "Tearing down. Cause: " + msg);
mIsRunning = false;
teardownNetwork();
- teardownIke();
if (mIkeSession == null) {
// Already disconnected, go straight to DisconnectedState
@@ -773,8 +842,91 @@
* does not complete teardown in a timely fashion, it will be killed (forcibly closed).
*/
private class DisconnectingState extends ActiveBaseState {
+ /**
+ * Whether to skip the RetryTimeoutState and go straight to the ConnectingState.
+ *
+ * <p>This is used when an underlying network change triggered a restart on a new network.
+ *
+ * <p>Reset (to false) upon exit of the DisconnectingState.
+ */
+ private boolean mSkipRetryTimeout = false;
+
+ // TODO(b/178441390): Remove this in favor of resetting retry timers on UND_NET change.
+ public void setSkipRetryTimeout(boolean shouldSkip) {
+ mSkipRetryTimeout = shouldSkip;
+ }
+
@Override
- protected void processStateMsg(Message msg) {}
+ protected void enterState() throws Exception {
+ if (mIkeSession == null) {
+ Slog.wtf(TAG, "IKE session was already closed when entering Disconnecting state.");
+ sendMessage(EVENT_SESSION_CLOSED, mCurrentToken);
+ return;
+ }
+
+ // If underlying network has already been lost, save some time and just kill the session
+ if (mUnderlying == null) {
+ // Will trigger a EVENT_SESSION_CLOSED as IkeSession shuts down.
+ mIkeSession.kill();
+ return;
+ }
+
+ mIkeSession.close();
+ sendMessageDelayed(
+ EVENT_TEARDOWN_TIMEOUT_EXPIRED,
+ mCurrentToken,
+ TimeUnit.SECONDS.toMillis(TEARDOWN_TIMEOUT_SECONDS));
+ }
+
+ @Override
+ protected void processStateMsg(Message msg) {
+ switch (msg.what) {
+ case EVENT_UNDERLYING_NETWORK_CHANGED: // Fallthrough
+ mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
+
+ // If we received a new underlying network, continue.
+ if (mUnderlying != null) {
+ break;
+ }
+
+ // Fallthrough; no network exists to send IKE close session requests.
+ case EVENT_TEARDOWN_TIMEOUT_EXPIRED:
+ // Grace period ended. Kill session, triggering EVENT_SESSION_CLOSED
+ mIkeSession.kill();
+
+ break;
+ case EVENT_DISCONNECT_REQUESTED:
+ teardownNetwork();
+
+ String reason = ((EventDisconnectRequestedInfo) msg.obj).reason;
+ if (reason.equals(DISCONNECT_REASON_UNDERLYING_NETWORK_LOST)) {
+ // Will trigger EVENT_SESSION_CLOSED immediately.
+ mIkeSession.kill();
+ break;
+ }
+
+ // Otherwise we are already in the process of shutting down.
+ break;
+ case EVENT_SESSION_CLOSED:
+ mIkeSession = null;
+
+ if (mIsRunning && mUnderlying != null) {
+ transitionTo(mSkipRetryTimeout ? mConnectingState : mRetryTimeoutState);
+ } else {
+ teardownNetwork();
+ transitionTo(mDisconnectedState);
+ }
+ break;
+ default:
+ logUnhandledMessage(msg);
+ break;
+ }
+ }
+
+ @Override
+ protected void exitState() throws Exception {
+ mSkipRetryTimeout = false;
+ }
}
/**
@@ -785,10 +937,175 @@
*/
private class ConnectingState extends ActiveBaseState {
@Override
- protected void processStateMsg(Message msg) {}
+ protected void enterState() {
+ if (mIkeSession != null) {
+ Slog.wtf(TAG, "ConnectingState entered with active session");
+
+ // Attempt to recover.
+ mIkeSession.kill();
+ mIkeSession = null;
+ }
+
+ mIkeSession = buildIkeSession();
+ }
+
+ @Override
+ protected void processStateMsg(Message msg) {
+ switch (msg.what) {
+ case EVENT_UNDERLYING_NETWORK_CHANGED:
+ final UnderlyingNetworkRecord oldUnderlying = mUnderlying;
+ mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
+
+ if (oldUnderlying == null) {
+ // This should never happen, but if it does, there's likely a nasty bug.
+ Slog.wtf(TAG, "Old underlying network was null in connected state. Bug?");
+ }
+
+ // If new underlying is null, all underlying networks have been lost; disconnect
+ if (mUnderlying == null) {
+ transitionTo(mDisconnectingState);
+ break;
+ }
+
+ if (oldUnderlying != null
+ && mUnderlying.network.equals(oldUnderlying.network)) {
+ break; // Only network properties have changed; continue connecting.
+ }
+ // Else, retry on the new network.
+
+ // Immediately come back to the ConnectingState (skip RetryTimeout, since this
+ // isn't a failure)
+ mDisconnectingState.setSkipRetryTimeout(true);
+
+ // fallthrough - disconnect, and retry on new network.
+ case EVENT_SESSION_LOST:
+ transitionTo(mDisconnectingState);
+ break;
+ case EVENT_SESSION_CLOSED:
+ // Disconnecting state waits for EVENT_SESSION_CLOSED to shutdown, and this
+ // message may not be posted again. Defer to ensure immediate shutdown.
+ deferMessage(msg);
+
+ transitionTo(mDisconnectingState);
+ break;
+ case EVENT_SETUP_COMPLETED: // fallthrough
+ case EVENT_TRANSFORM_CREATED:
+ // Child setup complete; move to ConnectedState for NetworkAgent registration
+ deferMessage(msg);
+ transitionTo(mConnectedState);
+ break;
+ case EVENT_DISCONNECT_REQUESTED:
+ handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason);
+ break;
+ default:
+ logUnhandledMessage(msg);
+ break;
+ }
+ }
}
- private abstract class ConnectedStateBase extends ActiveBaseState {}
+ private abstract class ConnectedStateBase extends ActiveBaseState {
+ protected void updateNetworkAgent(
+ @NonNull IpSecTunnelInterface tunnelIface,
+ @NonNull NetworkAgent agent,
+ @NonNull ChildSessionConfiguration childConfig) {
+ final NetworkCapabilities caps =
+ buildNetworkCapabilities(mConnectionConfig, mUnderlying);
+ final LinkProperties lp =
+ buildConnectedLinkProperties(mConnectionConfig, tunnelIface, childConfig);
+
+ agent.sendNetworkCapabilities(caps);
+ agent.sendLinkProperties(lp);
+ }
+
+ protected NetworkAgent buildNetworkAgent(
+ @NonNull IpSecTunnelInterface tunnelIface,
+ @NonNull ChildSessionConfiguration childConfig) {
+ final NetworkCapabilities caps =
+ buildNetworkCapabilities(mConnectionConfig, mUnderlying);
+ final LinkProperties lp =
+ buildConnectedLinkProperties(mConnectionConfig, tunnelIface, childConfig);
+
+ final NetworkAgent agent =
+ new NetworkAgent(
+ mVcnContext.getContext(),
+ mVcnContext.getLooper(),
+ TAG,
+ caps,
+ lp,
+ Vcn.getNetworkScore(),
+ new NetworkAgentConfig(),
+ mVcnContext.getVcnNetworkProvider()) {
+ @Override
+ public void unwanted() {
+ teardownAsynchronously();
+ }
+ };
+
+ agent.register();
+ agent.markConnected();
+
+ return agent;
+ }
+
+ protected void applyTransform(
+ int token,
+ @NonNull IpSecTunnelInterface tunnelIface,
+ @NonNull Network underlyingNetwork,
+ @NonNull IpSecTransform transform,
+ int direction) {
+ try {
+ // TODO: Set underlying network of tunnel interface
+
+ // Transforms do not need to be persisted; the IkeSession will keep them alive
+ mIpSecManager.applyTunnelModeTransform(tunnelIface, direction, transform);
+ } catch (IOException e) {
+ Slog.d(TAG, "Transform application failed for network " + token, e);
+ sessionLost(token, e);
+ }
+ }
+
+ protected void setupInterface(
+ int token,
+ @NonNull IpSecTunnelInterface tunnelIface,
+ @NonNull ChildSessionConfiguration childConfig) {
+ setupInterface(token, tunnelIface, childConfig, null);
+ }
+
+ protected void setupInterface(
+ int token,
+ @NonNull IpSecTunnelInterface tunnelIface,
+ @NonNull ChildSessionConfiguration childConfig,
+ @Nullable ChildSessionConfiguration oldChildConfig) {
+ try {
+ final Set<LinkAddress> newAddrs =
+ new ArraySet<>(childConfig.getInternalAddresses());
+ final Set<LinkAddress> existingAddrs = new ArraySet<>();
+ if (oldChildConfig != null) {
+ existingAddrs.addAll(oldChildConfig.getInternalAddresses());
+ }
+
+ final Set<LinkAddress> toAdd = new ArraySet<>();
+ toAdd.addAll(newAddrs);
+ toAdd.removeAll(existingAddrs);
+
+ final Set<LinkAddress> toRemove = new ArraySet<>();
+ toRemove.addAll(existingAddrs);
+ toRemove.removeAll(newAddrs);
+
+ for (LinkAddress address : toAdd) {
+ tunnelIface.addAddress(address.getAddress(), address.getPrefixLength());
+ }
+
+ for (LinkAddress address : toRemove) {
+ tunnelIface.removeAddress(address.getAddress(), address.getPrefixLength());
+ }
+ } catch (IOException e) {
+ Slog.d(TAG, "Adding address to tunnel failed for token " + token, e);
+ sessionLost(token, e);
+ }
+ }
+ }
/**
* Stable state representing a VCN that has a functioning connection to the mobility anchor.
@@ -798,7 +1115,89 @@
*/
class ConnectedState extends ConnectedStateBase {
@Override
- protected void processStateMsg(Message msg) {}
+ protected void enterState() throws Exception {
+ // Successful connection, clear failed attempt counter
+ mFailedAttempts = 0;
+ }
+
+ @Override
+ protected void processStateMsg(Message msg) {
+ switch (msg.what) {
+ case EVENT_UNDERLYING_NETWORK_CHANGED:
+ handleUnderlyingNetworkChanged(msg);
+ break;
+ case EVENT_SESSION_CLOSED:
+ // Disconnecting state waits for EVENT_SESSION_CLOSED to shutdown, and this
+ // message may not be posted again. Defer to ensure immediate shutdown.
+ deferMessage(msg);
+ transitionTo(mDisconnectingState);
+ break;
+ case EVENT_SESSION_LOST:
+ transitionTo(mDisconnectingState);
+ break;
+ case EVENT_TRANSFORM_CREATED:
+ final EventTransformCreatedInfo transformCreatedInfo =
+ (EventTransformCreatedInfo) msg.obj;
+
+ applyTransform(
+ mCurrentToken,
+ mTunnelIface,
+ mUnderlying.network,
+ transformCreatedInfo.transform,
+ transformCreatedInfo.direction);
+ break;
+ case EVENT_SETUP_COMPLETED:
+ mChildConfig = ((EventSetupCompletedInfo) msg.obj).childSessionConfig;
+
+ setupInterfaceAndNetworkAgent(mCurrentToken, mTunnelIface, mChildConfig);
+ break;
+ case EVENT_DISCONNECT_REQUESTED:
+ handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason);
+ break;
+ default:
+ logUnhandledMessage(msg);
+ break;
+ }
+ }
+
+ private void handleUnderlyingNetworkChanged(@NonNull Message msg) {
+ final UnderlyingNetworkRecord oldUnderlying = mUnderlying;
+ mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
+
+ if (mUnderlying == null) {
+ // Ignored for now; a new network may be coming up. If none does, the delayed
+ // NETWORK_LOST disconnect will be fired, and tear down the session + network.
+ return;
+ }
+
+ // mUnderlying assumed non-null, given check above.
+ // If network changed, migrate. Otherwise, update any existing networkAgent.
+ if (oldUnderlying == null || !oldUnderlying.network.equals(mUnderlying.network)) {
+ mIkeSession.setNetwork(mUnderlying.network);
+ } else {
+ // oldUnderlying is non-null & underlying network itself has not changed
+ // (only network properties were changed).
+
+ // Network not yet set up, or child not yet connected.
+ if (mNetworkAgent != null && mChildConfig != null) {
+ // If only network properties changed and agent is active, update properties
+ updateNetworkAgent(mTunnelIface, mNetworkAgent, mChildConfig);
+ }
+ }
+ }
+
+ protected void setupInterfaceAndNetworkAgent(
+ int token,
+ @NonNull IpSecTunnelInterface tunnelIface,
+ @NonNull ChildSessionConfiguration childConfig) {
+ setupInterface(token, tunnelIface, childConfig);
+
+ if (mNetworkAgent == null) {
+ mNetworkAgent = buildNetworkAgent(tunnelIface, childConfig);
+ } else {
+ updateNetworkAgent(tunnelIface, mNetworkAgent, childConfig);
+ }
+ }
}
/**
@@ -813,19 +1212,66 @@
@VisibleForTesting(visibility = Visibility.PRIVATE)
static NetworkCapabilities buildNetworkCapabilities(
- @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig) {
- final NetworkCapabilities caps = new NetworkCapabilities();
+ @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig,
+ @Nullable UnderlyingNetworkRecord underlying) {
+ final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder();
- caps.addTransportType(TRANSPORT_CELLULAR);
- caps.addCapability(NET_CAPABILITY_NOT_CONGESTED);
- caps.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ builder.addTransportType(TRANSPORT_CELLULAR);
+ builder.addCapability(NET_CAPABILITY_NOT_CONGESTED);
+ builder.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
// Add exposed capabilities
for (int cap : gatewayConnectionConfig.getAllExposedCapabilities()) {
- caps.addCapability(cap);
+ builder.addCapability(cap);
}
- return caps;
+ if (underlying != null) {
+ final NetworkCapabilities underlyingCaps = underlying.networkCapabilities;
+
+ // Mirror merged capabilities.
+ for (int cap : MERGED_CAPABILITIES) {
+ if (underlyingCaps.hasCapability(cap)) {
+ builder.addCapability(cap);
+ }
+ }
+
+ // Set admin UIDs for ConnectivityDiagnostics use.
+ final int[] underlyingAdminUids = underlyingCaps.getAdministratorUids();
+ Arrays.sort(underlyingAdminUids); // Sort to allow contains check below.
+
+ final int[] adminUids;
+ if (underlyingCaps.getOwnerUid() > 0 // No owner UID specified
+ && 0 > Arrays.binarySearch(// Owner UID not found in admin UID list.
+ underlyingAdminUids, underlyingCaps.getOwnerUid())) {
+ adminUids = Arrays.copyOf(underlyingAdminUids, underlyingAdminUids.length + 1);
+ adminUids[adminUids.length - 1] = underlyingCaps.getOwnerUid();
+ Arrays.sort(adminUids);
+ } else {
+ adminUids = underlyingAdminUids;
+ }
+ builder.setAdministratorUids(adminUids);
+
+ // Set TransportInfo for SysUI use (never parcelled out of SystemServer).
+ if (underlyingCaps.hasTransport(TRANSPORT_WIFI)
+ && underlyingCaps.getTransportInfo() instanceof WifiInfo) {
+ final WifiInfo wifiInfo = (WifiInfo) underlyingCaps.getTransportInfo();
+ builder.setTransportInfo(new VcnTransportInfo(wifiInfo));
+ } else if (underlyingCaps.hasTransport(TRANSPORT_CELLULAR)
+ && underlyingCaps.getNetworkSpecifier() instanceof TelephonyNetworkSpecifier) {
+ final TelephonyNetworkSpecifier telNetSpecifier =
+ (TelephonyNetworkSpecifier) underlyingCaps.getNetworkSpecifier();
+ builder.setTransportInfo(new VcnTransportInfo(telNetSpecifier.getSubscriptionId()));
+ } else {
+ Slog.wtf(
+ TAG,
+ "Unknown transport type or missing TransportInfo/NetworkSpecifier for"
+ + " non-null underlying network");
+ }
+ }
+
+ // TODO: Make a VcnNetworkSpecifier, and match all underlying subscription IDs.
+
+ return builder.build();
}
private static LinkProperties buildConnectedLinkProperties(
@@ -946,6 +1392,38 @@
mIsRunning = isRunning;
}
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ VcnIkeSession getIkeSession() {
+ return mIkeSession;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ void setIkeSession(@Nullable VcnIkeSession session) {
+ mIkeSession = session;
+ }
+
+ private IkeSessionParams buildIkeParams() {
+ // TODO: Implement this once IkeSessionParams is persisted
+ return null;
+ }
+
+ private ChildSessionParams buildChildParams() {
+ // TODO: Implement this once IkeSessionParams is persisted
+ return null;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ VcnIkeSession buildIkeSession() {
+ final int token = ++mCurrentToken;
+
+ return mDeps.newIkeSession(
+ mVcnContext,
+ buildIkeParams(),
+ buildChildParams(),
+ new IkeSessionCallbackImpl(token),
+ new ChildSessionCallbackImpl(token));
+ }
+
/** External dependencies used by VcnGatewayConnection, for injection in tests */
@VisibleForTesting(visibility = Visibility.PRIVATE)
public static class Dependencies {
@@ -953,24 +1431,79 @@
public UnderlyingNetworkTracker newUnderlyingNetworkTracker(
VcnContext vcnContext,
ParcelUuid subscriptionGroup,
+ TelephonySubscriptionSnapshot snapshot,
+ Set<Integer> requiredUnderlyingNetworkCapabilities,
UnderlyingNetworkTrackerCallback callback) {
- return new UnderlyingNetworkTracker(vcnContext, subscriptionGroup, callback);
+ return new UnderlyingNetworkTracker(
+ vcnContext,
+ subscriptionGroup,
+ snapshot,
+ requiredUnderlyingNetworkCapabilities,
+ callback);
}
/** Builds a new IkeSession. */
- public IkeSession newIkeSession(
+ public VcnIkeSession newIkeSession(
VcnContext vcnContext,
IkeSessionParams ikeSessionParams,
ChildSessionParams childSessionParams,
IkeSessionCallback ikeSessionCallback,
ChildSessionCallback childSessionCallback) {
- return new IkeSession(
- vcnContext.getContext(),
+ return new VcnIkeSession(
+ vcnContext,
ikeSessionParams,
childSessionParams,
- new HandlerExecutor(new Handler(vcnContext.getLooper())),
ikeSessionCallback,
childSessionCallback);
}
}
+
+ /** Proxy implementation of IKE session, used for testing. */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public static class VcnIkeSession {
+ private final IkeSession mImpl;
+
+ public VcnIkeSession(
+ VcnContext vcnContext,
+ IkeSessionParams ikeSessionParams,
+ ChildSessionParams childSessionParams,
+ IkeSessionCallback ikeSessionCallback,
+ ChildSessionCallback childSessionCallback) {
+ mImpl =
+ new IkeSession(
+ vcnContext.getContext(),
+ ikeSessionParams,
+ childSessionParams,
+ new HandlerExecutor(new Handler(vcnContext.getLooper())),
+ ikeSessionCallback,
+ childSessionCallback);
+ }
+
+ /** Creates a new IKE Child session. */
+ public void openChildSession(
+ @NonNull ChildSessionParams childSessionParams,
+ @NonNull ChildSessionCallback childSessionCallback) {
+ mImpl.openChildSession(childSessionParams, childSessionCallback);
+ }
+
+ /** Closes an IKE session as identified by the ChildSessionCallback. */
+ public void closeChildSession(@NonNull ChildSessionCallback childSessionCallback) {
+ mImpl.closeChildSession(childSessionCallback);
+ }
+
+ /** Gracefully closes this IKE Session, waiting for remote acknowledgement. */
+ public void close() {
+ mImpl.close();
+ }
+
+ /** Forcibly kills this IKE Session, without waiting for a closure confirmation. */
+ public void kill() {
+ mImpl.kill();
+ }
+
+ /** Sets the underlying network used by the IkeSession. */
+ public void setNetwork(@NonNull Network network) {
+ mImpl.setNetwork(network);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
index 7f5b23c..fe4ea30 100644
--- a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
+++ b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
@@ -21,9 +21,12 @@
import android.net.NetworkProvider;
import android.net.NetworkRequest;
import android.os.Looper;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
-import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
import java.util.Objects;
import java.util.Set;
@@ -40,24 +43,36 @@
private static final String TAG = VcnNetworkProvider.class.getSimpleName();
private final Set<NetworkRequestListener> mListeners = new ArraySet<>();
- private final SparseArray<NetworkRequestEntry> mRequests = new SparseArray<>();
+
+ /**
+ * Cache of NetworkRequest(s), scores and network providers, keyed by NetworkRequest
+ *
+ * <p>NetworkRequests are immutable once created, and therefore can be used as stable keys.
+ */
+ private final ArrayMap<NetworkRequest, NetworkRequestEntry> mRequests = new ArrayMap<>();
public VcnNetworkProvider(Context context, Looper looper) {
super(context, looper, VcnNetworkProvider.class.getSimpleName());
}
- // Package-private
- void registerListener(@NonNull NetworkRequestListener listener) {
+ /**
+ * Registers a NetworkRequestListener with this NetworkProvider.
+ *
+ * <p>Upon registering, the provided listener will receive all cached requests.
+ */
+ @VisibleForTesting(visibility = Visibility.PACKAGE)
+ public void registerListener(@NonNull NetworkRequestListener listener) {
mListeners.add(listener);
// Send listener all cached requests
- for (int i = 0; i < mRequests.size(); i++) {
- notifyListenerForEvent(listener, mRequests.valueAt(i));
+ for (NetworkRequestEntry entry : mRequests.values()) {
+ notifyListenerForEvent(listener, entry);
}
}
- // Package-private
- void unregisterListener(@NonNull NetworkRequestListener listener) {
+ /** Unregisters the specified listener from receiving future NetworkRequests. */
+ @VisibleForTesting(visibility = Visibility.PACKAGE)
+ public void unregisterListener(@NonNull NetworkRequestListener listener) {
mListeners.remove(listener);
}
@@ -75,7 +90,9 @@
request, score, providerId));
final NetworkRequestEntry entry = new NetworkRequestEntry(request, score, providerId);
- mRequests.put(request.requestId, entry);
+
+ // NetworkRequests are immutable once created, and therefore can be used as stable keys.
+ mRequests.put(request, entry);
// TODO(b/176939047): Intelligently route requests to prioritized VcnInstances (based on
// Default Data Sub, or similar)
@@ -86,7 +103,7 @@
@Override
public void onNetworkRequestWithdrawn(@NonNull NetworkRequest request) {
- mRequests.remove(request.requestId);
+ mRequests.remove(request);
}
private static class NetworkRequestEntry {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 7af237b..fd8fa82 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2023,13 +2023,6 @@
final ActivityRecord top = targetTask.performClearTaskForReuseLocked(mStartActivity,
mLaunchFlags);
- // The above code can remove {@code reusedActivity} from the task, leading to the
- // {@code ActivityRecord} removing its reference to the {@code Task}. The task
- // reference is needed in the call below to {@link setTargetStackAndMoveToFrontIfNeeded}
- if (targetTaskTop.getTask() == null) {
- targetTask.addChild(targetTaskTop);
- }
-
if (top != null) {
if (top.isRootOfTask()) {
// Activity aliases may mean we use different intents for the top activity,
diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS
index 389d07a..bbcc2c1 100644
--- a/services/core/jni/OWNERS
+++ b/services/core/jni/OWNERS
@@ -11,6 +11,9 @@
per-file com_android_server_HardwarePropertiesManagerService.cpp = michaelwr@google.com, santoscordon@google.com
per-file com_android_server_power_PowerManagerService.* = michaelwr@google.com, santoscordon@google.com
+# BatteryStats
+per-file com_android_server_am_BatteryStatsService.cpp = file:/BATTERY_STATS_OWNERS
+
per-file Android.bp = file:platform/build/soong:/OWNERS
per-file com_android_server_Usb* = file:/services/usb/OWNERS
per-file com_android_server_Vibrator* = file:/services/core/java/com/android/server/vibrator/OWNERS
diff --git a/services/core/xsd/Android.bp b/services/core/xsd/Android.bp
index b7d6424..3690afc 100644
--- a/services/core/xsd/Android.bp
+++ b/services/core/xsd/Android.bp
@@ -8,11 +8,19 @@
xsd_config {
name: "platform-compat-config",
- srcs: ["platform-compat-config.xsd"],
- api_dir: "platform-compat-schema",
+ srcs: ["platform-compat/config/platform-compat-config.xsd"],
+ api_dir: "platform-compat/config/schema",
package_name: "com.android.server.compat.config",
}
+xsd_config {
+ name: "platform-compat-overrides",
+ srcs: ["platform-compat/overrides/platform-compat-overrides.xsd"],
+ api_dir: "platform-compat/overrides/schema",
+ package_name: "com.android.server.compat.overrides",
+ gen_writer: true,
+}
+
xsd_config {
name: "display-device-config",
diff --git a/services/core/xsd/platform-compat-schema/OWNERS b/services/core/xsd/platform-compat/OWNERS
similarity index 100%
rename from services/core/xsd/platform-compat-schema/OWNERS
rename to services/core/xsd/platform-compat/OWNERS
diff --git a/services/core/xsd/platform-compat-config.xsd b/services/core/xsd/platform-compat/config/platform-compat-config.xsd
similarity index 100%
rename from services/core/xsd/platform-compat-config.xsd
rename to services/core/xsd/platform-compat/config/platform-compat-config.xsd
diff --git a/services/core/xsd/platform-compat-schema/current.txt b/services/core/xsd/platform-compat/config/schema/current.txt
similarity index 100%
rename from services/core/xsd/platform-compat-schema/current.txt
rename to services/core/xsd/platform-compat/config/schema/current.txt
diff --git a/services/core/xsd/platform-compat-schema/last_current.txt b/services/core/xsd/platform-compat/config/schema/last_current.txt
similarity index 100%
rename from services/core/xsd/platform-compat-schema/last_current.txt
rename to services/core/xsd/platform-compat/config/schema/last_current.txt
diff --git a/services/core/xsd/platform-compat-schema/last_removed.txt b/services/core/xsd/platform-compat/config/schema/last_removed.txt
similarity index 100%
rename from services/core/xsd/platform-compat-schema/last_removed.txt
rename to services/core/xsd/platform-compat/config/schema/last_removed.txt
diff --git a/services/core/xsd/platform-compat-schema/removed.txt b/services/core/xsd/platform-compat/config/schema/removed.txt
similarity index 100%
rename from services/core/xsd/platform-compat-schema/removed.txt
rename to services/core/xsd/platform-compat/config/schema/removed.txt
diff --git a/services/core/xsd/platform-compat/overrides/platform-compat-overrides.xsd b/services/core/xsd/platform-compat/overrides/platform-compat-overrides.xsd
new file mode 100644
index 0000000..e27e1b8
--- /dev/null
+++ b/services/core/xsd/platform-compat/overrides/platform-compat-overrides.xsd
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!-- This defines the format of the XML file used to store compat config overrides in
+ ~ /data/misc/appcompat/compat_framework_overrides.xml
+-->
+<xs:schema version="2.0" elementFormDefault="qualified"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema">
+
+
+ <xs:complexType name="override-value">
+ <xs:attribute type="xs:string" name="packageName" use="required" />
+ <xs:attribute type="xs:boolean" name="enabled" use="required" />
+ </xs:complexType>
+
+ <xs:complexType name="change-overrides">
+ <xs:attribute type="xs:long" name="changeId" use="required"/>
+ <xs:element name="validated">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="override-value" type="override-value" maxOccurs="unbounded" minOccurs="0" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="deferred">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="override-value" type="override-value" maxOccurs="unbounded" minOccurs="0" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ </xs:complexType>
+
+ <xs:element name="overrides">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="change-overrides" type="change-overrides" maxOccurs="unbounded" minOccurs="0" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+</xs:schema>
diff --git a/services/core/xsd/platform-compat/overrides/schema/current.txt b/services/core/xsd/platform-compat/overrides/schema/current.txt
new file mode 100644
index 0000000..08b8207
--- /dev/null
+++ b/services/core/xsd/platform-compat/overrides/schema/current.txt
@@ -0,0 +1,51 @@
+// Signature format: 2.0
+package com.android.server.compat.overrides {
+
+ public class ChangeOverrides {
+ ctor public ChangeOverrides();
+ method public long getChangeId();
+ method public com.android.server.compat.overrides.ChangeOverrides.Deferred getDeferred();
+ method public com.android.server.compat.overrides.ChangeOverrides.Validated getValidated();
+ method public void setChangeId(long);
+ method public void setDeferred(com.android.server.compat.overrides.ChangeOverrides.Deferred);
+ method public void setValidated(com.android.server.compat.overrides.ChangeOverrides.Validated);
+ }
+
+ public static class ChangeOverrides.Deferred {
+ ctor public ChangeOverrides.Deferred();
+ method public java.util.List<com.android.server.compat.overrides.OverrideValue> getOverrideValue();
+ }
+
+ public static class ChangeOverrides.Validated {
+ ctor public ChangeOverrides.Validated();
+ method public java.util.List<com.android.server.compat.overrides.OverrideValue> getOverrideValue();
+ }
+
+ public class OverrideValue {
+ ctor public OverrideValue();
+ method public boolean getEnabled();
+ method public String getPackageName();
+ method public void setEnabled(boolean);
+ method public void setPackageName(String);
+ }
+
+ public class Overrides {
+ ctor public Overrides();
+ method public java.util.List<com.android.server.compat.overrides.ChangeOverrides> getChangeOverrides();
+ }
+
+ public class XmlParser {
+ ctor public XmlParser();
+ method public static com.android.server.compat.overrides.Overrides read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ }
+
+ public class XmlWriter implements java.io.Closeable {
+ ctor public XmlWriter(java.io.PrintWriter);
+ method public void close();
+ method public static void write(com.android.server.compat.overrides.XmlWriter, com.android.server.compat.overrides.Overrides) throws java.io.IOException;
+ }
+
+}
+
diff --git a/services/core/xsd/platform-compat-schema/last_current.txt b/services/core/xsd/platform-compat/overrides/schema/last_current.txt
similarity index 100%
copy from services/core/xsd/platform-compat-schema/last_current.txt
copy to services/core/xsd/platform-compat/overrides/schema/last_current.txt
diff --git a/services/core/xsd/platform-compat-schema/last_removed.txt b/services/core/xsd/platform-compat/overrides/schema/last_removed.txt
similarity index 100%
copy from services/core/xsd/platform-compat-schema/last_removed.txt
copy to services/core/xsd/platform-compat/overrides/schema/last_removed.txt
diff --git a/services/core/xsd/platform-compat-schema/removed.txt b/services/core/xsd/platform-compat/overrides/schema/removed.txt
similarity index 100%
copy from services/core/xsd/platform-compat-schema/removed.txt
copy to services/core/xsd/platform-compat/overrides/schema/removed.txt
diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp
index 0ae10b6..a3cadf3 100644
--- a/services/incremental/BinderIncrementalService.cpp
+++ b/services/incremental/BinderIncrementalService.cpp
@@ -88,7 +88,6 @@
}
sp<ProcessState> ps(ProcessState::self());
ps->startThreadPool();
- ps->giveThreadPoolName();
// sm->addService increments the reference count, and now we're OK with returning the pointer.
return self.get();
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 3f50bcd..62c3f43 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -139,6 +139,7 @@
import com.android.server.om.OverlayManagerService;
import com.android.server.os.BugreportManagerService;
import com.android.server.os.DeviceIdentifiersPolicyService;
+import com.android.server.os.NativeTombstoneManagerService;
import com.android.server.os.SchedulingPolicyService;
import com.android.server.people.PeopleService;
import com.android.server.pm.BackgroundDexOptService;
@@ -1073,6 +1074,11 @@
mSystemServiceManager.startService(ROLLBACK_MANAGER_SERVICE_CLASS);
t.traceEnd();
+ // Tracks native tombstones.
+ t.traceBegin("StartNativeTombstoneManagerService");
+ mSystemServiceManager.startService(NativeTombstoneManagerService.class);
+ t.traceEnd();
+
// Service to capture bugreports.
t.traceBegin("StartBugreportManagerService");
mSystemServiceManager.startService(BugreportManagerService.class);
@@ -1534,15 +1540,6 @@
}
t.traceEnd();
- t.traceBegin("StartVcnManagementService");
- try {
- vcnManagement = VcnManagementService.create(context);
- ServiceManager.addService(Context.VCN_MANAGEMENT_SERVICE, vcnManagement);
- } catch (Throwable e) {
- reportWtf("starting VCN Management Service", e);
- }
- t.traceEnd();
-
t.traceBegin("StartTextServicesManager");
mSystemServiceManager.startService(TextServicesManagerService.Lifecycle.class);
t.traceEnd();
@@ -1640,6 +1637,15 @@
networkPolicy.bindConnectivityManager(connectivity);
t.traceEnd();
+ t.traceBegin("StartVcnManagementService");
+ try {
+ vcnManagement = VcnManagementService.create(context);
+ ServiceManager.addService(Context.VCN_MANAGEMENT_SERVICE, vcnManagement);
+ } catch (Throwable e) {
+ reportWtf("starting VCN Management Service", e);
+ }
+ t.traceEnd();
+
t.traceBegin("StartNsdService");
try {
serviceDiscovery = NsdService.create(context);
@@ -2421,15 +2427,6 @@
reportWtf("making IpSec Service ready", e);
}
t.traceEnd();
- t.traceBegin("MakeVcnManagementServiceReady");
- try {
- if (vcnManagementF != null) {
- vcnManagementF.systemReady();
- }
- } catch (Throwable e) {
- reportWtf("making VcnManagementService ready", e);
- }
- t.traceEnd();
t.traceBegin("MakeNetworkStatsServiceReady");
try {
if (networkStatsF != null) {
@@ -2448,6 +2445,15 @@
reportWtf("making Connectivity Service ready", e);
}
t.traceEnd();
+ t.traceBegin("MakeVcnManagementServiceReady");
+ try {
+ if (vcnManagementF != null) {
+ vcnManagementF.systemReady();
+ }
+ } catch (Throwable e) {
+ reportWtf("making VcnManagementService ready", e);
+ }
+ t.traceEnd();
t.traceBegin("MakeNetworkPolicyServiceReady");
try {
if (networkPolicyF != null) {
diff --git a/services/smartspace/OWNERS b/services/smartspace/OWNERS
new file mode 100644
index 0000000..19ef9d7
--- /dev/null
+++ b/services/smartspace/OWNERS
@@ -0,0 +1,2 @@
+srazdan@google.com
+alexmang@google.com
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
new file mode 100644
index 0000000..1328b91
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.apphibernation;
+
+import static android.content.pm.PackageManager.MATCH_ANY_USER;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.AdditionalAnswers.returnsArgAt;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.intThat;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import android.app.IActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.UserInfo;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.os.UserManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.SystemService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests for {@link com.android.server.apphibernation.AppHibernationService}
+ */
+@SmallTest
+public final class AppHibernationServiceTest {
+ private static final String PACKAGE_SCHEME = "package";
+ private static final String PACKAGE_NAME_1 = "package1";
+ private static final String PACKAGE_NAME_2 = "package2";
+ private static final int USER_ID_1 = 1;
+ private static final int USER_ID_2 = 2;
+
+ private final List<UserInfo> mUserInfos = new ArrayList<>();
+
+ private AppHibernationService mAppHibernationService;
+ private BroadcastReceiver mBroadcastReceiver;
+ @Mock
+ private Context mContext;
+ @Mock
+ private IPackageManager mIPackageManager;
+ @Mock
+ private IActivityManager mIActivityManager;
+ @Mock
+ private UserManager mUserManager;
+ @Mock
+ private HibernationStateDiskStore<UserLevelState> mHibernationStateDiskStore;
+ @Captor
+ private ArgumentCaptor<BroadcastReceiver> mReceiverCaptor;
+
+ @Before
+ public void setUp() throws RemoteException {
+ // Share class loader to allow access to package-private classes
+ System.setProperty("dexmaker.share_classloader", "true");
+ MockitoAnnotations.initMocks(this);
+ doReturn(mContext).when(mContext).createContextAsUser(any(), anyInt());
+
+ mAppHibernationService = new AppHibernationService(new MockInjector(mContext));
+
+ verify(mContext).registerReceiver(mReceiverCaptor.capture(), any());
+ mBroadcastReceiver = mReceiverCaptor.getValue();
+
+ doReturn(mUserInfos).when(mUserManager).getUsers();
+
+ doAnswer(returnsArgAt(2)).when(mIActivityManager).handleIncomingUser(anyInt(), anyInt(),
+ anyInt(), anyBoolean(), anyBoolean(), any(), any());
+
+ List<PackageInfo> packages = new ArrayList<>();
+ packages.add(makePackageInfo(PACKAGE_NAME_1));
+ doReturn(new ParceledListSlice<>(packages)).when(mIPackageManager).getInstalledPackages(
+ intThat(arg -> (arg & MATCH_ANY_USER) != 0), anyInt());
+ mAppHibernationService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+ UserInfo userInfo = addUser(USER_ID_1);
+ mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(userInfo));
+ doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_1);
+ }
+
+ @Test
+ public void testSetHibernatingForUser_packageIsHibernating() {
+ // WHEN we hibernate a package for a user
+ mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true);
+
+ // THEN the package is marked hibernating for the user
+ assertTrue(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_1, USER_ID_1));
+ }
+
+ @Test
+ public void testSetHibernatingForUser_newPackageAdded_packageIsHibernating() {
+ // WHEN a new package is added and it is hibernated
+ Intent intent = new Intent(Intent.ACTION_PACKAGE_ADDED,
+ Uri.fromParts(PACKAGE_SCHEME, PACKAGE_NAME_2, null /* fragment */));
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, USER_ID_1);
+ mBroadcastReceiver.onReceive(mContext, intent);
+
+ mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_2, USER_ID_1, true);
+
+ // THEN the new package is hibernated
+ assertTrue(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_2, USER_ID_1));
+ }
+
+ @Test
+ public void testSetHibernatingForUser_newUserUnlocked_packageIsHibernating()
+ throws RemoteException {
+ // WHEN a new user is added and a package from the user is hibernated
+ UserInfo user2 = addUser(USER_ID_2);
+ mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(user2));
+ doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_2);
+ mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_2, true);
+
+ // THEN the new user's package is hibernated
+ assertTrue(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_1, USER_ID_2));
+ }
+
+ @Test
+ public void testIsHibernatingForUser_packageReplaced_stillReturnsHibernating() {
+ // GIVEN a package is currently hibernated
+ mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true);
+
+ // WHEN the package is removed but marked as replacing
+ Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED,
+ Uri.fromParts(PACKAGE_SCHEME, PACKAGE_NAME_2, null /* fragment */));
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, USER_ID_1);
+ intent.putExtra(Intent.EXTRA_REPLACING, true);
+ mBroadcastReceiver.onReceive(mContext, intent);
+
+ // THEN the package is still hibernating
+ assertTrue(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_1, USER_ID_1));
+ }
+
+ @Test
+ public void testSetHibernatingGlobally_packageIsHibernatingGlobally() throws RemoteException {
+ // WHEN we hibernate a package
+ mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_1, true);
+
+ // THEN the package is marked hibernating for the user
+ assertTrue(mAppHibernationService.isHibernatingGlobally(PACKAGE_NAME_1));
+ }
+
+ /**
+ * Add a mock user with one package.
+ */
+ private UserInfo addUser(int userId) throws RemoteException {
+ return addUser(userId, new String[]{PACKAGE_NAME_1});
+ }
+
+ /**
+ * Add a mock user with the packages specified.
+ */
+ private UserInfo addUser(int userId, String[] packageNames) throws RemoteException {
+ UserInfo userInfo = new UserInfo(userId, "user_" + userId, 0 /* flags */);
+ mUserInfos.add(userInfo);
+ List<PackageInfo> userPackages = new ArrayList<>();
+ for (String pkgName : packageNames) {
+ userPackages.add(makePackageInfo(pkgName));
+ }
+ doReturn(new ParceledListSlice<>(userPackages)).when(mIPackageManager)
+ .getInstalledPackages(intThat(arg -> (arg & MATCH_ANY_USER) == 0), eq(userId));
+ return userInfo;
+ }
+
+ private static PackageInfo makePackageInfo(String packageName) {
+ PackageInfo pkg = new PackageInfo();
+ pkg.packageName = packageName;
+ return pkg;
+ }
+
+ private class MockInjector implements AppHibernationService.Injector {
+ private final Context mContext;
+
+ MockInjector(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public IActivityManager getActivityManager() {
+ return mIActivityManager;
+ }
+
+ @Override
+ public Context getContext() {
+ return mContext;
+ }
+
+ @Override
+ public IPackageManager getPackageManager() {
+ return mIPackageManager;
+ }
+
+ @Override
+ public UserManager getUserManager() {
+ return mUserManager;
+ }
+
+ @Override
+ public HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore() {
+ return Mockito.mock(HibernationStateDiskStore.class);
+ }
+
+ @Override
+ public HibernationStateDiskStore<UserLevelState> getUserLevelDiskStore(int userId) {
+ return Mockito.mock(HibernationStateDiskStore.class);
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/HibernationStateDiskStoreTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/HibernationStateDiskStoreTest.java
new file mode 100644
index 0000000..59f3c35
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/apphibernation/HibernationStateDiskStoreTest.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.apphibernation;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.FileUtils;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+
+@SmallTest
+public class HibernationStateDiskStoreTest {
+ private static final String STATES_FILE_NAME = "states";
+ private final MockScheduledExecutorService mMockScheduledExecutorService =
+ new MockScheduledExecutorService();
+
+ private File mFile;
+ private HibernationStateDiskStore<String> mHibernationStateDiskStore;
+
+
+ @Before
+ public void setUp() {
+ mFile = new File(InstrumentationRegistry.getContext().getCacheDir(), "test");
+ mHibernationStateDiskStore = new HibernationStateDiskStore<>(mFile,
+ new MockProtoReadWriter(), mMockScheduledExecutorService, STATES_FILE_NAME);
+ }
+
+ @After
+ public void tearDown() {
+ FileUtils.deleteContentsAndDir(mFile);
+ }
+
+ @Test
+ public void testScheduleWriteHibernationStates_writesDataThatCanBeRead() {
+ // GIVEN some data to be written
+ List<String> toWrite = new ArrayList<>(Arrays.asList("A", "B"));
+
+ // WHEN the data is written
+ mHibernationStateDiskStore.scheduleWriteHibernationStates(toWrite);
+ mMockScheduledExecutorService.executeScheduledTask();
+
+ // THEN the read data is equal to what was written
+ List<String> storedStrings = mHibernationStateDiskStore.readHibernationStates();
+ for (int i = 0; i < toWrite.size(); i++) {
+ assertEquals(toWrite.get(i), storedStrings.get(i));
+ }
+ }
+
+ @Test
+ public void testScheduleWriteHibernationStates_laterWritesOverwritePrevious() {
+ // GIVEN store has some data it is scheduled to write
+ mHibernationStateDiskStore.scheduleWriteHibernationStates(
+ new ArrayList<>(Arrays.asList("C", "D")));
+
+ // WHEN a write is scheduled with new data
+ List<String> toWrite = new ArrayList<>(Arrays.asList("A", "B"));
+ mHibernationStateDiskStore.scheduleWriteHibernationStates(toWrite);
+ mMockScheduledExecutorService.executeScheduledTask();
+
+ // THEN the written data is the last scheduled data
+ List<String> storedStrings = mHibernationStateDiskStore.readHibernationStates();
+ for (int i = 0; i < toWrite.size(); i++) {
+ assertEquals(toWrite.get(i), storedStrings.get(i));
+ }
+ }
+
+ /**
+ * Mock proto read / writer that just writes and reads a list of String data.
+ */
+ private final class MockProtoReadWriter implements ProtoReadWriter<List<String>> {
+ private static final long FIELD_ID = 1;
+
+ @Override
+ public void writeToProto(@NonNull ProtoOutputStream stream,
+ @NonNull List<String> data) {
+ for (int i = 0, size = data.size(); i < size; i++) {
+ stream.write(FIELD_ID, data.get(i));
+ }
+ }
+
+ @Nullable
+ @Override
+ public List<String> readFromProto(@NonNull ProtoInputStream stream)
+ throws IOException {
+ ArrayList<String> list = new ArrayList<>();
+ while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ list.add(stream.readString(FIELD_ID));
+ }
+ return list;
+ }
+ }
+
+ /**
+ * Mock scheduled executor service that has minimum implementation and can synchronously
+ * execute scheduled tasks.
+ */
+ private final class MockScheduledExecutorService implements ScheduledExecutorService {
+
+ Runnable mScheduledRunnable = null;
+
+ @Override
+ public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
+ mScheduledRunnable = command;
+ return Mockito.mock(ScheduledFuture.class);
+ }
+
+ @Override
+ public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay,
+ long period, TimeUnit unit) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay,
+ long delay, TimeUnit unit) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void shutdown() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<Runnable> shutdownNow() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isShutdown() {
+ return false;
+ }
+
+ @Override
+ public boolean isTerminated() {
+ return false;
+ }
+
+ @Override
+ public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> Future<T> submit(Callable<T> task) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> Future<T> submit(Runnable task, T result) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Future<?> submit(Runnable task) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
+ throws InterruptedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout,
+ TimeUnit unit) throws InterruptedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
+ throws InterruptedException, ExecutionException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+ throws InterruptedException, ExecutionException, TimeoutException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ throw new UnsupportedOperationException();
+ }
+
+ void executeScheduledTask() {
+ mScheduledRunnable.run();
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index ac8dc34..a53ff9b 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -44,6 +44,8 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
import java.util.UUID;
@RunWith(AndroidJUnit4.class)
@@ -69,6 +71,10 @@
os.close();
}
+ private String readFile(File file) throws IOException {
+ return new String(Files.readAllBytes(Paths.get(file.toURI())));
+ }
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -499,4 +505,86 @@
assertThat(compatConfig.isChangeEnabled(1236L,
ApplicationInfoBuilder.create().withTargetSdk(1).build())).isTrue();
}
+
+ @Test
+ public void testSaveOverrides() throws Exception {
+ File overridesFile = new File(createTempDir(), "overrides.xml");
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addDisabledChangeWithId(1L)
+ .addEnableSinceSdkChangeWithId(2, 2L)
+ .build();
+ compatConfig.forceNonDebuggableFinalForTest(true);
+ compatConfig.initOverrides(overridesFile);
+ when(mPackageManager.getApplicationInfo(eq("foo.bar"), anyInt()))
+ .thenReturn(ApplicationInfoBuilder.create()
+ .withPackageName("foo.bar")
+ .debuggable()
+ .build());
+ when(mPackageManager.getApplicationInfo(eq("bar.baz"), anyInt()))
+ .thenThrow(new NameNotFoundException());
+
+ compatConfig.addOverride(1L, "foo.bar", true);
+ compatConfig.addOverride(2L, "bar.baz", false);
+
+ assertThat(readFile(overridesFile)).isEqualTo("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<overrides>\n"
+ + " <change-overrides changeId=\"1\">\n"
+ + " <validated>\n"
+ + " <override-value packageName=\"foo.bar\" enabled=\"true\">\n"
+ + " </override-value>\n"
+ + " </validated>\n"
+ + " <deferred>\n"
+ + " </deferred>\n"
+ + " </change-overrides>\n"
+ + " <change-overrides changeId=\"2\">\n"
+ + " <validated>\n"
+ + " </validated>\n"
+ + " <deferred>\n"
+ + " <override-value packageName=\"bar.baz\" enabled=\"false\">\n"
+ + " </override-value>\n"
+ + " </deferred>\n"
+ + " </change-overrides>\n"
+ + "</overrides>\n");
+ }
+
+ @Test
+ public void testLoadOverrides() throws Exception {
+ File tempDir = createTempDir();
+ File overridesFile = new File(tempDir, "overrides.xml");
+ // Change 1 is enabled for foo.bar (validated)
+ // Change 2 is disabled for bar.baz (deferred)
+ String xmlData = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+ + "<overrides>"
+ + "<change-overrides changeId=\"1\">"
+ + "<deferred/>"
+ + "<validated>"
+ + "<override-value packageName=\"foo.bar\" enabled=\"true\"/>"
+ + "</validated>"
+ + "</change-overrides>"
+ + "<change-overrides changeId=\"2\">"
+ + "<deferred>"
+ + "<override-value packageName=\"bar.baz\" enabled=\"false\"/>"
+ + "</deferred>"
+ + "<validated/>"
+ + "</change-overrides>"
+ + "</overrides>";
+ writeToFile(tempDir, "overrides.xml", xmlData);
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addDisabledChangeWithId(1L)
+ .addEnableSinceSdkChangeWithId(2, 2L)
+ .build();
+ compatConfig.forceNonDebuggableFinalForTest(true);
+ compatConfig.initOverrides(overridesFile);
+ ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
+ .withPackageName("foo.bar")
+ .debuggable()
+ .build();
+ when(mPackageManager.getApplicationInfo(eq("foo.bar"), anyInt()))
+ .thenReturn(applicationInfo);
+ when(mPackageManager.getApplicationInfo(eq("bar.baz"), anyInt()))
+ .thenThrow(new NameNotFoundException());
+
+ assertThat(compatConfig.isChangeEnabled(1L, applicationInfo)).isTrue();
+ assertThat(compatConfig.willChangeBeEnabled(2L, "bar.baz")).isFalse();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/OWNERS b/services/tests/servicestests/src/com/android/server/location/timezone/OWNERS
deleted file mode 100644
index 28aff18..0000000
--- a/services/tests/servicestests/src/com/android/server/location/timezone/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-# Bug component: 847766
-nfuller@google.com
-include /core/java/android/app/timedetector/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
index 1581d9a..691d174 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
@@ -82,6 +82,11 @@
}
@Override
+ String getRebootEscrowServerBlob() {
+ return makeDirs(mStorageDir, super.getRebootEscrowServerBlob()).getAbsolutePath();
+ }
+
+ @Override
protected File getSyntheticPasswordDirectoryForUser(int userId) {
return makeDirs(mStorageDir, super.getSyntheticPasswordDirectoryForUser(
userId).getAbsolutePath());
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
index f74e45b..a4ba4c8 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
@@ -26,6 +26,7 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doNothing;
@@ -52,6 +53,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.widget.RebootEscrowListener;
+import com.android.server.locksettings.ResumeOnRebootServiceProvider.ResumeOnRebootServiceConnection;
import org.junit.Before;
import org.junit.Test;
@@ -92,6 +94,7 @@
private UserManager mUserManager;
private RebootEscrowManager.Callbacks mCallbacks;
private IRebootEscrow mRebootEscrow;
+ private ResumeOnRebootServiceConnection mServiceConnection;
private RebootEscrowKeyStoreManager mKeyStoreManager;
LockSettingsStorageTestable mStorage;
@@ -108,6 +111,7 @@
static class MockInjector extends RebootEscrowManager.Injector {
private final IRebootEscrow mRebootEscrow;
+ private final ResumeOnRebootServiceConnection mServiceConnection;
private final RebootEscrowProviderInterface mRebootEscrowProvider;
private final UserManager mUserManager;
private final MockableRebootEscrowInjected mInjected;
@@ -116,10 +120,11 @@
MockInjector(Context context, UserManager userManager,
IRebootEscrow rebootEscrow,
RebootEscrowKeyStoreManager keyStoreManager,
+ LockSettingsStorageTestable storage,
MockableRebootEscrowInjected injected) {
- super(context);
+ super(context, storage);
mRebootEscrow = rebootEscrow;
-
+ mServiceConnection = null;
RebootEscrowProviderHalImpl.Injector halInjector =
new RebootEscrowProviderHalImpl.Injector() {
@Override
@@ -133,6 +138,22 @@
mInjected = injected;
}
+ MockInjector(Context context, UserManager userManager,
+ ResumeOnRebootServiceConnection serviceConnection,
+ RebootEscrowKeyStoreManager keyStoreManager,
+ LockSettingsStorageTestable storage,
+ MockableRebootEscrowInjected injected) {
+ super(context, storage);
+ mServiceConnection = serviceConnection;
+ mRebootEscrow = null;
+ RebootEscrowProviderServerBasedImpl.Injector injector =
+ new RebootEscrowProviderServerBasedImpl.Injector(serviceConnection);
+ mRebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(storage, injector);
+ mUserManager = userManager;
+ mKeyStoreManager = keyStoreManager;
+ mInjected = injected;
+ }
+
@Override
public UserManager getUserManager() {
return mUserManager;
@@ -165,6 +186,7 @@
mUserManager = mock(UserManager.class);
mCallbacks = mock(RebootEscrowManager.Callbacks.class);
mRebootEscrow = mock(IRebootEscrow.class);
+ mServiceConnection = mock(ResumeOnRebootServiceConnection.class);
mKeyStoreManager = mock(RebootEscrowKeyStoreManager.class);
mAesKey = new SecretKeySpec(TEST_AES_KEY, "AES");
@@ -186,7 +208,12 @@
when(mCallbacks.isUserSecure(SECURE_SECONDARY_USER_ID)).thenReturn(true);
mInjected = mock(MockableRebootEscrowInjected.class);
mService = new RebootEscrowManager(new MockInjector(mContext, mUserManager, mRebootEscrow,
- mKeyStoreManager, mInjected), mCallbacks, mStorage);
+ mKeyStoreManager, mStorage, mInjected), mCallbacks, mStorage);
+ }
+
+ private void setServerBasedRebootEscrowProvider() throws Exception {
+ mService = new RebootEscrowManager(new MockInjector(mContext, mUserManager,
+ mServiceConnection, mKeyStoreManager, mStorage, mInjected), mCallbacks, mStorage);
}
@Test
@@ -202,6 +229,19 @@
}
@Test
+ public void prepareRebootEscrowServerBased_Success() throws Exception {
+ setServerBasedRebootEscrowProvider();
+ RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+ mService.setRebootEscrowListener(mockListener);
+ mService.prepareRebootEscrow();
+
+ mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+ verify(mockListener).onPreparedForReboot(eq(true));
+ verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+ assertFalse(mStorage.hasRebootEscrowServerBlob());
+ }
+
+ @Test
public void prepareRebootEscrow_ClearCredentials_Success() throws Exception {
RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
mService.setRebootEscrowListener(mockListener);
@@ -246,6 +286,28 @@
}
@Test
+ public void armServiceServerBased_Success() throws Exception {
+ setServerBasedRebootEscrowProvider();
+ RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+ mService.setRebootEscrowListener(mockListener);
+ mService.prepareRebootEscrow();
+
+ clearInvocations(mServiceConnection);
+ mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+ verify(mockListener).onPreparedForReboot(eq(true));
+ verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+
+ when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
+ .thenAnswer(invocation -> invocation.getArgument(0));
+ assertTrue(mService.armRebootEscrowIfNeeded());
+ verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
+
+ assertTrue(mStorage.hasRebootEscrow(PRIMARY_USER_ID));
+ assertFalse(mStorage.hasRebootEscrow(NONSECURE_SECONDARY_USER_ID));
+ assertTrue(mStorage.hasRebootEscrowServerBlob());
+ }
+
+ @Test
public void armService_HalFailure_NonFatal() throws Exception {
RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
mService.setRebootEscrowListener(mockListener);
@@ -346,6 +408,40 @@
}
@Test
+ public void loadRebootEscrowDataIfAvailable_ServerBased_Success() throws Exception {
+ setServerBasedRebootEscrowProvider();
+
+ when(mInjected.getBootCount()).thenReturn(0);
+ RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+ mService.setRebootEscrowListener(mockListener);
+ mService.prepareRebootEscrow();
+
+ clearInvocations(mServiceConnection);
+ mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+ verify(mockListener).onPreparedForReboot(eq(true));
+ verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+
+ // Use x -> x for both wrap & unwrap functions.
+ when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
+ .thenAnswer(invocation -> invocation.getArgument(0));
+ assertTrue(mService.armRebootEscrowIfNeeded());
+ verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
+ assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+ // pretend reboot happens here
+ when(mInjected.getBootCount()).thenReturn(1);
+ ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
+ doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture());
+
+ when(mServiceConnection.unwrap(any(), anyLong()))
+ .thenAnswer(invocation -> invocation.getArgument(0));
+ mService.loadRebootEscrowDataIfAvailable();
+ verify(mServiceConnection).unwrap(any(), anyLong());
+ assertTrue(metricsSuccessCaptor.getValue());
+ verify(mKeyStoreManager).clearKeyStoreEncryptionKey();
+ }
+
+ @Test
public void loadRebootEscrowDataIfAvailable_TooManyBootsInBetween_NoMetrics() throws Exception {
when(mInjected.getBootCount()).thenReturn(0);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowProviderServerBasedImplTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowProviderServerBasedImplTests.java
new file mode 100644
index 0000000..bc1e025
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowProviderServerBasedImplTests.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.InstrumentationRegistry;
+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.stubbing.Answer;
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class RebootEscrowProviderServerBasedImplTests {
+ private SecretKey mKeyStoreEncryptionKey;
+ private RebootEscrowKey mRebootEscrowKey;
+ private ResumeOnRebootServiceProvider.ResumeOnRebootServiceConnection mServiceConnection;
+ private LockSettingsStorageTestable mStorage;
+ private RebootEscrowProviderServerBasedImpl mRebootEscrowProvider;
+ private Answer<byte[]> mFakeEncryption;
+
+ private static final byte[] TEST_AES_KEY = new byte[] {
+ 0x48, 0x19, 0x12, 0x54, 0x13, 0x13, 0x52, 0x31,
+ 0x44, 0x74, 0x61, 0x54, 0x29, 0x74, 0x37, 0x61,
+ 0x70, 0x70, 0x75, 0x25, 0x27, 0x31, 0x49, 0x09,
+ 0x26, 0x52, 0x72, 0x63, 0x63, 0x61, 0x78, 0x23,
+ };
+
+ @Before
+ public void setUp() throws Exception {
+ mKeyStoreEncryptionKey = new SecretKeySpec(TEST_AES_KEY, "AES");
+ mRebootEscrowKey = RebootEscrowKey.generate();
+ mServiceConnection = mock(
+ ResumeOnRebootServiceProvider.ResumeOnRebootServiceConnection.class);
+
+ Context context = new ContextWrapper(InstrumentationRegistry.getContext());
+ mStorage = new LockSettingsStorageTestable(context,
+ new File(InstrumentationRegistry.getContext().getFilesDir(), "locksettings"));
+ mRebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(mStorage,
+ new RebootEscrowProviderServerBasedImpl.Injector(mServiceConnection));
+
+ mFakeEncryption = invocation -> {
+ byte[] secret = invocation.getArgument(0);
+ for (int i = 0; i < secret.length; i++) {
+ secret[i] = (byte) (secret[i] ^ 0xf);
+ }
+ return secret;
+ };
+ }
+
+ @Test
+ public void getAndClearRebootEscrowKey_loopback_success() throws Exception {
+ when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())).thenAnswer(mFakeEncryption);
+ when(mServiceConnection.unwrap(any(), anyLong())).thenAnswer(mFakeEncryption);
+
+ assertTrue(mRebootEscrowProvider.hasRebootEscrowSupport());
+ mRebootEscrowProvider.storeRebootEscrowKey(mRebootEscrowKey, mKeyStoreEncryptionKey);
+ assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+
+ RebootEscrowKey ks = mRebootEscrowProvider.getAndClearRebootEscrowKey(
+ mKeyStoreEncryptionKey);
+ assertThat(ks.getKeyBytes(), is(mRebootEscrowKey.getKeyBytes()));
+ assertFalse(mStorage.hasRebootEscrowServerBlob());
+ }
+
+ @Test
+ public void getAndClearRebootEscrowKey_WrongDecryptionMethod_failure() throws Exception {
+ when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())).thenAnswer(mFakeEncryption);
+ when(mServiceConnection.unwrap(any(), anyLong())).thenAnswer(
+ invocation -> {
+ byte[] secret = invocation.getArgument(0);
+ for (int i = 0; i < secret.length; i++) {
+ secret[i] = (byte) (secret[i] ^ 0xe);
+ }
+ return secret;
+ }
+ );
+
+ assertTrue(mRebootEscrowProvider.hasRebootEscrowSupport());
+ mRebootEscrowProvider.storeRebootEscrowKey(mRebootEscrowKey, mKeyStoreEncryptionKey);
+ assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+ // Expect to get wrong key bytes
+ RebootEscrowKey ks = mRebootEscrowProvider.getAndClearRebootEscrowKey(
+ mKeyStoreEncryptionKey);
+ assertNotEquals(ks.getKeyBytes(), mRebootEscrowKey.getKeyBytes());
+ assertFalse(mStorage.hasRebootEscrowServerBlob());
+ }
+
+ @Test
+ public void getAndClearRebootEscrowKey_ServiceConnectionException_failure() throws Exception {
+ when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())).thenAnswer(mFakeEncryption);
+ doThrow(IOException.class).when(mServiceConnection).unwrap(any(), anyLong());
+
+ assertTrue(mRebootEscrowProvider.hasRebootEscrowSupport());
+ mRebootEscrowProvider.storeRebootEscrowKey(mRebootEscrowKey, mKeyStoreEncryptionKey);
+ assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+ // Expect to get null key bytes when the server service fails to unwrap the blob.
+ RebootEscrowKey ks = mRebootEscrowProvider.getAndClearRebootEscrowKey(
+ mKeyStoreEncryptionKey);
+ assertNull(ks);
+ assertFalse(mStorage.hasRebootEscrowServerBlob());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index df19aeb..58ba907 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -1829,11 +1829,11 @@
}
/**
- * Exhaustively test isUidNetworkingBlocked to output the expected results based on external
+ * Exhaustively test checkUidNetworkingBlocked to output the expected results based on external
* conditions.
*/
@Test
- public void testIsUidNetworkingBlocked() {
+ public void testCheckUidNetworkingBlocked() {
final ArrayList<Pair<Boolean, Integer>> expectedBlockedStates = new ArrayList<>();
// Metered network. Data saver on.
@@ -1877,17 +1877,16 @@
private void verifyNetworkBlockedState(boolean metered, boolean backgroundRestricted,
ArrayList<Pair<Boolean, Integer>> expectedBlockedStateForRules) {
- final NetworkPolicyManagerInternal npmi = LocalServices
- .getService(NetworkPolicyManagerInternal.class);
for (Pair<Boolean, Integer> pair : expectedBlockedStateForRules) {
final boolean expectedResult = pair.first;
final int rule = pair.second;
assertEquals(formatBlockedStateError(UID_A, rule, metered, backgroundRestricted),
- expectedResult,
- npmi.isUidNetworkingBlocked(UID_A, rule, metered, backgroundRestricted));
+ expectedResult, mService.checkUidNetworkingBlocked(UID_A, rule,
+ metered, backgroundRestricted));
assertFalse(formatBlockedStateError(SYSTEM_UID, rule, metered, backgroundRestricted),
- npmi.isUidNetworkingBlocked(SYSTEM_UID, rule, metered, backgroundRestricted));
+ mService.checkUidNetworkingBlocked(SYSTEM_UID, rule, metered,
+ backgroundRestricted));
}
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index a118e0d..bbb25cd 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -28,8 +28,8 @@
import static android.app.NotificationManager.IMPORTANCE_MAX;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
-import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.CHANNEL_ID_FIELD_NUMBER;
import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.CHANNEL_NAME_FIELD_NUMBER;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 3c7206f..69e4190 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -33,8 +33,8 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
-import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID;
import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
import static com.android.os.AtomsProto.DNDModeProto.CHANNELS_BYPASSING_FIELD_NUMBER;
import static com.android.os.AtomsProto.DNDModeProto.ENABLED_FIELD_NUMBER;
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index 9d48955..e567208 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -1025,6 +1025,8 @@
writeLocked(fos, stats, version, packagesTokenData);
file.finishWrite(fos);
fos = null;
+ } catch (Exception e) {
+ // Do nothing. Exception has already been handled.
} finally {
// When fos is null (successful write), this will no-op
file.failWrite(fos);
@@ -1032,7 +1034,7 @@
}
private static void writeLocked(OutputStream out, IntervalStats stats, int version,
- PackagesTokenData packagesTokenData) throws RuntimeException {
+ PackagesTokenData packagesTokenData) throws Exception {
switch (version) {
case 1:
case 2:
@@ -1044,6 +1046,7 @@
UsageStatsProto.write(out, stats);
} catch (Exception e) {
Slog.e(TAG, "Unable to write interval stats to proto.", e);
+ throw e;
}
break;
case 5:
@@ -1052,6 +1055,7 @@
UsageStatsProtoV2.write(out, stats);
} catch (Exception e) {
Slog.e(TAG, "Unable to write interval stats to proto.", e);
+ throw e;
}
break;
default:
diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp
index 7cc233b..cac7b82 100644
--- a/startop/view_compiler/Android.bp
+++ b/startop/view_compiler/Android.bp
@@ -84,7 +84,6 @@
static_libs: [
"libviewcompiler",
],
- test_suites: ["general-tests"],
}
cc_binary_host {
diff --git a/startop/view_compiler/TEST_MAPPING b/startop/view_compiler/TEST_MAPPING
index 5f7d3f9..791e471 100644
--- a/startop/view_compiler/TEST_MAPPING
+++ b/startop/view_compiler/TEST_MAPPING
@@ -10,10 +10,6 @@
"include-filter": "android.view.cts.PrecompiledLayoutTest"
}
]
- },
- {
- "name": "view-compiler-tests",
- "host": true
}
]
}
diff --git a/telecomm/java/android/telecom/BluetoothCallQualityReport.java b/telecomm/java/android/telecom/BluetoothCallQualityReport.java
new file mode 100644
index 0000000..10339a8
--- /dev/null
+++ b/telecomm/java/android/telecom/BluetoothCallQualityReport.java
@@ -0,0 +1,256 @@
+/*
+ * 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.telecom;
+
+import android.annotation.ElapsedRealtimeLong;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class represents the quality report that bluetooth framework sends
+ * whenever there's a bad voice quality is detected from their side.
+ * It is sent as part of a call event via {@link Call#sendCallEvent(String, Bundle)}
+ * associated with extra EXTRA_BLUETOOTH_CALL_QUALITY_REPORT.
+ * Note that this report will be sent only during an active voice/voip call.
+ * @hide
+ */
+@SystemApi
+public final class BluetoothCallQualityReport implements Parcelable {
+
+ /**
+ * Event that is sent via {@link Call#sendCallEvent(String, Bundle)} for a call quality report
+ */
+ public static final String EVENT_BLUETOOTH_CALL_QUALITY_REPORT =
+ "android.telecom.event.BLUETOOTH_CALL_QUALITY_REPORT";
+
+ /**
+ * Extra key sent with {@link Call#sendCallEvent(String, Bundle)}
+ */
+ public static final String EXTRA_BLUETOOTH_CALL_QUALITY_REPORT =
+ "android.telecom.extra.BLUETOOTH_CALL_QUALITY_REPORT";
+
+ private final long mSentTimestampMillis;
+ private final boolean mChoppyVoice;
+ private final int mRssiDbm;
+ private final int mSnrDb;
+ private final int mRetransmittedPacketsCount;
+ private final int mPacketsNotReceivedCount;
+ private final int mNegativeAcknowledgementCount;
+
+ /**
+ * @return Time in milliseconds since the epoch. Designates when report was sent.
+ * Used to determine whether this report arrived too late to be useful.
+ */
+ public @ElapsedRealtimeLong long getSentTimestampMillis() {
+ return mSentTimestampMillis;
+ }
+
+ /**
+ * @return {@code true} if bluetooth hardware detects voice is choppy
+ */
+ public boolean isChoppyVoice() {
+ return mChoppyVoice;
+ }
+
+ /**
+ * @return Received Signal Strength Indication (RSSI) value in dBm.
+ * This value shall be an absolute received signal strength value.
+ */
+ public @IntRange(from = -127, to = 20) int getRssiDbm() {
+ return mRssiDbm;
+ }
+
+ /**
+ * @return Signal-to-Noise Ratio (SNR) value in dB.
+ * The controller shall provide the average SNR of all the channels currently used by the link.
+ */
+ public int getSnrDb() {
+ return mSnrDb;
+ }
+
+ /**
+ * @return The number of retransmissions since the last event.
+ * This count shall be reset after it is reported.
+ */
+ public @IntRange(from = 0) int getRetransmittedPacketsCount() {
+ return mRetransmittedPacketsCount;
+ }
+
+ /**
+ * @return No RX count since the last event.
+ * The count increases when no packet is received at the scheduled time slot or the received
+ * packet is corrupted.
+ * This count shall be reset after it is reported.
+ */
+ public @IntRange(from = 0) int getPacketsNotReceivedCount() {
+ return mPacketsNotReceivedCount;
+ }
+
+ /**
+ * @return NAK (Negative Acknowledge) count since the last event.
+ * This count shall be reset after it is reported.
+ */
+ public @IntRange(from = 0) int getNegativeAcknowledgementCount() {
+ return mNegativeAcknowledgementCount;
+ }
+
+ //
+ // Parcelable implementation
+ //
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeLong(mSentTimestampMillis);
+ out.writeBoolean(mChoppyVoice);
+ out.writeInt(mRssiDbm);
+ out.writeInt(mSnrDb);
+ out.writeInt(mRetransmittedPacketsCount);
+ out.writeInt(mPacketsNotReceivedCount);
+ out.writeInt(mNegativeAcknowledgementCount);
+ }
+
+ public static final @android.annotation.NonNull Creator<BluetoothCallQualityReport> CREATOR =
+ new Creator<BluetoothCallQualityReport>() {
+ @Override
+ public BluetoothCallQualityReport createFromParcel(Parcel in) {
+ return new BluetoothCallQualityReport(in);
+ }
+
+ @Override
+ public BluetoothCallQualityReport[] newArray(int size) {
+ return new BluetoothCallQualityReport[size];
+ }
+ };
+
+ /**
+ * Builder class for {@link ConnectionRequest}
+ */
+ public static final class Builder {
+ private long mSentTimestampMillis;
+ private boolean mChoppyVoice;
+ private int mRssiDbm;
+ private int mSnrDb;
+ private int mRetransmittedPacketsCount;
+ private int mPacketsNotReceivedCount;
+ private int mNegativeAcknowledgementCount;
+
+ public Builder() { }
+
+ /**
+ * Set the time when report was sent in milliseconds since the epoch.
+ * @param sentTimestampMillis
+ */
+ public @NonNull Builder setSentTimestampMillis(long sentTimestampMillis) {
+ mSentTimestampMillis = sentTimestampMillis;
+ return this;
+ }
+
+ /**
+ * Set if bluetooth hardware detects voice is choppy
+ * @param choppyVoice
+ */
+ public @NonNull Builder setChoppyVoice(boolean choppyVoice) {
+ mChoppyVoice = choppyVoice;
+ return this;
+ }
+
+ /**
+ * Set Received Signal Strength Indication (RSSI) value in dBm.
+ * @param rssiDbm
+ */
+ public @NonNull Builder setRssiDbm(int rssiDbm) {
+ mRssiDbm = rssiDbm;
+ return this;
+ }
+
+ /**
+ * Set Signal-to-Noise Ratio (SNR) value in dB.
+ * @param snrDb
+ */
+ public @NonNull Builder setSnrDb(int snrDb) {
+ mSnrDb = snrDb;
+ return this;
+ }
+
+ /**
+ * Set The number of retransmissions since the last event.
+ * @param retransmittedPacketsCount
+ */
+ public @NonNull Builder setRetransmittedPacketsCount(
+ int retransmittedPacketsCount) {
+ mRetransmittedPacketsCount = retransmittedPacketsCount;
+ return this;
+ }
+
+ /**
+ * Set No RX count since the last event.
+ * @param packetsNotReceivedCount
+ */
+ public @NonNull Builder setPacketsNotReceivedCount(
+ int packetsNotReceivedCount) {
+ mPacketsNotReceivedCount = packetsNotReceivedCount;
+ return this;
+ }
+
+ /**
+ * Set NAK (Negative Acknowledge) count since the last event.
+ * @param negativeAcknowledgementCount
+ */
+ public @NonNull Builder setNegativeAcknowledgementCount(
+ int negativeAcknowledgementCount) {
+ mNegativeAcknowledgementCount = negativeAcknowledgementCount;
+ return this;
+ }
+
+ /**
+ * Build the {@link BluetoothCallQualityReport}
+ * @return Result of the builder
+ */
+ public @NonNull BluetoothCallQualityReport build() {
+ return new BluetoothCallQualityReport(this);
+ }
+ }
+
+ private BluetoothCallQualityReport(Parcel in) {
+ mSentTimestampMillis = in.readLong();
+ mChoppyVoice = in.readBoolean();
+ mRssiDbm = in.readInt();
+ mSnrDb = in.readInt();
+ mRetransmittedPacketsCount = in.readInt();
+ mPacketsNotReceivedCount = in.readInt();
+ mNegativeAcknowledgementCount = in.readInt();
+ }
+
+ private BluetoothCallQualityReport(Builder builder) {
+ mSentTimestampMillis = builder.mSentTimestampMillis;
+ mChoppyVoice = builder.mChoppyVoice;
+ mRssiDbm = builder.mRssiDbm;
+ mSnrDb = builder.mSnrDb;
+ mRetransmittedPacketsCount = builder.mRetransmittedPacketsCount;
+ mPacketsNotReceivedCount = builder.mPacketsNotReceivedCount;
+ mNegativeAcknowledgementCount = builder.mNegativeAcknowledgementCount;
+ }
+}
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index e55720c..335a102 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -3005,6 +3005,26 @@
public void onCallAudioStateChanged(CallAudioState state) {}
/**
+ * Inform this Connection when it will or will not be tracked by an {@link InCallService} which
+ * can provide an InCall UI.
+ * This is primarily intended for use by Connections reported by self-managed
+ * {@link ConnectionService} which typically maintain their own UI.
+ *
+ * @param isUsingAlternativeUi Indicates whether an InCallService that can provide InCall UI is
+ * currently tracking the self-managed call.
+ */
+ public void onUsingAlternativeUi(boolean isUsingAlternativeUi) {}
+
+ /**
+ * Inform this Conenection when it will or will not be tracked by an non-UI
+ * {@link InCallService}.
+ *
+ * @param isTracked Indicates whether an non-UI InCallService is currently tracking the
+ * self-managed call.
+ */
+ public void onTrackedByNonUiService(boolean isTracked) {}
+
+ /**
* Notifies this Connection of an internal state change. This method is called after the
* state is changed.
*
diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java
index b73ef9b..be5fae4 100644
--- a/telecomm/java/android/telecom/ConnectionRequest.java
+++ b/telecomm/java/android/telecom/ConnectionRequest.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.net.Uri;
@@ -67,7 +68,8 @@
* Sets the participants for the resulting {@link ConnectionRequest}
* @param participants The participants to which the {@link Connection} is to connect.
*/
- public @NonNull Builder setParticipants(@Nullable List<Uri> participants) {
+ public @NonNull Builder setParticipants(
+ @SuppressLint("NullableCollection") @Nullable List<Uri> participants) {
this.mParticipants = participants;
return this;
}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index b1ccb53..a4ecb72 100755
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -137,6 +137,8 @@
private static final String SESSION_HOLD = "CS.h";
private static final String SESSION_UNHOLD = "CS.u";
private static final String SESSION_CALL_AUDIO_SC = "CS.cASC";
+ private static final String SESSION_USING_ALTERNATIVE_UI = "CS.uAU";
+ private static final String SESSION_TRACKED_BY_NON_UI_SERVICE = "CS.tBNUS";
private static final String SESSION_PLAY_DTMF = "CS.pDT";
private static final String SESSION_STOP_DTMF = "CS.sDT";
private static final String SESSION_CONFERENCE = "CS.c";
@@ -200,6 +202,9 @@
private static final int MSG_ADD_PARTICIPANT = 39;
private static final int MSG_EXPLICIT_CALL_TRANSFER = 40;
private static final int MSG_EXPLICIT_CALL_TRANSFER_CONSULTATIVE = 41;
+ private static final int MSG_ON_CALL_FILTERING_COMPLETED = 42;
+ private static final int MSG_ON_USING_ALTERNATIVE_UI = 43;
+ private static final int MSG_ON_TRACKED_BY_NON_UI_SERVICE = 44;
private static Connection sNullConnection;
@@ -584,6 +589,36 @@
}
@Override
+ public void onUsingAlternativeUi(String callId, boolean usingAlternativeUiShowing,
+ Session.Info sessionInfo) {
+ Log.startSession(sessionInfo, SESSION_USING_ALTERNATIVE_UI);
+ try {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = usingAlternativeUiShowing;
+ args.arg3 = Log.createSubsession();
+ mHandler.obtainMessage(MSG_ON_USING_ALTERNATIVE_UI, args).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
+ public void onTrackedByNonUiService(String callId, boolean isTracked,
+ Session.Info sessionInfo) {
+ Log.startSession(sessionInfo, SESSION_TRACKED_BY_NON_UI_SERVICE);
+ try {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = isTracked;
+ args.arg3 = Log.createSubsession();
+ mHandler.obtainMessage(MSG_ON_TRACKED_BY_NON_UI_SERVICE, args).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
public void playDtmfTone(String callId, char digit, Session.Info sessionInfo) {
Log.startSession(sessionInfo, SESSION_PLAY_DTMF);
try {
@@ -1226,6 +1261,34 @@
}
break;
}
+ case MSG_ON_USING_ALTERNATIVE_UI: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ Log.continueSession((Session) args.arg3,
+ SESSION_HANDLER + SESSION_USING_ALTERNATIVE_UI);
+ try {
+ String callId = (String) args.arg1;
+ boolean isUsingAlternativeUi = (boolean) args.arg2;
+ onUsingAlternativeUi(callId, isUsingAlternativeUi);
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
+ case MSG_ON_TRACKED_BY_NON_UI_SERVICE: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ Log.continueSession((Session) args.arg3,
+ SESSION_HANDLER + SESSION_TRACKED_BY_NON_UI_SERVICE);
+ try {
+ String callId = (String) args.arg1;
+ boolean isTracked = (boolean) args.arg2;
+ onTrackedByNonUiService(callId, isTracked);
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
case MSG_PLAY_DTMF_TONE: {
SomeArgs args = (SomeArgs) msg.obj;
try {
@@ -1928,10 +1991,12 @@
request.getExtras().getBoolean(TelecomManager.EXTRA_IS_HANDOVER, false);
boolean isHandover = request.getExtras() != null && request.getExtras().getBoolean(
TelecomManager.EXTRA_IS_HANDOVER_CONNECTION, false);
- Log.d(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, " +
- "isIncoming: %b, isUnknown: %b, isLegacyHandover: %b, isHandover: %b",
- callManagerAccount, callId, request, isIncoming, isUnknown, isLegacyHandover,
- isHandover);
+ boolean addSelfManaged = request.getExtras() != null && request.getExtras().getBoolean(
+ PhoneAccount.EXTRA_ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE, false);
+ Log.i(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, "
+ + "isIncoming: %b, isUnknown: %b, isLegacyHandover: %b, isHandover: %b, "
+ + " addSelfManaged: %b", callManagerAccount, callId, request, isIncoming,
+ isUnknown, isLegacyHandover, isHandover, addSelfManaged);
Connection connection = null;
if (isHandover) {
@@ -2206,6 +2271,22 @@
}
}
+ private void onUsingAlternativeUi(String callId, boolean isUsingAlternativeUi) {
+ Log.i(this, "onUsingAlternativeUi %s %s", callId, isUsingAlternativeUi);
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "onUsingAlternativeUi")
+ .onUsingAlternativeUi(isUsingAlternativeUi);
+ }
+ }
+
+ private void onTrackedByNonUiService(String callId, boolean isTracked) {
+ Log.i(this, "onTrackedByNonUiService %s %s", callId, isTracked);
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "onTrackedByNonUiService")
+ .onTrackedByNonUiService(isTracked);
+ }
+ }
+
private void playDtmfTone(String callId, char digit) {
Log.i(this, "playDtmfTone %s %c", callId, digit);
if (mConnectionById.containsKey(callId)) {
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index 835ecaa..579b33e 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -188,6 +188,15 @@
"android.telecom.extra.SKIP_CALL_FILTERING";
/**
+ * Boolean {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()}) which
+ * indicates whether a Self-managed {@link PhoneAccount} want to expose its calls to all
+ * {@link InCallService} which declares the metadata
+ * {@link TelecomManager#METADATA_INCLUDE_SELF_MANAGED_CALLS}.
+ */
+ public static final String EXTRA_ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE =
+ "android.telecom.extra.ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE";
+
+ /**
* Flag indicating that this {@code PhoneAccount} can act as a connection manager for
* other connections. The {@link ConnectionService} associated with this {@code PhoneAccount}
* will be allowed to manage phone calls including using its own proprietary phone-call
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index fb54179..d555547 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -136,4 +136,9 @@
int error, in Session.Info sessionInfo);
void handoverComplete(String callId, in Session.Info sessionInfo);
+
+ void onUsingAlternativeUi(String callId, boolean isUsingAlternativeUi,
+ in Session.Info sessionInfo);
+
+ void onTrackedByNonUiService(String callId, boolean isTracked, in Session.Info sessionInfo);
}
diff --git a/telephony/java/android/telephony/CarrierBandwidth.java b/telephony/java/android/telephony/CarrierBandwidth.java
index 17747a3..b153fef 100644
--- a/telephony/java/android/telephony/CarrierBandwidth.java
+++ b/telephony/java/android/telephony/CarrierBandwidth.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -102,7 +103,7 @@
/**
* Retrieves the upstream bandwidth for the primary network in Kbps. This always only refers to
* the estimated first hop transport bandwidth.
- * This will be INVALID if the network is not connected
+ * This will be {@link #INVALID} if the network is not connected
*
* @return The estimated first hop upstream (device to network) bandwidth.
*/
@@ -113,7 +114,7 @@
/**
* Retrieves the downstream bandwidth for the primary network in Kbps. This always only refers
* to the estimated first hop transport bandwidth.
- * This will be INVALID if the network is not connected
+ * This will be {@link #INVALID} if the network is not connected
*
* @return The estimated first hop downstream (network to device) bandwidth.
*/
@@ -124,10 +125,19 @@
/**
* Retrieves the upstream bandwidth for the secondary network in Kbps. This always only refers
* to the estimated first hop transport bandwidth.
- * This will be INVALID if the network is not connected
+ * <p/>
+ * This will be {@link #INVALID} if either are the case:
+ * <ol>
+ * <li>The network is not connected</li>
+ * <li>The device does not support
+ * {@link android.telephony.TelephonyManager#CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE}.</li>
+ * </ol>
*
* @return The estimated first hop upstream (device to network) bandwidth.
*/
+ @RequiresFeature(
+ enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+ value = TelephonyManager.CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE)
public int getSecondaryDownlinkCapacityKbps() {
return mSecondaryDownlinkCapacityKbps;
}
@@ -135,10 +145,18 @@
/**
* Retrieves the downstream bandwidth for the secondary network in Kbps. This always only
* refers to the estimated first hop transport bandwidth.
- * This will be INVALID if the network is not connected
- *
+ * <p/>
+ * This will be {@link #INVALID} if either are the case:
+ * <ol>
+ * <li>The network is not connected</li>
+ * <li>The device does not support
+ * {@link android.telephony.TelephonyManager#CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE}.</li>
+ * </ol>
* @return The estimated first hop downstream (network to device) bandwidth.
*/
+ @RequiresFeature(
+ enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+ value = TelephonyManager.CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE)
public int getSecondaryUplinkCapacityKbps() {
return mSecondaryUplinkCapacityKbps;
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 3a9896a..b81c4f2 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3937,6 +3937,20 @@
public static final String KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL =
KEY_PREFIX + "enable_presence_group_subscribe_bool";
+ /**
+ * An integer key associated with the period of time in seconds the non-rcs capability
+ * information of each contact is cached on the device.
+ * <p>
+ * The rcs capability cache expiration sec is managed by
+ * {@code android.telephony.ims.ProvisioningManager} but non-rcs capability is managed by
+ * {@link CarrierConfigManager} since non-rcs capability will be provided via ACS or carrier
+ * config.
+ * <p>
+ * The default value is 2592000 secs (30 days), see RCC.07 Annex A.1.9.
+ */
+ public static final String KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT =
+ KEY_PREFIX + "non_rcs_capabilities_cache_expiration_sec_int";
+
private Ims() {}
private static PersistableBundle getDefaults() {
@@ -3947,6 +3961,7 @@
defaults.putBoolean(KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL, false);
defaults.putBoolean(KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL, false);
defaults.putBoolean(KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL, true);
+ defaults.putInt(KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT, 30 * 24 * 60 * 60);
return defaults;
}
}
@@ -4124,6 +4139,13 @@
*/
public static final String KEY_USE_ACS_FOR_RCS_BOOL = "use_acs_for_rcs_bool";
+ /**
+ * Indicates temporarily unmetered mobile data is supported by the carrier.
+ * @hide
+ */
+ public static final String KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL =
+ "network_temp_not_metered_supported_bool";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -4673,6 +4695,7 @@
sDefaults.putString(KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING, "");
sDefaults.putBoolean(KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED, false);
sDefaults.putBoolean(KEY_USE_ACS_FOR_RCS_BOOL, false);
+ sDefaults.putBoolean(KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL, false);
sDefaults.putInt(KEY_DEFAULT_RTT_MODE_INT, 0);
}
diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java
index d502da9..99a77ae5 100644
--- a/telephony/java/android/telephony/DataFailCause.java
+++ b/telephony/java/android/telephony/DataFailCause.java
@@ -915,6 +915,8 @@
public static final int IPV6_PREFIX_UNAVAILABLE = 0x8CA;
/** System preference change back to SRAT during handoff */
public static final int HANDOFF_PREFERENCE_CHANGED = 0x8CB;
+ /** Data call fail due to the slice not being allowed for the data call. */
+ public static final int SLICE_REJECTED = 0x8CC;
//IKE error notifications message as specified in 3GPP TS 24.302 (Section 8.1.2.2).
@@ -985,7 +987,7 @@
* the authentication failed.
*/
public static final int IWLAN_IKEV2_AUTH_FAILURE = 0x4001;
- /** IKE message timeout, tunnel setup failed due to no response from EPDG */
+ /** IKE message timeout, tunnel setup failed due to no response from EPDG */
public static final int IWLAN_IKEV2_MSG_TIMEOUT = 0x4002;
/** IKE Certification validation failure */
public static final int IWLAN_IKEV2_CERT_INVALID = 0x4003;
@@ -1419,6 +1421,7 @@
sFailCauseMap.put(VSNCP_RECONNECT_NOT_ALLOWED, "VSNCP_RECONNECT_NOT_ALLOWED");
sFailCauseMap.put(IPV6_PREFIX_UNAVAILABLE, "IPV6_PREFIX_UNAVAILABLE");
sFailCauseMap.put(HANDOFF_PREFERENCE_CHANGED, "HANDOFF_PREFERENCE_CHANGED");
+ sFailCauseMap.put(SLICE_REJECTED, "SLICE_REJECTED");
sFailCauseMap.put(IWLAN_PDN_CONNECTION_REJECTION, "IWLAN_PDN_CONNECTION_REJECTION");
sFailCauseMap.put(IWLAN_MAX_CONNECTION_REACHED, "IWLAN_MAX_CONNECTION_REACHED");
sFailCauseMap.put(IWLAN_SEMANTIC_ERROR_IN_THE_TFT_OPERATION,
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index 8507d85..706e3cb 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -344,6 +344,7 @@
// TODO: Instead of doing this, we should create a formal way for cloning cell identity.
// Cell identity is not an immutable object so we have to deep copy it.
mCellIdentity = CellIdentity.CREATOR.createFromParcel(p);
+ p.recycle();
}
if (nri.mVoiceSpecificInfo != null) {
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 904232b..4e481b3 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2589,14 +2589,45 @@
* requested state until explicitly cleared, or the next reboot,
* whichever happens first.
* @throws SecurityException if the caller doesn't meet the requirements
- * outlined above.
+ * outlined above.
*/
public void setSubscriptionOverrideUnmetered(int subId, boolean overrideUnmetered,
@DurationMillisLong long timeoutMillis) {
+ setSubscriptionOverrideUnmetered(subId, overrideUnmetered,
+ TelephonyManager.getAllNetworkTypes(), timeoutMillis);
+ }
+ /**
+ * Temporarily override the billing relationship plan between a carrier and
+ * a specific subscriber to be considered unmetered. This will be reflected
+ * to apps via {@link NetworkCapabilities#NET_CAPABILITY_NOT_METERED}.
+ * <p>
+ * This method is only accessible to the following narrow set of apps:
+ * <ul>
+ * <li>The carrier app for this subscriberId, as determined by
+ * {@link TelephonyManager#hasCarrierPrivileges()}.
+ * <li>The carrier app explicitly delegated access through
+ * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}.
+ * </ul>
+ *
+ * @param subId the subscriber this override applies to.
+ * @param overrideUnmetered set if the billing relationship should be
+ * considered unmetered.
+ * @param networkTypes the network types this override applies to.
+ * {@see TelephonyManager#getAllNetworkTypes()}
+ * @param timeoutMillis the timeout after which the requested override will
+ * be automatically cleared, or {@code 0} to leave in the
+ * requested state until explicitly cleared, or the next reboot,
+ * whichever happens first.
+ * @throws SecurityException if the caller doesn't meet the requirements
+ * outlined above.
+ */
+ public void setSubscriptionOverrideUnmetered(int subId, boolean overrideUnmetered,
+ @NonNull @Annotation.NetworkType int[] networkTypes,
+ @DurationMillisLong long timeoutMillis) {
final int overrideValue = overrideUnmetered ? SUBSCRIPTION_OVERRIDE_UNMETERED : 0;
getNetworkPolicyManager().setSubscriptionOverride(subId, SUBSCRIPTION_OVERRIDE_UNMETERED,
- overrideValue, timeoutMillis, mContext.getOpPackageName());
+ overrideValue, networkTypes, timeoutMillis, mContext.getOpPackageName());
}
/**
@@ -2621,13 +2652,46 @@
* requested state until explicitly cleared, or the next reboot,
* whichever happens first.
* @throws SecurityException if the caller doesn't meet the requirements
- * outlined above.
+ * outlined above.
*/
public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested,
@DurationMillisLong long timeoutMillis) {
+ setSubscriptionOverrideCongested(subId, overrideCongested,
+ TelephonyManager.getAllNetworkTypes(), timeoutMillis);
+ }
+
+ /**
+ * Temporarily override the billing relationship plan between a carrier and
+ * a specific subscriber to be considered congested. This will cause the
+ * device to delay certain network requests when possible, such as developer
+ * jobs that are willing to run in a flexible time window.
+ * <p>
+ * This method is only accessible to the following narrow set of apps:
+ * <ul>
+ * <li>The carrier app for this subscriberId, as determined by
+ * {@link TelephonyManager#hasCarrierPrivileges()}.
+ * <li>The carrier app explicitly delegated access through
+ * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}.
+ * </ul>
+ *
+ * @param subId the subscriber this override applies to.
+ * @param overrideCongested set if the subscription should be considered
+ * congested.
+ * @param networkTypes the network types this override applies to.
+ * {@see TelephonyManager#getAllNetworkTypes()}
+ * @param timeoutMillis the timeout after which the requested override will
+ * be automatically cleared, or {@code 0} to leave in the
+ * requested state until explicitly cleared, or the next reboot,
+ * whichever happens first.
+ * @throws SecurityException if the caller doesn't meet the requirements
+ * outlined above.
+ */
+ public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested,
+ @NonNull @Annotation.NetworkType int[] networkTypes,
+ @DurationMillisLong long timeoutMillis) {
final int overrideValue = overrideCongested ? SUBSCRIPTION_OVERRIDE_CONGESTED : 0;
getNetworkPolicyManager().setSubscriptionOverride(subId, SUBSCRIPTION_OVERRIDE_CONGESTED,
- overrideValue, timeoutMillis, mContext.getOpPackageName());
+ overrideValue, networkTypes, timeoutMillis, mContext.getOpPackageName());
}
/**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 646744d..e8ace34 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -31,6 +31,7 @@
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.StringDef;
import android.annotation.SuppressAutoDoc;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
@@ -14344,9 +14345,22 @@
return Collections.emptyList();
}
+ /**
+ * Indicates whether {@link CarrierBandwidth#getSecondaryDownlinkCapacityKbps()} and
+ * {@link CarrierBandwidth#getSecondaryUplinkCapacityKbps()} are visible. See comments
+ * on respective methods for more information.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE =
+ "CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE";
+
/** @hide */
- @IntDef(prefix = {"RADIO_INTERFACE_CAPABILITY_"},
- value = {})
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef(prefix = "CAPABILITY_", value = {
+ CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE,
+ })
public @interface RadioInterfaceCapability {}
/**
@@ -14359,6 +14373,7 @@
*
* @hide
*/
+ @SystemApi
public boolean isRadioInterfaceCapabilitySupported(
@NonNull @RadioInterfaceCapability String capability) {
try {
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index 8348502..46ec4a3 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -136,6 +136,7 @@
private final int mPduSessionId;
private final Qos mDefaultQos;
private final List<QosSession> mQosSessions;
+ private final SliceInfo mSliceInfo;
/**
* @param cause Data call fail cause. {@link DataFailCause#NONE} indicates no error.
@@ -186,6 +187,7 @@
mPduSessionId = PDU_SESSION_ID_NOT_SET;
mDefaultQos = null;
mQosSessions = new ArrayList<>();
+ mSliceInfo = null;
}
private DataCallResponse(@DataFailureCause int cause, long suggestedRetryTime, int id,
@@ -194,7 +196,8 @@
@Nullable List<InetAddress> dnsAddresses, @Nullable List<InetAddress> gatewayAddresses,
@Nullable List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6,
@HandoverFailureMode int handoverFailureMode, int pduSessionId,
- @Nullable Qos defaultQos, @Nullable List<QosSession> qosSessions) {
+ @Nullable Qos defaultQos, @Nullable List<QosSession> qosSessions,
+ @Nullable SliceInfo sliceInfo) {
mCause = cause;
mSuggestedRetryTime = suggestedRetryTime;
mId = id;
@@ -216,6 +219,7 @@
mPduSessionId = pduSessionId;
mDefaultQos = defaultQos;
mQosSessions = qosSessions;
+ mSliceInfo = sliceInfo;
}
/** @hide */
@@ -243,6 +247,7 @@
mDefaultQos = source.readParcelable(Qos.class.getClassLoader());
mQosSessions = new ArrayList<>();
source.readList(mQosSessions, QosSession.class.getClassLoader());
+ mSliceInfo = source.readParcelable(SliceInfo.class.getClassLoader());
}
/**
@@ -368,7 +373,7 @@
}
/**
- * @return default QOS of the data call received from the network
+ * @return default QOS of the data connection received from the network
*
* @hide
*/
@@ -379,16 +384,24 @@
}
/**
- * @return All the dedicated bearer QOS sessions of the data call received from the network
+ * @return All the dedicated bearer QOS sessions of the data connection received from the
+ * network.
*
* @hide
*/
-
@NonNull
public List<QosSession> getQosSessions() {
return mQosSessions;
}
+ /**
+ * @return The slice info related to this data connection.
+ */
+ @Nullable
+ public SliceInfo getSliceInfo() {
+ return mSliceInfo;
+ }
+
@NonNull
@Override
public String toString() {
@@ -411,6 +424,7 @@
.append(" pduSessionId=").append(getPduSessionId())
.append(" defaultQos=").append(mDefaultQos)
.append(" qosSessions=").append(mQosSessions)
+ .append(" sliceInfo=").append(mSliceInfo)
.append("}");
return sb.toString();
}
@@ -454,7 +468,8 @@
&& mHandoverFailureMode == other.mHandoverFailureMode
&& mPduSessionId == other.mPduSessionId
&& isQosSame
- && isQosSessionsSame;
+ && isQosSessionsSame
+ && Objects.equals(mSliceInfo, other.mSliceInfo);
}
@Override
@@ -462,7 +477,7 @@
return Objects.hash(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType,
mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses,
mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId, mDefaultQos,
- mQosSessions);
+ mQosSessions, mSliceInfo);
}
@Override
@@ -493,6 +508,7 @@
dest.writeParcelable((NrQos)mDefaultQos, flags);
}
dest.writeList(mQosSessions);
+ dest.writeParcelable(mSliceInfo, flags);
}
public static final @android.annotation.NonNull Parcelable.Creator<DataCallResponse> CREATOR =
@@ -576,6 +592,8 @@
private List<QosSession> mQosSessions = new ArrayList<>();
+ private SliceInfo mSliceInfo;
+
/**
* Default constructor for Builder.
*/
@@ -799,6 +817,21 @@
}
/**
+ * The Slice used for this data connection.
+ * <p/>
+ * If a handover occurs from EPDG to 5G,
+ * this is the {@link SliceInfo} used in {@link DataService#setupDataCall}.
+ *
+ * @param sliceInfo the slice info for the data call
+ *
+ * @return The same instance of the builder.
+ */
+ public @NonNull Builder setSliceInfo(@Nullable SliceInfo sliceInfo) {
+ mSliceInfo = sliceInfo;
+ return this;
+ }
+
+ /**
* Build the DataCallResponse.
*
* @return the DataCallResponse object.
@@ -807,7 +840,7 @@
return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus,
mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses,
mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId,
- mDefaultQos, mQosSessions);
+ mDefaultQos, mQosSessions, mSliceInfo);
}
}
}
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index 2ec9651..03c2ef9 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -194,13 +194,19 @@
* The standard range of values are 1-15 while 0 means no pdu session id
* was attached to this call. Reference: 3GPP TS 24.007 section
* 11.2.3.1b.
+ * @param sliceInfo used within the data connection when a handover occurs from EPDG to 5G.
+ * The value is null unless the access network is
+ * {@link android.telephony.AccessNetworkConstants.AccessNetworkType#NGRAN} and a
+ * handover is occurring from EPDG to 5G. If the slice passed is rejected, then
+ * {@link DataCallResponse#getCause()} is
+ * {@link android.telephony.DataFailCause#SLICE_REJECTED}.
* @param callback The result callback for this request.
*/
public void setupDataCall(int accessNetworkType, @NonNull DataProfile dataProfile,
boolean isRoaming, boolean allowRoaming,
@SetupDataReason int reason,
@Nullable LinkProperties linkProperties,
- @IntRange(from = 0, to = 15) int pduSessionId,
+ @IntRange(from = 0, to = 15) int pduSessionId, @Nullable SliceInfo sliceInfo,
@NonNull DataServiceCallback callback) {
/* Call the old version since the new version isn't supported */
setupDataCall(accessNetworkType, dataProfile, isRoaming, allowRoaming, reason,
@@ -392,10 +398,11 @@
public final int reason;
public final LinkProperties linkProperties;
public final int pduSessionId;
+ public final SliceInfo sliceInfo;
public final IDataServiceCallback callback;
SetupDataCallRequest(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
boolean allowRoaming, int reason, LinkProperties linkProperties,
- int pduSessionId, IDataServiceCallback callback) {
+ int pduSessionId, SliceInfo sliceInfo, IDataServiceCallback callback) {
this.accessNetworkType = accessNetworkType;
this.dataProfile = dataProfile;
this.isRoaming = isRoaming;
@@ -403,6 +410,7 @@
this.linkProperties = linkProperties;
this.reason = reason;
this.pduSessionId = pduSessionId;
+ this.sliceInfo = sliceInfo;
this.callback = callback;
}
}
@@ -513,6 +521,7 @@
setupDataCallRequest.dataProfile, setupDataCallRequest.isRoaming,
setupDataCallRequest.allowRoaming, setupDataCallRequest.reason,
setupDataCallRequest.linkProperties, setupDataCallRequest.pduSessionId,
+ setupDataCallRequest.sliceInfo,
(setupDataCallRequest.callback != null)
? new DataServiceCallback(setupDataCallRequest.callback)
: null);
@@ -676,10 +685,12 @@
@Override
public void setupDataCall(int slotIndex, int accessNetworkType, DataProfile dataProfile,
boolean isRoaming, boolean allowRoaming, int reason,
- LinkProperties linkProperties, int pduSessionId, IDataServiceCallback callback) {
+ LinkProperties linkProperties, int pduSessionId, SliceInfo sliceInfo,
+ IDataServiceCallback callback) {
mHandler.obtainMessage(DATA_SERVICE_REQUEST_SETUP_DATA_CALL, slotIndex, 0,
new SetupDataCallRequest(accessNetworkType, dataProfile, isRoaming,
- allowRoaming, reason, linkProperties, pduSessionId, callback))
+ allowRoaming, reason, linkProperties, pduSessionId, sliceInfo,
+ callback))
.sendToTarget();
}
diff --git a/telephony/java/android/telephony/data/IDataService.aidl b/telephony/java/android/telephony/data/IDataService.aidl
index 3f1f033..e0b9a1a 100644
--- a/telephony/java/android/telephony/data/IDataService.aidl
+++ b/telephony/java/android/telephony/data/IDataService.aidl
@@ -19,6 +19,7 @@
import android.net.LinkProperties;
import android.telephony.data.DataProfile;
import android.telephony.data.IDataServiceCallback;
+import android.telephony.data.SliceInfo;
/**
* {@hide}
@@ -29,7 +30,7 @@
void removeDataServiceProvider(int slotId);
void setupDataCall(int slotId, int accessNetwork, in DataProfile dataProfile, boolean isRoaming,
boolean allowRoaming, int reason, in LinkProperties linkProperties,
- int pduSessionId, IDataServiceCallback callback);
+ int pduSessionId, in SliceInfo sliceInfo, IDataServiceCallback callback);
void deactivateDataCall(int slotId, int cid, int reason, IDataServiceCallback callback);
void setInitialAttachApn(int slotId, in DataProfile dataProfile, boolean isRoaming,
IDataServiceCallback callback);
diff --git a/core/java/com/android/internal/net/VpnInfo.aidl b/telephony/java/android/telephony/data/SliceInfo.aidl
similarity index 82%
copy from core/java/com/android/internal/net/VpnInfo.aidl
copy to telephony/java/android/telephony/data/SliceInfo.aidl
index 6fc97be..286ea5e 100644
--- a/core/java/com/android/internal/net/VpnInfo.aidl
+++ b/telephony/java/android/telephony/data/SliceInfo.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -14,6 +14,7 @@
* limitations under the License.
*/
-package com.android.internal.net;
+/** @hide */
+package android.telephony.data;
-parcelable VpnInfo;
+parcelable SliceInfo;
diff --git a/telephony/java/android/telephony/data/SliceInfo.java b/telephony/java/android/telephony/data/SliceInfo.java
new file mode 100644
index 0000000..51857a7
--- /dev/null
+++ b/telephony/java/android/telephony/data/SliceInfo.java
@@ -0,0 +1,342 @@
+/*
+ * 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.telephony.data;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Represents a S-NSSAI as defined in 3GPP TS 24.501.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SliceInfo implements Parcelable {
+ /**
+ * When set on a Slice Differentiator, this value indicates that there is no corresponding
+ * Slice.
+ */
+ public static final int SLICE_DIFFERENTIATOR_NO_SLICE = -1;
+
+ /**
+ * Indicates that the service type is not present.
+ */
+ public static final int SLICE_SERVICE_TYPE_NONE = 0;
+
+ /**
+ * Slice suitable for the handling of 5G enhanced Mobile Broadband.
+ */
+ public static final int SLICE_SERVICE_TYPE_EMBB = 1;
+
+ /**
+ * Slice suitable for the handling of ultra-reliable low latency communications.
+ */
+ public static final int SLICE_SERVICE_TYPE_URLLC = 2;
+
+ /**
+ * Slice suitable for the handling of massive IoT.
+ */
+ public static final int SLICE_SERVICE_TYPE_MIOT = 3;
+
+ /**
+ * The min acceptable value for a Slice Differentiator
+ */
+ @SuppressLint("MinMaxConstant")
+ public static final int MIN_SLICE_DIFFERENTIATOR = -1;
+
+ /**
+ * The max acceptable value for a Slice Differentiator
+ */
+ @SuppressLint("MinMaxConstant")
+ public static final int MAX_SLICE_DIFFERENTIATOR = 0xFFFFFE;
+
+ /** @hide */
+ @IntDef(prefix = { "SLICE_SERVICE_TYPE_" }, value = {
+ SLICE_SERVICE_TYPE_NONE,
+ SLICE_SERVICE_TYPE_EMBB,
+ SLICE_SERVICE_TYPE_URLLC,
+ SLICE_SERVICE_TYPE_MIOT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SliceServiceType {}
+
+
+ @SliceServiceType
+ private final int mSliceServiceType;
+ @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
+ private final int mSliceDifferentiator;
+ @SliceServiceType
+ private final int mMappedHplmnSliceServiceType;
+ @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
+ private final int mMappedHplmnSliceDifferentiator;
+
+ private SliceInfo(@SliceServiceType int sliceServiceType,
+ int sliceDifferentiator, int mappedHplmnSliceServiceType,
+ int mappedHplmnSliceDifferentiator) {
+ mSliceServiceType = sliceServiceType;
+ mSliceDifferentiator = sliceDifferentiator;
+ mMappedHplmnSliceDifferentiator = mappedHplmnSliceDifferentiator;
+ mMappedHplmnSliceServiceType = mappedHplmnSliceServiceType;
+ }
+
+ /**
+ * The type of service provided by the slice.
+ * <p/>
+ * see: 3GPP TS 24.501 Section 9.11.2.8.
+ */
+ @SliceServiceType
+ public int getSliceServiceType() {
+ return mSliceServiceType;
+ }
+
+ /**
+ * Identifies the slice from others with the same Slice Service Type.
+ * <p/>
+ * Returns {@link #SLICE_DIFFERENTIATOR_NO_SLICE} if {@link #getSliceServiceType} returns
+ * {@link #SLICE_SERVICE_TYPE_NONE}.
+ * <p/>
+ * see: 3GPP TS 24.501 Section 9.11.2.8.
+ */
+ @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
+ public int getSliceDifferentiator() {
+ return mSliceDifferentiator;
+ }
+
+ /**
+ * Corresponds to a Slice Info (S-NSSAI) of the HPLMN.
+ * <p/>
+ * see: 3GPP TS 24.501 Section 9.11.2.8.
+ */
+ @SliceServiceType
+ public int getMappedHplmnSliceServiceType() {
+ return mMappedHplmnSliceServiceType;
+ }
+
+ /**
+ * This Slice Differentiator corresponds to a {@link SliceInfo} (S-NSSAI) of the HPLMN;
+ * {@link #getSliceDifferentiator()} is mapped to this value.
+ * <p/>
+ * Returns {@link #SLICE_DIFFERENTIATOR_NO_SLICE} if either of the following are true:
+ * <ul>
+ * <li>{@link #getSliceDifferentiator()} returns {@link #SLICE_DIFFERENTIATOR_NO_SLICE}</li>
+ * <li>{@link #getMappedHplmnSliceServiceType()} returns {@link #SLICE_SERVICE_TYPE_NONE}</li>
+ * </ul>
+ * <p/>
+ * see: 3GPP TS 24.501 Section 9.11.2.8.
+ */
+ @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
+ public int getMappedHplmnSliceDifferentiator() {
+ return mMappedHplmnSliceDifferentiator;
+ }
+
+ private SliceInfo(@NonNull Parcel in) {
+ mSliceServiceType = in.readInt();
+ mSliceDifferentiator = in.readInt();
+ mMappedHplmnSliceServiceType = in.readInt();
+ mMappedHplmnSliceDifferentiator = in.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mSliceServiceType);
+ dest.writeInt(mSliceDifferentiator);
+ dest.writeInt(mMappedHplmnSliceServiceType);
+ dest.writeInt(mMappedHplmnSliceDifferentiator);
+ }
+
+ public static final @android.annotation.NonNull Parcelable.Creator<SliceInfo> CREATOR =
+ new Parcelable.Creator<SliceInfo>() {
+ @Override
+ @NonNull
+ public SliceInfo createFromParcel(@NonNull Parcel source) {
+ return new SliceInfo(source);
+ }
+
+ @Override
+ @NonNull
+ public SliceInfo[] newArray(int size) {
+ return new SliceInfo[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return "SliceInfo{"
+ + "mSliceServiceType=" + sliceServiceTypeToString(mSliceServiceType)
+ + ", mSliceDifferentiator=" + mSliceDifferentiator
+ + ", mMappedHplmnSliceServiceType="
+ + sliceServiceTypeToString(mMappedHplmnSliceServiceType)
+ + ", mMappedHplmnSliceDifferentiator=" + mMappedHplmnSliceDifferentiator
+ + '}';
+ }
+
+ private static String sliceServiceTypeToString(@SliceServiceType int sliceServiceType) {
+ switch(sliceServiceType) {
+ case SLICE_SERVICE_TYPE_NONE:
+ return "NONE";
+ case SLICE_SERVICE_TYPE_EMBB:
+ return "EMBB";
+ case SLICE_SERVICE_TYPE_URLLC:
+ return "URLLC";
+ case SLICE_SERVICE_TYPE_MIOT:
+ return "MIOT";
+ default:
+ return Integer.toString(sliceServiceType);
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ SliceInfo sliceInfo = (SliceInfo) o;
+ return mSliceServiceType == sliceInfo.mSliceServiceType
+ && mSliceDifferentiator == sliceInfo.mSliceDifferentiator
+ && mMappedHplmnSliceServiceType == sliceInfo.mMappedHplmnSliceServiceType
+ && mMappedHplmnSliceDifferentiator == sliceInfo.mMappedHplmnSliceDifferentiator;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mSliceServiceType, mSliceDifferentiator, mMappedHplmnSliceServiceType,
+ mMappedHplmnSliceDifferentiator);
+ }
+
+ /**
+ * Provides a convenient way to set the fields of a {@link SliceInfo} when creating a
+ * new instance.
+ *
+ * <p>The example below shows how you might create a new {@code SliceInfo}:
+ *
+ * <pre><code>
+ *
+ * SliceInfo response = new SliceInfo.Builder()
+ * .setSliceServiceType(SLICE_SERVICE_TYPE_URLLC)
+ * .build();
+ * </code></pre>
+ */
+ public static final class Builder {
+ @SliceServiceType
+ private int mSliceServiceType = SLICE_SERVICE_TYPE_NONE;
+ @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
+ private int mSliceDifferentiator = SLICE_DIFFERENTIATOR_NO_SLICE;
+ @SliceServiceType
+ private int mMappedHplmnSliceServiceType = SLICE_SERVICE_TYPE_NONE;
+ @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
+ private int mMappedHplmnSliceDifferentiator = SLICE_DIFFERENTIATOR_NO_SLICE;
+
+ /**
+ * Default constructor for Builder.
+ */
+ public Builder() {
+ }
+
+ /**
+ * Set the Slice Service Type.
+ *
+ * @return The same instance of the builder.
+ */
+ @NonNull
+ public Builder setSliceServiceType(@SliceServiceType int mSliceServiceType) {
+ this.mSliceServiceType = mSliceServiceType;
+ return this;
+ }
+
+ /**
+ * Set the Slice Differentiator.
+ * <p/>
+ * A value of {@link #SLICE_DIFFERENTIATOR_NO_SLICE} indicates that there is no
+ * corresponding Slice.
+ *
+ * @throws IllegalArgumentException if the parameter is not between
+ * {@link #MIN_SLICE_DIFFERENTIATOR} and {@link #MAX_SLICE_DIFFERENTIATOR}.
+ *
+ * @return The same instance of the builder.
+ */
+ @NonNull
+ public Builder setSliceDifferentiator(
+ @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
+ int sliceDifferentiator) {
+ if (sliceDifferentiator < MIN_SLICE_DIFFERENTIATOR
+ || sliceDifferentiator > MAX_SLICE_DIFFERENTIATOR) {
+ throw new IllegalArgumentException("The slice diffentiator value is out of range");
+ }
+ this.mSliceDifferentiator = sliceDifferentiator;
+ return this;
+ }
+
+ /**
+ * Set the HPLMN Slice Service Type.
+ *
+ * @return The same instance of the builder.
+ */
+ @NonNull
+ public Builder setMappedHplmnSliceServiceType(
+ @SliceServiceType int mappedHplmnSliceServiceType) {
+ this.mMappedHplmnSliceServiceType = mappedHplmnSliceServiceType;
+ return this;
+ }
+
+ /**
+ * Set the HPLMN Slice Differentiator.
+ * <p/>
+ * A value of {@link #SLICE_DIFFERENTIATOR_NO_SLICE} indicates that there is no
+ * corresponding Slice of the HPLMN.
+ *
+ * @throws IllegalArgumentException if the parameter is not between
+ * {@link #MIN_SLICE_DIFFERENTIATOR} and {@link #MAX_SLICE_DIFFERENTIATOR}.
+ *
+ * @return The same instance of the builder.
+ */
+ @NonNull
+ public Builder setMappedHplmnSliceDifferentiator(
+ @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
+ int mappedHplmnSliceDifferentiator) {
+ if (mappedHplmnSliceDifferentiator < MIN_SLICE_DIFFERENTIATOR
+ || mappedHplmnSliceDifferentiator > MAX_SLICE_DIFFERENTIATOR) {
+ throw new IllegalArgumentException("The slice diffentiator value is out of range");
+ }
+ this.mMappedHplmnSliceDifferentiator = mappedHplmnSliceDifferentiator;
+ return this;
+ }
+
+ /**
+ * Build the {@link SliceInfo}.
+ *
+ * @return the {@link SliceInfo} object.
+ */
+ @NonNull
+ public SliceInfo build() {
+ return new SliceInfo(this.mSliceServiceType, this.mSliceDifferentiator,
+ this.mMappedHplmnSliceServiceType, this.mMappedHplmnSliceDifferentiator);
+ }
+ }
+}
diff --git a/telephony/java/android/telephony/euicc/DownloadableSubscription.java b/telephony/java/android/telephony/euicc/DownloadableSubscription.java
index 52b31d7..a5150b0 100644
--- a/telephony/java/android/telephony/euicc/DownloadableSubscription.java
+++ b/telephony/java/android/telephony/euicc/DownloadableSubscription.java
@@ -15,6 +15,7 @@
*/
package android.telephony.euicc;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.PendingIntent;
@@ -102,44 +103,81 @@
this.accessRules = accessRules;
}
- /** @hide */
- @SystemApi
public static final class Builder {
@Nullable private String encodedActivationCode;
@Nullable private String confirmationCode;
@Nullable private String carrierName;
List<UiccAccessRule> accessRules;
+ /** @hide */
+ @SystemApi
public Builder() {}
- public Builder(DownloadableSubscription baseSubscription) {
+ public Builder(@NonNull DownloadableSubscription baseSubscription) {
encodedActivationCode = baseSubscription.getEncodedActivationCode();
confirmationCode = baseSubscription.getConfirmationCode();
carrierName = baseSubscription.getCarrierName();
accessRules = baseSubscription.getAccessRules();
}
+ public Builder(@NonNull String encodedActivationCode) {
+ this.encodedActivationCode = encodedActivationCode;
+ }
+
+ /**
+ * Builds a {@link DownloadableSubscription} object.
+ * @return a non-null {@link DownloadableSubscription} object.
+ */
+ @NonNull
public DownloadableSubscription build() {
return new DownloadableSubscription(encodedActivationCode, confirmationCode,
carrierName, accessRules);
}
- public Builder setEncodedActivationCode(String value) {
+ /**
+ * Sets the encoded activation code.
+ * @param value the activation code to use. An activation code can be parsed from a user
+ * scanned QR code. The format of activation code is defined in SGP.22. For
+ * example, "1$SMDP.GSMA.COM$04386-AGYFT-A74Y8-3F815$1.3.6.1.4.1.31746". For
+ * detail, see {@code com.android.euicc.data.ActivationCode}. Must not be null.
+ */
+ @NonNull
+ public Builder setEncodedActivationCode(@NonNull String value) {
encodedActivationCode = value;
return this;
}
- public Builder setConfirmationCode(String value) {
+ /**
+ * Sets the confirmation code.
+ * @param value the confirmation code to use to authenticate the carrier server got
+ * subscription download.
+ */
+ @NonNull
+ public Builder setConfirmationCode(@NonNull String value) {
confirmationCode = value;
return this;
}
- public Builder setCarrierName(String value) {
+ /**
+ * Sets the user-visible carrier name.
+ * @param value carrier name.
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public Builder setCarrierName(@NonNull String value) {
carrierName = value;
return this;
}
- public Builder setAccessRules(List<UiccAccessRule> value) {
+ /**
+ * Sets the {@link UiccAccessRule}s dictating access to this subscription.
+ * @param value A list of {@link UiccAccessRule}s.
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public Builder setAccessRules(@NonNull List<UiccAccessRule> value) {
accessRules = value;
return this;
}
diff --git a/telephony/java/android/telephony/ims/DelegateStateCallback.java b/telephony/java/android/telephony/ims/DelegateStateCallback.java
index fb65949..6bf992e 100644
--- a/telephony/java/android/telephony/ims/DelegateStateCallback.java
+++ b/telephony/java/android/telephony/ims/DelegateStateCallback.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.telephony.ims.stub.SipDelegate;
import android.telephony.ims.stub.SipTransportImplBase;
@@ -52,7 +53,9 @@
* implementing this feature elsewhere. If all features of this {@link SipDelegate} are
* denied, this method should still be called.
*/
- void onCreated(@NonNull SipDelegate delegate, @Nullable Set<FeatureTagState> deniedTags);
+ void onCreated(@NonNull SipDelegate delegate,
+ @SuppressLint("NullableCollection") // TODO(b/154763999): Mark deniedTags @Nonnull
+ @Nullable Set<FeatureTagState> deniedTags);
/**
* This must be called by the ImsService after the framework calls
diff --git a/telephony/java/android/telephony/ims/ImsUtListener.java b/telephony/java/android/telephony/ims/ImsUtListener.java
index baa0576..754814f 100644
--- a/telephony/java/android/telephony/ims/ImsUtListener.java
+++ b/telephony/java/android/telephony/ims/ImsUtListener.java
@@ -178,4 +178,11 @@
public ImsUtListener(IImsUtListener serviceInterface) {
mServiceInterface = serviceInterface;
}
+
+ /**
+ * @hide
+ */
+ public IImsUtListener getListenerInterface() {
+ return mServiceInterface;
+ }
}
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index f3c38bc..08eec29d 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -25,6 +25,7 @@
import android.annotation.StringDef;
import android.annotation.SystemApi;
import android.annotation.WorkerThread;
+import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
@@ -1325,7 +1326,7 @@
* the RCS VoLTE single registration feature. Only default messaging application may receive
* the intent.
*
- * <p>Contains {@link #EXTRA_SUBSCRIPTION_INDEX} to specify the subscription index for which
+ * <p>Contains {@link #EXTRA_SUBSCRIPTION_ID} to specify the subscription index for which
* the intent is valid. and {@link #EXTRA_STATUS} to specify RCS VoLTE single registration
* status.
*/
@@ -1371,7 +1372,7 @@
* provisioning is done using autoconfiguration, then these parameters shall be
* sent in the HTTP get request to fetch the RCS provisioning. RCS client
* configuration must be provided by the application before registering for the
- * provisioning status events {@link #registerRcsProvisioningChangedCallback()}
+ * provisioning status events {@link #registerRcsProvisioningChangedCallback}
* @param rcc RCS client configuration {@link RcsClientConfiguration}
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@@ -1387,13 +1388,15 @@
}
/**
- * Returns a flag to indicate if the device software and the carrier
- * have the capability to support RCS Volte single IMS registration.
- * @return true if this single registration is capable, false otherwise
+ * Returns a flag to indicate whether or not the device supports IMS single registration for
+ * MMTEL and RCS features as well as if the carrier has provisioned the feature.
+ * @return true if IMS single registration is capable at this time, or false otherwise
* @throws ImsException If the remote ImsService is not available for
* any reason or the subscription associated with this instance is no
* longer active. See {@link ImsException#getCode()} for more
* information.
+ * @see PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION for whether or not this
+ * device supports IMS single registration.
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isRcsVolteSingleRegistrationCapable() throws ImsException {
@@ -1430,7 +1433,7 @@
* available. This can happen if the service crashed, for example.
* It shall also throw this exception when the RCS client parameters for the
* application are not valid. In that case application must set the client
- * params (See {@link #setRcsClientConfiguration()}) and re register the
+ * params (See {@link #setRcsClientConfiguration}) and re register the
* callback.
* See {@link ImsException#getCode()} for a more detailed reason.
*/
@@ -1458,9 +1461,9 @@
* will result in a no-op.
* @param callback The existing {@link RcsProvisioningCallback} to be
* removed.
- * @see #registerRcsProvisioningChangedCallback(RcsClientConfiguration,
- * Executor, RcsProvisioningCallback) @throws IllegalArgumentException
- * if the subscription associated with this callback is invalid.
+ * @see #registerRcsProvisioningChangedCallback
+ * @throws IllegalArgumentException if the subscription associated with this callback is
+ * invalid.
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void unregisterRcsProvisioningChangedCallback(
diff --git a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
index 519d016..5eb75e7 100644
--- a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
+++ b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringDef;
+import android.annotation.SystemApi;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
@@ -34,14 +35,135 @@
* network during a SUBSCRIBE request. See RFC3863 for more information.
* @hide
*/
+@SystemApi
public final class RcsContactPresenceTuple implements Parcelable {
- /** The service id of the MMTEL */
+ /**
+ * The service ID used to indicate that MMTEL service is available.
+ * <p>
+ * See the GSMA RCC.07 specification for more information.
+ */
public static final String SERVICE_ID_MMTEL = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.mmtel";
- /** The service id of the Call Composer */
+ /**
+ * The service ID used to indicate that the chat(v1.0) is available.
+ * <p>
+ * See the GSMA RCC.07 specification for more information.
+ */
+ public static final String SERVICE_ID_CHAT_V1 = "org.openmobilealliance:IM-session";
+
+ /**
+ * The service ID used to indicate that the chat(v2.0) is available.
+ * <p>
+ * See the GSMA RCC.07 specification for more information.
+ */
+ public static final String SERVICE_ID_CHAT_V2 = "org.openmobilealliance:ChatSession";
+
+ /**
+ * The service ID used to indicate that the File Transfer is available.
+ * <p>
+ * See the GSMA RCC.07 specification for more information.
+ */
+ public static final String SERVICE_ID_FT = "org.openmobilealliance:File-Transfer-HTTP";
+
+ /**
+ * The service ID used to indicate that the File Transfer over SMS is available.
+ * <p>
+ * See the GSMA RCC.07 specification for more information.
+ */
+ public static final String SERVICE_ID_FT_OVER_SMS =
+ "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.ftsms";
+
+ /**
+ * The service ID used to indicate that the Geolocation Push is available.
+ * <p>
+ * See the GSMA RCC.07 specification for more information.
+ */
+ public static final String SERVICE_ID_GEO_PUSH =
+ "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.geopush";
+
+ /**
+ * The service ID used to indicate that the Geolocation Push via SMS is available.
+ * <p>
+ * See the GSMA RCC.07 specification for more information.
+ */
+ public static final String SERVICE_ID_GEO_PUSH_VIA_SMS =
+ "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.geosms";
+
+ /**
+ * The service ID used to indicate that the Call Composer is available.
+ * <p>
+ * See the GSMA RCC.07 specification for more information.
+ */
public static final String SERVICE_ID_CALL_COMPOSER =
- "org.3gpp.urn:urn-7:3gppservice.ims.icsi.gsma.callcomposer";
+ "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.callcomposer";
+
+ /**
+ * The service ID used to indicate that the Post Call is available.
+ * <p>
+ * See the GSMA RCC.07 specification for more information.
+ */
+ public static final String SERVICE_ID_POST_CALL =
+ "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.callunanswered";
+
+ /**
+ * The service ID used to indicate that the Shared Map is available.
+ * <p>
+ * See the GSMA RCC.07 specification for more information.
+ */
+ public static final String SERVICE_ID_SHARED_MAP =
+ "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.sharedmap";
+
+ /**
+ * The service ID used to indicate that the Shared Sketch is available.
+ * <p>
+ * See the GSMA RCC.07 specification for more information.
+ */
+ public static final String SERVICE_ID_SHARED_SKETCH =
+ "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.sharedsketch";
+
+ /**
+ * The service ID used to indicate that the Chatbot using Session is available.
+ * <p>
+ * See the GSMA RCC.07 specification for more information.
+ */
+ public static final String SERVICE_ID_CHATBOT =
+ "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.chatbot";
+
+ /**
+ * The service ID used to indicate that the Standalone Messaging is available.
+ * <p>
+ * See the GSMA RCC.07 specification for more information.
+ */
+ public static final String SERVICE_ID_CHATBOT_STANDALONE =
+ " org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.chatbot.sa";
+
+ /**
+ * The service ID used to indicate that the Chatbot Role is available.
+ * <p>
+ * See the GSMA RCC.07 specification for more information.
+ */
+ public static final String SERVICE_ID_CHATBOT_ROLE = "org.gsma.rcs.isbot";
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef(prefix = "SERVICE_ID_", value = {
+ SERVICE_ID_MMTEL,
+ SERVICE_ID_CHAT_V1,
+ SERVICE_ID_CHAT_V2,
+ SERVICE_ID_FT,
+ SERVICE_ID_FT_OVER_SMS,
+ SERVICE_ID_GEO_PUSH,
+ SERVICE_ID_GEO_PUSH_VIA_SMS,
+ SERVICE_ID_CALL_COMPOSER,
+ SERVICE_ID_POST_CALL,
+ SERVICE_ID_SHARED_MAP,
+ SERVICE_ID_SHARED_SKETCH,
+ SERVICE_ID_CHATBOT,
+ SERVICE_ID_CHATBOT_STANDALONE,
+ SERVICE_ID_CHATBOT_ROLE
+ })
+ public @interface ServiceId {}
/** The service capabilities is available. */
public static final String TUPLE_BASIC_STATUS_OPEN = "open";
@@ -149,6 +271,7 @@
in.readStringList(mSupportedDuplexModeList);
in.readStringList(mUnsupportedDuplexModeList);
}
+
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeBoolean(mIsAudioCapable);
@@ -217,12 +340,14 @@
/**
* Builds a RcsContactPresenceTuple instance.
+ * @param status The status associated with the service capability. See RFC3865 for more
+ * information.
* @param serviceId The OMA Presence service-id associated with this capability. See the
* OMA Presence SIMPLE specification v1.1, section 10.5.1.
* @param serviceVersion The OMA Presence version associated with the service capability.
* See the OMA Presence SIMPLE specification v1.1, section 10.5.1.
*/
- public Builder(@NonNull @BasicStatus String status, @NonNull String serviceId,
+ public Builder(@NonNull @BasicStatus String status, @NonNull @ServiceId String serviceId,
@NonNull String serviceVersion) {
mPresenceTuple = new RcsContactPresenceTuple(status, serviceId, serviceVersion);
}
@@ -230,16 +355,17 @@
/**
* The optional SIP Contact URI associated with the PIDF tuple element.
*/
- public @NonNull Builder addContactUri(@NonNull Uri contactUri) {
+ public @NonNull Builder setContactUri(@NonNull Uri contactUri) {
mPresenceTuple.mContactUri = contactUri;
return this;
}
/**
* The optional timestamp indicating the data and time of the status change of this tuple.
- * See RFC3863, section 4.1.7 for more information on the expected format.
+ * Per RFC3863 section 4.1.7, the timestamp is formatted as an IMPP datetime format
+ * string per RFC3339.
*/
- public @NonNull Builder addTimeStamp(@NonNull String timestamp) {
+ public @NonNull Builder setTimestamp(@NonNull String timestamp) {
mPresenceTuple.mTimestamp = timestamp;
return this;
}
@@ -248,7 +374,7 @@
* An optional parameter containing the description element of the service-description. See
* OMA Presence SIMPLE specification v1.1
*/
- public @NonNull Builder addDescription(@NonNull String description) {
+ public @NonNull Builder setServiceDescription(@NonNull String description) {
mPresenceTuple.mServiceDescription = description;
return this;
}
@@ -257,7 +383,7 @@
* An optional parameter containing the service capabilities of the presence tuple if they
* are present in the servcaps element.
*/
- public @NonNull Builder addServiceCapabilities(@NonNull ServiceCapabilities caps) {
+ public @NonNull Builder setServiceCapabilities(@NonNull ServiceCapabilities caps) {
mPresenceTuple.mServiceCapabilities = caps;
return this;
}
diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
index d4715bf..fe85502 100644
--- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java
+++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
@@ -33,6 +34,7 @@
* Contains the User Capability Exchange capabilities corresponding to a contact's URI.
* @hide
*/
+@SystemApi
public final class RcsContactUceCapability implements Parcelable {
/** Contains presence information associated with the contact */
@@ -70,52 +72,46 @@
public @interface SourceType {}
/**
+ * Capability information for the requested contact has expired and can not be refreshed due to
+ * a temporary network error. This is a temporary error and the capabilities of the contact
+ * should be queried again at a later time.
+ */
+ public static final int REQUEST_RESULT_UNKNOWN = 0;
+
+ /**
* The requested contact was found to be offline when queried. This is only applicable to
* contact capabilities that were queried via OPTIONS requests and the network returned a
* 408/480 response.
*/
- public static final int REQUEST_RESULT_NOT_ONLINE = 0;
+ public static final int REQUEST_RESULT_NOT_ONLINE = 1;
/**
* Capability information for the requested contact was not found. The contact should not be
* considered an RCS user.
*/
- public static final int REQUEST_RESULT_NOT_FOUND = 1;
+ public static final int REQUEST_RESULT_NOT_FOUND = 2;
/**
* Capability information for the requested contact was found successfully.
*/
- public static final int REQUEST_RESULT_FOUND = 2;
-
- /**
- * Capability information for the requested contact has expired and can not be refreshed due to
- * a temporary network error. This is a temporary error and the capabilities of the contact
- * should be queried again at a later time.
- */
- public static final int REQUEST_RESULT_UNKNOWN = 3;
+ public static final int REQUEST_RESULT_FOUND = 3;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = "REQUEST_RESULT_", value = {
+ REQUEST_RESULT_UNKNOWN,
REQUEST_RESULT_NOT_ONLINE,
REQUEST_RESULT_NOT_FOUND,
- REQUEST_RESULT_FOUND,
- REQUEST_RESULT_UNKNOWN
+ REQUEST_RESULT_FOUND
})
public @interface RequestResult {}
/**
- * The base class of {@link OptionsBuilder} and {@link PresenceBuilder}
- */
- public static abstract class RcsUcsCapabilityBuilder {
- public abstract @NonNull RcsContactUceCapability build();
- }
-
- /**
* Builder to help construct {@link RcsContactUceCapability} instances when capabilities were
* queried through SIP OPTIONS.
+ * @hide
*/
- public static class OptionsBuilder extends RcsUcsCapabilityBuilder {
+ public static final class OptionsBuilder {
private final RcsContactUceCapability mCapabilities;
@@ -162,7 +158,6 @@
/**
* @return the constructed instance.
*/
- @Override
public @NonNull RcsContactUceCapability build() {
return mCapabilities;
}
@@ -172,7 +167,7 @@
* Builder to help construct {@link RcsContactUceCapability} instances when capabilities were
* queried through a presence server.
*/
- public static class PresenceBuilder extends RcsUcsCapabilityBuilder {
+ public static final class PresenceBuilder {
private final RcsContactUceCapability mCapabilities;
@@ -214,7 +209,6 @@
/**
* @return the RcsContactUceCapability instance.
*/
- @Override
public @NonNull RcsContactUceCapability build() {
return mCapabilities;
}
@@ -284,6 +278,7 @@
* <p>
* Note: this is only populated if {@link #getCapabilityMechanism} is
* {@link RcsContactUceCapability#CAPABILITY_MECHANISM_OPTIONS}
+ * @hide
*/
public @NonNull List<String> getOptionsFeatureTags() {
if (mCapabilityMechanism != CAPABILITY_MECHANISM_OPTIONS) {
@@ -299,7 +294,7 @@
* Note: this is only populated if {@link #getCapabilityMechanism} is
* {@link RcsContactUceCapability#CAPABILITY_MECHANISM_PRESENCE}
*/
- public @NonNull List<RcsContactPresenceTuple> getPresenceTuples() {
+ public @NonNull List<RcsContactPresenceTuple> getCapabilityTuples() {
if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) {
return Collections.emptyList();
}
@@ -309,13 +304,14 @@
/**
* Get the RcsContactPresenceTuple associated with the given service id.
* @param serviceId The service id to get the presence tuple.
- * @return The RcsContactPresenceTuple which has the given service id.
+ * @return The RcsContactPresenceTuple which has the given service id or {@code null} if the
+ * service id does not exist in the list of presence tuples returned from the network.
*
* <p>
* Note: this is only populated if {@link #getCapabilityMechanism} is
* {@link RcsContactUceCapability#CAPABILITY_MECHANISM_PRESENCE}
*/
- public @Nullable RcsContactPresenceTuple getPresenceTuple(@NonNull String serviceId) {
+ public @Nullable RcsContactPresenceTuple getCapabilityTuple(@NonNull String serviceId) {
if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) {
return null;
}
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
index 6c31466..070fd799 100644
--- a/telephony/java/android/telephony/ims/RcsUceAdapter.java
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -63,6 +63,7 @@
* RcsFeature should not publish capabilities or service capability requests.
* @hide
*/
+ @SystemApi
public static final int CAPABILITY_TYPE_PRESENCE_UCE = 1 << 1;
/**@hide*/
@@ -77,12 +78,14 @@
* An unknown error has caused the request to fail.
* @hide
*/
+ @SystemApi
public static final int ERROR_GENERIC_FAILURE = 1;
/**
* The carrier network does not have UCE support enabled for this subscriber.
* @hide
*/
+ @SystemApi
public static final int ERROR_NOT_ENABLED = 2;
/**
@@ -90,12 +93,14 @@
* 1x only currently).
* @hide
*/
+ @SystemApi
public static final int ERROR_NOT_AVAILABLE = 3;
/**
* The network has responded with SIP 403 error and a reason "User not registered."
* @hide
*/
+ @SystemApi
public static final int ERROR_NOT_REGISTERED = 4;
/**
@@ -103,12 +108,14 @@
* presence" for this subscriber.
* @hide
*/
+ @SystemApi
public static final int ERROR_NOT_AUTHORIZED = 5;
/**
* The network has responded to this request with a SIP 403 error and no reason.
* @hide
*/
+ @SystemApi
public static final int ERROR_FORBIDDEN = 6;
/**
@@ -116,6 +123,7 @@
* subscriber to the carrier network.
* @hide
*/
+ @SystemApi
public static final int ERROR_NOT_FOUND = 7;
/**
@@ -123,6 +131,7 @@
* with a lower number of contact numbers. The number varies per carrier.
* @hide
*/
+ @SystemApi
// TODO: Try to integrate this into the API so that the service will split based on carrier.
public static final int ERROR_REQUEST_TOO_LARGE = 8;
@@ -130,18 +139,21 @@
* The network did not respond to the capabilities request before the request timed out.
* @hide
*/
+ @SystemApi
public static final int ERROR_REQUEST_TIMEOUT = 9;
/**
* The request failed due to the service having insufficient memory.
* @hide
*/
+ @SystemApi
public static final int ERROR_INSUFFICIENT_MEMORY = 10;
/**
* The network was lost while trying to complete the request.
* @hide
*/
+ @SystemApi
public static final int ERROR_LOST_NETWORK = 11;
/**
@@ -149,6 +161,7 @@
* time returned in {@link CapabilitiesCallback#onError} has elapsed.
* @hide
*/
+ @SystemApi
public static final int ERROR_SERVER_UNAVAILABLE = 12;
/**@hide*/
@@ -405,6 +418,7 @@
* @see #requestCapabilities(Executor, List, CapabilitiesCallback)
* @hide
*/
+ @SystemApi
public interface CapabilitiesCallback {
/**
@@ -424,10 +438,10 @@
* The pending request has resulted in an error and may need to be retried, depending on the
* error code.
* @param errorCode The reason for the framework being unable to process the request.
- * @param retryAfterMilliseconds The time in milliseconds the requesting application should
+ * @param retryIntervalMillis The time in milliseconds the requesting application should
* wait before retrying, if non-zero.
*/
- void onError(@ErrorCode int errorCode, long retryAfterMilliseconds);
+ void onError(@ErrorCode int errorCode, long retryIntervalMillis);
}
private final Context mContext;
@@ -458,9 +472,9 @@
* {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} is enabled or else
* this operation will fail with {@link #ERROR_NOT_AVAILABLE} or {@link #ERROR_NOT_ENABLED}.
*
+ * @param contactNumbers A list of numbers that the capabilities are being requested for.
* @param executor The executor that will be used when the request is completed and the
* {@link CapabilitiesCallback} is called.
- * @param contactNumbers A list of numbers that the capabilities are being requested for.
* @param c A one-time callback for when the request for capabilities completes or there is an
* error processing the request.
* @throws ImsException if the subscription associated with this instance of
@@ -469,9 +483,10 @@
* becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public void requestCapabilities(@NonNull @CallbackExecutor Executor executor,
- @NonNull List<Uri> contactNumbers,
+ public void requestCapabilities(@NonNull List<Uri> contactNumbers,
+ @NonNull @CallbackExecutor Executor executor,
@NonNull CapabilitiesCallback c) throws ImsException {
if (c == null) {
throw new IllegalArgumentException("Must include a non-null CapabilitiesCallback.");
@@ -495,8 +510,7 @@
public void onCapabilitiesReceived(List<RcsContactUceCapability> contactCapabilities) {
final long callingIdentity = Binder.clearCallingIdentity();
try {
- executor.execute(() ->
- c.onCapabilitiesReceived(contactCapabilities));
+ executor.execute(() -> c.onCapabilitiesReceived(contactCapabilities));
} finally {
restoreCallingIdentity(callingIdentity);
}
@@ -550,13 +564,17 @@
* {@link #ERROR_NOT_AVAILABLE} or {@link #ERROR_NOT_ENABLED}.
*
* @param contactNumber The contact of the capabilities is being requested for.
+ * @param executor The executor that will be used when the request is completed and the
+ * {@link CapabilitiesCallback} is called.
* @param c A one-time callback for when the request for capabilities completes or there is
* an error processing the request.
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
- public void requestNetworkAvailability(@NonNull @CallbackExecutor Executor executor,
- @NonNull Uri contactNumber, @NonNull CapabilitiesCallback c) throws ImsException {
+ public void requestAvailability(@NonNull Uri contactNumber,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull CapabilitiesCallback c) throws ImsException {
if (executor == null) {
throw new IllegalArgumentException("Must include a non-null Executor.");
}
@@ -569,7 +587,7 @@
IImsRcsController imsRcsController = getIImsRcsController();
if (imsRcsController == null) {
- Log.e(TAG, "requestNetworkAvailability: IImsRcsController is null");
+ Log.e(TAG, "requestAvailability: IImsRcsController is null");
throw new ImsException("Cannot find remote IMS service",
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
@@ -579,8 +597,7 @@
public void onCapabilitiesReceived(List<RcsContactUceCapability> contactCapabilities) {
final long callingIdentity = Binder.clearCallingIdentity();
try {
- executor.execute(() ->
- c.onCapabilitiesReceived(contactCapabilities));
+ executor.execute(() -> c.onCapabilitiesReceived(contactCapabilities));
} finally {
restoreCallingIdentity(callingIdentity);
}
@@ -606,12 +623,12 @@
};
try {
- imsRcsController.requestNetworkAvailability(mSubId, mContext.getOpPackageName(),
+ imsRcsController.requestAvailability(mSubId, mContext.getOpPackageName(),
mContext.getAttributionTag(), contactNumber, internalCallback);
} catch (ServiceSpecificException e) {
throw new ImsException(e.toString(), e.errorCode);
} catch (RemoteException e) {
- Log.e(TAG, "Error calling IImsRcsController#requestNetworkAvailability", e);
+ Log.e(TAG, "Error calling IImsRcsController#requestAvailability", e);
throw new ImsException("Remote IMS Service is not available",
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
@@ -683,7 +700,7 @@
if (imsRcsController == null) {
Log.e(TAG, "addOnPublishStateChangedListener : IImsRcsController is null");
throw new ImsException("Cannot find remote IMS service",
- ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
PublishStateCallbackAdapter stateCallback = addPublishStateCallback(executor, listener);
@@ -694,7 +711,7 @@
} catch (RemoteException e) {
Log.e(TAG, "Error calling IImsRcsController#registerUcePublishStateCallback", e);
throw new ImsException("Remote IMS Service is not available",
- ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
}
diff --git a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
index eddbb10..8762b6a 100644
--- a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
+++ b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
@@ -280,6 +280,12 @@
"sip_config_path_header_string";
/**
+ * The SIP User-Agent header value used by the IMS stack during IMS registration.
+ */
+ public static final String KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING =
+ "sip_config_sip_user_agent_header_string";
+
+ /**
* SIP User part string in contact header
*/
public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING =
@@ -292,12 +298,20 @@
"sip_config_p_access_network_info_header_string";
/**
- * SIP P-last-access-network-info header string
+ * The SIP P-last-access-network-info header value, populated for networks that require this
+ * information to be provided in outgoing SIP messages.
*/
public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING =
"sip_config_p_last_access_network_info_header_string";
/**
+ * The Cellular-Network-Info header value (See 3GPP 24.229, section 7.2.15), populated for
+ * networks that require this information to be provided as part of outgoing SIP messages.
+ */
+ public static final String KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING =
+ "sip_config_cellular_network_info_header_string";
+
+ /**
* SIP P-associated-uri header string
*/
public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING =
@@ -320,9 +334,11 @@
KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING,
KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING,
KEY_SIP_CONFIG_PATH_HEADER_STRING,
+ KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING,
KEY_SIP_CONFIG_URI_USER_PART_STRING,
KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING,
KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING,
+ KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING,
KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING
})
@Retention(RetentionPolicy.SOURCE)
diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java
index 2e9eb94..04421c9 100644
--- a/telephony/java/android/telephony/ims/SipDelegateManager.java
+++ b/telephony/java/android/telephony/ims/SipDelegateManager.java
@@ -24,6 +24,7 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.telephony.BinderCacheManager;
@@ -47,6 +48,9 @@
* This allows multiple IMS applications to forward SIP messages to/from their application for the
* purposes of providing a single IMS registration to the carrier's IMS network from potentially
* many IMS stacks implementing a subset of the supported MMTEL/RCS features.
+ * <p>
+ * This API is only supported if the device supports the
+ * {@link PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION} feature.
* @hide
*/
@SystemApi
@@ -269,6 +273,7 @@
* {@link ImsException#getCode()} for more information.
*
* @see CarrierConfigManager.Ims#KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL
+ * @see PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isSupported() throws ImsException {
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
index 3634989..7a6c28b 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
@@ -52,7 +52,7 @@
// ImsUceAdapter specific
void requestCapabilities(int subId, String callingPackage, String callingFeatureId,
in List<Uri> contactNumbers, IRcsUceControllerCallback c);
- void requestNetworkAvailability(int subId, String callingPackage,
+ void requestAvailability(int subId, String callingPackage,
String callingFeatureId, in Uri contactNumber,
IRcsUceControllerCallback c);
int getUcePublishState(int subId);
diff --git a/telephony/java/android/telephony/ims/aidl/IPublishResponseCallback.aidl b/telephony/java/android/telephony/ims/aidl/IPublishResponseCallback.aidl
index 481e7f8..b99d8a7d 100644
--- a/telephony/java/android/telephony/ims/aidl/IPublishResponseCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IPublishResponseCallback.aidl
@@ -26,4 +26,5 @@
oneway interface IPublishResponseCallback {
void onCommandError(int code);
void onNetworkResponse(int code, String reason);
+ void onNetworkRespHeader(int code, String reasonPhrase, int reasonHeaderCause, String reasonHeaderText);
}
diff --git a/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl b/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl
index a141993..8cc8020 100644
--- a/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl
@@ -30,6 +30,7 @@
oneway interface ISubscribeResponseCallback {
void onCommandError(int code);
void onNetworkResponse(int code, in String reason);
+ void onNetworkRespHeader(int code, String reasonPhrase, int reasonHeaderCause, String reasonHeaderText);
void onNotifyCapabilitiesUpdate(in List<String> pidfXmls);
void onResourceTerminated(in List<RcsContactTerminatedReason> uriTerminatedReason);
void onTerminated(in String reason, long retryAfterMilliseconds);
diff --git a/telephony/java/android/telephony/ims/aidl/RcsPublishResponseAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/RcsPublishResponseAidlWrapper.java
index 22985d0..65415ea 100644
--- a/telephony/java/android/telephony/ims/aidl/RcsPublishResponseAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/RcsPublishResponseAidlWrapper.java
@@ -34,10 +34,11 @@
}
@Override
- public void onCommandError(int code) {
+ public void onCommandError(int code) throws ImsException {
try {
mResponseBinder.onCommandError(code);
} catch (RemoteException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
}
@@ -46,6 +47,18 @@
try {
mResponseBinder.onNetworkResponse(code, reason);
} catch (RemoteException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ @Override
+ public void onNetworkResponse(int code, String reasonPhrase, int reasonHeaderCause,
+ String reasonHeaderText) throws ImsException {
+ try {
+ mResponseBinder.onNetworkRespHeader(code, reasonPhrase, reasonHeaderCause,
+ reasonHeaderText);
+ } catch (RemoteException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
}
}
diff --git a/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java
index 1fb339c..11118c0 100644
--- a/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java
@@ -40,10 +40,11 @@
}
@Override
- public void onCommandError(int code) {
+ public void onCommandError(int code) throws ImsException {
try {
mResponseBinder.onCommandError(code);
} catch (RemoteException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
}
@@ -52,6 +53,18 @@
try {
mResponseBinder.onNetworkResponse(code, reason);
} catch (RemoteException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ @Override
+ public void onNetworkResponse(int code, String reasonPhrase, int reasonHeaderCause,
+ String reasonHeaderText) throws ImsException {
+ try {
+ mResponseBinder.onNetworkRespHeader(code, reasonPhrase, reasonHeaderCause,
+ reasonHeaderText);
+ } catch (RemoteException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
}
@@ -60,6 +73,7 @@
try {
mResponseBinder.onNotifyCapabilitiesUpdate(pidfXmls);
} catch (RemoteException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
}
@@ -69,6 +83,7 @@
try {
mResponseBinder.onResourceTerminated(getTerminatedReasonList(uriTerminatedReason));
} catch (RemoteException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
}
@@ -90,6 +105,7 @@
try {
mResponseBinder.onTerminated(reason, retryAfterMilliseconds);
} catch (RemoteException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
}
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
index 06c35ea..5f8e93d 100644
--- a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
@@ -23,6 +23,8 @@
import com.android.ims.internal.IImsEcbm;
import com.android.ims.internal.IImsEcbmListener;
+import java.util.Objects;
+
/**
* Base implementation of ImsEcbm, which implements stub versions of the methods
* in the IImsEcbm AIDL. Override the methods that your implementation of ImsEcbm supports.
@@ -36,11 +38,31 @@
public class ImsEcbmImplBase {
private static final String TAG = "ImsEcbmImplBase";
+ private final Object mLock = new Object();
private IImsEcbmListener mListener;
- private IImsEcbm mImsEcbm = new IImsEcbm.Stub() {
+ private final IImsEcbm mImsEcbm = new IImsEcbm.Stub() {
@Override
public void setListener(IImsEcbmListener listener) {
- mListener = listener;
+ synchronized (mLock) {
+ if (mListener != null && !mListener.asBinder().isBinderAlive()) {
+ Log.w(TAG, "setListener: discarding dead Binder");
+ mListener = null;
+ }
+ if (mListener != null && listener != null && Objects.equals(
+ mListener.asBinder(), listener.asBinder())) {
+ return;
+ }
+ if (listener == null) {
+ mListener = null;
+ } else if (listener != null && mListener == null) {
+ mListener = listener;
+ } else {
+ // Fail fast here instead of silently overwriting the listener to another
+ // listener due to another connection connecting.
+ throw new IllegalStateException("ImsEcbmImplBase: Listener already set by "
+ + "another connection.");
+ }
+ }
}
@Override
@@ -69,9 +91,13 @@
*/
public final void enteredEcbm() {
Log.d(TAG, "Entered ECBM.");
- if (mListener != null) {
+ IImsEcbmListener listener;
+ synchronized (mLock) {
+ listener = mListener;
+ }
+ if (listener != null) {
try {
- mListener.enteredECBM();
+ listener.enteredECBM();
} catch (RemoteException e) {
throw new RuntimeException(e);
}
@@ -85,9 +111,13 @@
*/
public final void exitedEcbm() {
Log.d(TAG, "Exited ECBM.");
- if (mListener != null) {
+ IImsEcbmListener listener;
+ synchronized (mLock) {
+ listener = mListener;
+ }
+ if (listener != null) {
try {
- mListener.exitedECBM();
+ listener.exitedECBM();
} catch (RemoteException e) {
throw new RuntimeException(e);
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
index d002903..8e961ac 100644
--- a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
@@ -25,6 +25,7 @@
import com.android.ims.internal.IImsMultiEndpoint;
import java.util.List;
+import java.util.Objects;
/**
* Base implementation of ImsMultiEndpoint, which implements stub versions of the methods
@@ -41,10 +42,32 @@
private static final String TAG = "MultiEndpointImplBase";
private IImsExternalCallStateListener mListener;
- private IImsMultiEndpoint mImsMultiEndpoint = new IImsMultiEndpoint.Stub() {
+ private final Object mLock = new Object();
+ private final IImsMultiEndpoint mImsMultiEndpoint = new IImsMultiEndpoint.Stub() {
+
@Override
public void setListener(IImsExternalCallStateListener listener) throws RemoteException {
- mListener = listener;
+ synchronized (mLock) {
+ if (mListener != null && !mListener.asBinder().isBinderAlive()) {
+ Log.w(TAG, "setListener: discarding dead Binder");
+ mListener = null;
+ }
+ if (mListener != null && listener != null && Objects.equals(
+ mListener.asBinder(), listener.asBinder())) {
+ return;
+ }
+
+ if (listener == null) {
+ mListener = null;
+ } else if (listener != null && mListener == null) {
+ mListener = listener;
+ } else {
+ // Fail fast here instead of silently overwriting the listener to another
+ // listener due to another connection connecting.
+ throw new IllegalStateException("ImsMultiEndpointImplBase: Listener already"
+ + " set by another connection.");
+ }
+ }
}
@Override
@@ -65,9 +88,13 @@
*/
public final void onImsExternalCallStateUpdate(List<ImsExternalCallState> externalCallDialogs) {
Log.d(TAG, "ims external call state update triggered.");
- if (mListener != null) {
+ IImsExternalCallStateListener listener;
+ synchronized (mLock) {
+ listener = mListener;
+ }
+ if (listener != null) {
try {
- mListener.onImsExternalCallStateUpdate(externalCallDialogs);
+ listener.onImsExternalCallStateUpdate(externalCallDialogs);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
index f5219d5..83b89aa 100644
--- a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
@@ -23,12 +23,14 @@
import android.os.Bundle;
import android.os.RemoteException;
import android.telephony.ims.ImsUtListener;
+import android.util.Log;
import com.android.ims.internal.IImsUt;
import com.android.ims.internal.IImsUtListener;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
/**
* Base implementation of IMS UT interface, which implements stubs. Override these methods to
@@ -40,6 +42,7 @@
// will break other implementations of ImsUt maintained by other ImsServices.
@SystemApi
public class ImsUtImplBase {
+ private static final String TAG = "ImsUtImplBase";
/**
* Bar all incoming calls. (See 3GPP TS 24.611)
* @hide
@@ -116,7 +119,10 @@
*/
public static final int INVALID_RESULT = -1;
- private IImsUt.Stub mServiceImpl = new IImsUt.Stub() {
+ private final IImsUt.Stub mServiceImpl = new IImsUt.Stub() {
+ private final Object mLock = new Object();
+ private ImsUtListener mUtListener;
+
@Override
public void close() throws RemoteException {
ImsUtImplBase.this.close();
@@ -202,7 +208,31 @@
@Override
public void setListener(IImsUtListener listener) throws RemoteException {
- ImsUtImplBase.this.setListener(new ImsUtListener(listener));
+ synchronized (mLock) {
+ if (mUtListener != null
+ && !mUtListener.getListenerInterface().asBinder().isBinderAlive()) {
+ Log.w(TAG, "setListener: discarding dead Binder");
+ mUtListener = null;
+ }
+ if (mUtListener != null && listener != null && Objects.equals(
+ mUtListener.getListenerInterface().asBinder(), listener.asBinder())) {
+ return;
+ }
+
+ if (listener == null) {
+ mUtListener = null;
+ } else if (listener != null && mUtListener == null) {
+ mUtListener = new ImsUtListener(listener);
+ } else {
+ // This is a limitation of the current API surface, there can only be one
+ // listener connected. Fail fast instead of silently overwriting the other
+ // listener.
+ throw new IllegalStateException("ImsUtImplBase#setListener: listener already "
+ + "set by another connected interface!");
+ }
+ }
+
+ ImsUtImplBase.this.setListener(mUtListener);
}
@Override
diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
index c84e23c..ec98be6 100644
--- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
@@ -24,6 +24,7 @@
import android.annotation.SystemApi;
import android.net.Uri;
import android.telephony.ims.ImsException;
+import android.telephony.ims.RcsUceAdapter;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.RcsFeature;
import android.util.Log;
@@ -139,19 +140,48 @@
* Provide the framework with a subsequent network response update to
* {@link #publishCapabilities(String, PublishResponseCallback)}.
*
- * @param code The SIP response code sent from the network for the operation
+ * If this network response also contains a “Reason” header, then the
+ * {@link onNetworkResponse(int, String, int, String)} method should be used instead.
+ *
+ * @param sipCode The SIP response code sent from the network for the operation
* token specified.
* @param reason The optional reason response from the network. If there is a reason header
* included in the response, that should take precedence over the reason provided in the
- * status line. If the network provided no reason with the code, the string should be empty.
+ * status line. If the network provided no reason with the sip code, the string should be
+ * empty.
* @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
* not currently connected to the framework. This can happen if the {@link RcsFeature}
* is not {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
* the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases
* when the Telephony stack has crashed.
*/
- void onNetworkResponse(@IntRange(from = 100, to = 699) int code,
+ void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode,
@NonNull String reason) throws ImsException;
+
+ /**
+ * Provide the framework with a subsequent network response update to
+ * {@link #publishCapabilities(RcsContactUceCapability, int)} that also
+ * includes a reason provided in the “reason” header. See RFC3326 for more
+ * information.
+ *
+ * @param sipCode The SIP response code sent from the network.
+ * @param reasonPhrase The optional reason response from the network. If the
+ * network provided no reason with the sip code, the string should be empty.
+ * @param reasonHeaderCause The “cause” parameter of the “reason” header
+ * included in the SIP message.
+ * @param reasonHeaderText The “text” parameter of the “reason” header
+ * included in the SIP message.
+ * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
+ * not currently connected to the framework. This can happen if the
+ * {@link RcsFeature} is not
+ * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
+ * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in
+ * rare cases when the Telephony stack has crashed.
+ */
+ void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode,
+ @NonNull String reasonPhrase,
+ @IntRange(from = 100, to = 699) int reasonHeaderCause,
+ @NonNull String reasonHeaderText) throws ImsException;
}
/**
@@ -173,7 +203,7 @@
/**
* Send the response of a SIP OPTIONS capability exchange to the framework.
- * @param code The SIP response code that was sent by the network in response
+ * @param sipCode The SIP response code that was sent by the network in response
* to the request sent by {@link #sendOptionsCapabilityRequest}.
* @param reason The optional SIP response reason sent by the network.
* If none was sent, this should be an empty string.
@@ -186,17 +216,20 @@
* {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare
* cases when the Telephony stack has crashed.
*/
- void onNetworkResponse(int code, @NonNull String reason,
+ void onNetworkResponse(int sipCode, @NonNull String reason,
@Nullable List<String> theirCaps) throws ImsException;
}
/**
* Interface used by the framework to receive the response of the subscribe request.
- * @hide
*/
public interface SubscribeResponseCallback {
/**
* Notify the framework that the command associated with this callback has failed.
+ * <p>
+ * Must only be called when there was an error generating a SUBSCRIBE request due to an
+ * IMS stack error. This is a terminating event, so no other callback event will be
+ * expected after this callback.
*
* @param code The reason why the associated command has failed.
* @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
@@ -211,27 +244,66 @@
/**
* Notify the framework of the response to the SUBSCRIBE request from
* {@link #subscribeForCapabilities(List<Uri>, SubscribeResponseCallback)}.
+ * <p>
+ * If the carrier network responds to the SUBSCRIBE request with a 2XX response, then the
+ * framework will expect the IMS stack to call {@link #onNotifyCapabilitiesUpdate},
+ * {@link #onResourceTerminated}, and {@link #onTerminated} as required for the
+ * subsequent NOTIFY responses to the subscription.
*
- * @param code The SIP response code sent from the network for the operation
+ * If this network response also contains a “Reason” header, then the
+ * {@link onNetworkResponse(int, String, int, String)} method should be used instead.
+ *
+ * @param sipCode The SIP response code sent from the network for the operation
* token specified.
* @param reason The optional reason response from the network. If the network
- * provided no reason with the code, the string should be empty.
+ * provided no reason with the sip code, the string should be empty.
* @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
* not currently connected to the framework. This can happen if the
* {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the
* {@link RcsFeature} has not received the {@link ImsFeature#onFeatureReady()} callback.
* This may also happen in rare cases when the Telephony stack has crashed.
*/
- void onNetworkResponse(@IntRange(from = 100, to = 699) int code,
+ void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode,
@NonNull String reason) throws ImsException;
/**
- * Provides the framework with latest XML PIDF documents included in the
- * network response for the requested contacts' capabilities requested by the
- * Framework using {@link #requestCapabilities(List, int)}. This should be
- * called every time a new NOTIFY event is received with new capability
+ * Notify the framework of the response to the SUBSCRIBE request from
+ * {@link #subscribeForCapabilities(RcsContactUceCapability, int)} that also
+ * includes a reason provided in the “reason” header. See RFC3326 for more
* information.
*
+ * @param sipCode The SIP response code sent from the network,
+ * @param reasonPhrase The optional reason response from the network. If the
+ * network provided no reason with the sip code, the string should be empty.
+ * @param reasonHeaderCause The “cause” parameter of the “reason” header
+ * included in the SIP message.
+ * @param reasonHeaderText The “text” parameter of the “reason” header
+ * included in the SIP message.
+ * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
+ * not currently connected to the framework. This can happen if the
+ * {@link RcsFeature} is not
+ * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
+ * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in
+ * rare cases when the Telephony stack has crashed.
+ */
+ void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode,
+ @NonNull String reasonPhrase,
+ @IntRange(from = 100, to = 699) int reasonHeaderCause,
+ @NonNull String reasonHeaderText) throws ImsException;
+
+ /**
+ * Notify the framework of the latest XML PIDF documents included in the network response
+ * for the requested contacts' capabilities requested by the Framework using
+ * {@link RcsUceAdapter#requestCapabilities(Executor, List<Uri>, CapabilitiesCallback)}.
+ * <p>
+ * The expected format for the PIDF XML is defined in RFC3861. Each XML document must be a
+ * "application/pidf+xml" object and start with a root <presence> element. For NOTIFY
+ * responses that contain RLMI information and potentially multiple PIDF XMLs, each
+ * PIDF XML should be separated and added as a separate item in the List. This should be
+ * called every time a new NOTIFY event is received with new capability information.
+ *
+ * @param pidfXmls The list of the PIDF XML data for the contact URIs that it subscribed
+ * for.
* @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
* not currently connected to the framework.
* This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the
@@ -242,21 +314,42 @@
void onNotifyCapabilitiesUpdate(@NonNull List<String> pidfXmls) throws ImsException;
/**
- * A resource in the resource list for the presence subscribe event has been terminated.
+ * Notify the framework that a resource in the RLMI XML contained in the NOTIFY response
+ * for the ongoing SUBSCRIBE dialog has been terminated.
* <p>
- * This allows the framework to know that there will not be any capability information for
- * a specific contact URI that they subscribed for.
+ * This will be used to notify the framework that a contact URI that the IMS stack has
+ * subscribed to on the Resource List Server has been terminated as well as the reason why.
+ * Usually this means that there will not be any capability information for the contact URI
+ * that they subscribed for. See RFC 4662 for more information.
+ *
+ * @param uriTerminatedReason The contact URIs which have been terminated. Each pair in the
+ * list is the contact URI and its terminated reason.
+ * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
+ * not currently connected to the framework.
+ * This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the
+ * {@link RcsFeature} {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not
+ * received the {@link ImsFeature#onFeatureReady()} callback. This may also happen in
+ * rare cases when the Telephony stack has crashed.
*/
void onResourceTerminated(
@NonNull List<Pair<Uri, String>> uriTerminatedReason) throws ImsException;
/**
- * The subscription associated with a previous #requestCapabilities operation
- * has been terminated. This will mostly be due to the subscription expiring,
- * but may also happen due to an error.
- * <p>
- * This allows the framework to know that there will no longer be any
- * capability updates for the requested operationToken.
+ * The subscription associated with a previous
+ * {@link RcsUceAdapter#requestCapabilities(Executor, List<Uri>, CapabilitiesCallback)}
+ * operation has been terminated. This will mostly be due to the network sending a final
+ * NOTIFY response due to the subscription expiring, but this may also happen due to a
+ * network error.
+ *
+ * @param reason The reason for the request being unable to process.
+ * @param retryAfterMilliseconds The time in milliseconds the requesting application should
+ * wait before retrying, if non-zero.
+ * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
+ * not currently connected to the framework.
+ * This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the
+ * {@link RcsFeature} {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not
+ * received the {@link ImsFeature#onFeatureReady()} callback. This may also happen in
+ * rare cases when the Telephony stack has crashed.
*/
void onTerminated(@NonNull String reason, long retryAfterMilliseconds) throws ImsException;
}
@@ -278,18 +371,23 @@
/**
* The user capabilities of one or multiple contacts have been requested by the framework.
* <p>
+ * The implementer must follow up this call with an
+ * {@link SubscribeResponseCallback#onCommandError} call to indicate this operation has failed.
* The response from the network to the SUBSCRIBE request must be sent back to the framework
- * using {@link #onSubscribeNetworkResponse(int, String, int)}. As NOTIFY requests come in from
- * the network, the requested contact’s capabilities should be sent back to the framework using
- * {@link #onSubscribeNotifyRequest} and {@link onSubscribeResourceTerminated}
+ * using {@link SubscribeResponseCallback#onNetworkResponse(int, String)}.
+ * As NOTIFY requests come in from the network, the requested contact’s capabilities should be
+ * sent back to the framework using
+ * {@link SubscribeResponseCallback#onNotifyCapabilitiesUpdate(List<String>}) and
+ * {@link SubscribeResponseCallback#onResourceTerminated(List<Pair<Uri, String>>)}
* should be called with the presence information for the contacts specified.
* <p>
- * Once the subscription is terminated, {@link #onSubscriptionTerminated} must be called for
- * the framework to finish listening for NOTIFY responses.
+ * Once the subscription is terminated,
+ * {@link SubscribeResponseCallback#onTerminated(String, long)} must be called for the
+ * framework to finish listening for NOTIFY responses.
+ *
* @param uris A {@link List} of the {@link Uri}s that the framework is requesting the UCE
* capabilities for.
* @param cb The callback of the subscribe request.
- * @hide
*/
// executor used is defined in the constructor.
@SuppressLint("ExecutorRegistration")
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index e556664..77d46f4 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2382,6 +2382,26 @@
*/
String getMobileProvisioningUrl();
+ /*
+ * Remove the EAB contacts from the EAB database.
+ */
+ int removeContactFromEab(int subId, String contacts);
+
+ /**
+ * Get the EAB contact from the EAB database.
+ */
+ String getContactFromEab(String contact);
+
+ /*
+ * Check whether the device supports RCS User Capability Exchange or not.
+ */
+ boolean getDeviceUceEnabled();
+
+ /*
+ * Set the device supports RCS User Capability Exchange.
+ */
+ void setDeviceUceEnabled(boolean isEnabled);
+
/**
* Set a SignalStrengthUpdateRequest to receive notification when Signal Strength breach the
* specified thresholds.
diff --git a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
index 02597d5..e673549 100644
--- a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
@@ -96,6 +96,19 @@
assertSessionReady(sessionId);
}
+ @Test
+ public void testStagedInstallationShouldCleanUpOnValidationFailure() throws Exception {
+ InstallUtils.commitExpectingFailure(AssertionError.class, "INSTALL_FAILED_INVALID_APK",
+ Install.single(TestApp.AIncompleteSplit).setStaged());
+ }
+
+ @Test
+ public void testStagedInstallationShouldCleanUpOnValidationFailureMultiPackage()
+ throws Exception {
+ InstallUtils.commitExpectingFailure(AssertionError.class, "INSTALL_FAILED_INVALID_APK",
+ Install.multi(TestApp.AIncompleteSplit, TestApp.B1, TestApp.Apex1).setStaged());
+ }
+
private static void assertSessionReady(int sessionId) {
assertSessionState(sessionId,
(session) -> assertThat(session.isStagedSessionReady()).isTrue());
diff --git a/tests/UpdatableSystemFontTest/OWNERS b/tests/UpdatableSystemFontTest/OWNERS
new file mode 100644
index 0000000..34ac813
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 24939
+
+include /graphics/java/android/graphics/fonts/OWNERS
diff --git a/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java b/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java
index ef973ac..861d221 100644
--- a/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java
+++ b/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java
@@ -182,6 +182,24 @@
@SmallTest
@Test
+ public void setFunctionsNcmAndRndis() {
+ final long rndisPlusNcm = UsbManager.FUNCTION_RNDIS | UsbManager.FUNCTION_NCM;
+
+ mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS,
+ UsbManager.FUNCTION_NCM));
+ assertEquals(UsbManager.FUNCTION_NCM, mUsbHandler.getEnabledFunctions() & rndisPlusNcm);
+
+ mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS,
+ rndisPlusNcm));
+ assertEquals(rndisPlusNcm, mUsbHandler.getEnabledFunctions() & rndisPlusNcm);
+
+ mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS,
+ UsbManager.FUNCTION_NCM));
+ assertEquals(UsbManager.FUNCTION_NCM, mUsbHandler.getEnabledFunctions() & rndisPlusNcm);
+ }
+
+ @SmallTest
+ @Test
public void enableAdb() {
sendBootCompleteMessages(mUsbHandler);
Message msg = mUsbHandler.obtainMessage(MSG_ENABLE_ADB);
diff --git a/tests/UsbTests/src/com/android/server/usb/UsbManagerNoPermTest.java b/tests/UsbTests/src/com/android/server/usb/UsbManagerNoPermTest.java
index a0fd9d4..b8bd98e 100644
--- a/tests/UsbTests/src/com/android/server/usb/UsbManagerNoPermTest.java
+++ b/tests/UsbTests/src/com/android/server/usb/UsbManagerNoPermTest.java
@@ -16,6 +16,8 @@
package com.android.server.usb;
+import static org.junit.Assert.assertEquals;
+
import android.content.Context;
import android.hardware.usb.UsbManager;
@@ -23,12 +25,12 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.usblib.UsbManagerTestLib;
+
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
-import com.android.server.usblib.UsbManagerTestLib;
-
/**
* Unit tests for {@link android.hardware.usb.UsbManager}.
* Note: NOT claimed MANAGE_USB permission in Manifest
@@ -78,4 +80,35 @@
public void testUsbApi_SetCurrentFunctions_OnSecurityException() throws Exception {
mUsbManagerTestLib.testSetCurrentFunctionsEx(UsbManager.FUNCTION_NONE);
}
+
+ public void assertSettableFunctions(boolean settable, long functions) {
+ assertEquals(
+ "areSettableFunctions(" + UsbManager.usbFunctionsToString(functions) + "):",
+ settable, UsbManager.areSettableFunctions(functions));
+ }
+
+ /**
+ * Tests the behaviour of the static areSettableFunctions method. This method performs no IPCs
+ * and requires no permissions.
+ */
+ @Test
+ public void testUsbManager_AreSettableFunctions() {
+ // NONE is settable.
+ assertSettableFunctions(true, UsbManager.FUNCTION_NONE);
+
+ // MTP, PTP, RNDIS, MIDI, NCM are all settable by themselves.
+ assertSettableFunctions(true, UsbManager.FUNCTION_MTP);
+ assertSettableFunctions(true, UsbManager.FUNCTION_PTP);
+ assertSettableFunctions(true, UsbManager.FUNCTION_RNDIS);
+ assertSettableFunctions(true, UsbManager.FUNCTION_MIDI);
+ assertSettableFunctions(true, UsbManager.FUNCTION_NCM);
+
+ // Setting two functions at the same time is not allowed...
+ assertSettableFunctions(false, UsbManager.FUNCTION_MTP | UsbManager.FUNCTION_PTP);
+ assertSettableFunctions(false, UsbManager.FUNCTION_PTP | UsbManager.FUNCTION_RNDIS);
+ assertSettableFunctions(false, UsbManager.FUNCTION_MIDI | UsbManager.FUNCTION_NCM);
+
+ // ... except in the special case of RNDIS and NCM.
+ assertSettableFunctions(true, UsbManager.FUNCTION_RNDIS | UsbManager.FUNCTION_NCM);
+ }
}
diff --git a/tests/net/Android.bp b/tests/net/Android.bp
index f6a2846..ffde68e 100644
--- a/tests/net/Android.bp
+++ b/tests/net/Android.bp
@@ -36,7 +36,7 @@
"libvndksupport",
"libziparchive",
"libz",
- "netd_aidl_interface-cpp",
+ "netd_aidl_interface-V5-cpp",
],
}
@@ -53,6 +53,7 @@
jarjar_rules: "jarjar-rules.txt",
static_libs: [
"androidx.test.rules",
+ "bouncycastle-repackaged-unbundled",
"FrameworksNetCommonTests",
"frameworks-base-testutils",
"frameworks-net-integration-testutils",
diff --git a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java b/tests/net/common/java/android/net/OemNetworkPreferencesTest.java
index cade5ba..d232a507 100644
--- a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java
+++ b/tests/net/common/java/android/net/OemNetworkPreferencesTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,22 +20,20 @@
import static com.android.testutils.ParcelUtils.assertParcelSane;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.os.Build;
-import android.util.SparseArray;
import androidx.test.filters.SmallTest;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.DevSdkIgnoreRunner;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Map;
@IgnoreUpTo(Build.VERSION_CODES.R)
@RunWith(DevSdkIgnoreRunner.class)
@@ -45,51 +43,51 @@
private static final int TEST_PREF = OemNetworkPreferences.OEM_NETWORK_PREFERENCE_DEFAULT;
private static final String TEST_PACKAGE = "com.google.apps.contacts";
- private final List<String> mPackages = new ArrayList<>();
private final OemNetworkPreferences.Builder mBuilder = new OemNetworkPreferences.Builder();
- @Before
- public void beforeEachTestMethod() {
- mPackages.add(TEST_PACKAGE);
- }
-
@Test
- public void builderAddNetworkPreferenceRequiresNonNullPackages() {
+ public void testBuilderAddNetworkPreferenceRequiresNonNullPackageName() {
assertThrows(NullPointerException.class,
- () -> mBuilder.addNetworkPreference(TEST_PREF, null));
+ () -> mBuilder.addNetworkPreference(null, TEST_PREF));
}
@Test
- public void getNetworkPreferencesReturnsCorrectValue() {
- final int expectedNumberOfMappings = 1;
- mBuilder.addNetworkPreference(TEST_PREF, mPackages);
+ public void testBuilderRemoveNetworkPreferenceRequiresNonNullPackageName() {
+ assertThrows(NullPointerException.class,
+ () -> mBuilder.removeNetworkPreference(null));
+ }
- final SparseArray<List<String>> networkPreferences =
+ @Test
+ public void testGetNetworkPreferenceReturnsCorrectValue() {
+ final int expectedNumberOfMappings = 1;
+ mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
+
+ final Map<String, Integer> networkPreferences =
mBuilder.build().getNetworkPreferences();
assertEquals(expectedNumberOfMappings, networkPreferences.size());
- assertEquals(mPackages.size(), networkPreferences.get(TEST_PREF).size());
- assertEquals(mPackages.get(0), networkPreferences.get(TEST_PREF).get(0));
+ assertTrue(networkPreferences.containsKey(TEST_PACKAGE));
}
@Test
- public void getNetworkPreferencesReturnsUnmodifiableValue() {
+ public void testGetNetworkPreferenceReturnsUnmodifiableValue() {
final String newPackage = "new.com.google.apps.contacts";
- mBuilder.addNetworkPreference(TEST_PREF, mPackages);
+ mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
- final SparseArray<List<String>> networkPreferences =
+ final Map<String, Integer> networkPreferences =
mBuilder.build().getNetworkPreferences();
assertThrows(UnsupportedOperationException.class,
- () -> networkPreferences.get(TEST_PREF).set(mPackages.size() - 1, newPackage));
+ () -> networkPreferences.put(newPackage, TEST_PREF));
assertThrows(UnsupportedOperationException.class,
- () -> networkPreferences.get(TEST_PREF).add(newPackage));
+ () -> networkPreferences.remove(TEST_PACKAGE));
+
}
@Test
- public void toStringReturnsCorrectValue() {
- mBuilder.addNetworkPreference(TEST_PREF, mPackages);
+ public void testToStringReturnsCorrectValue() {
+ mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
final String networkPreferencesString = mBuilder.build().getNetworkPreferences().toString();
@@ -99,10 +97,56 @@
@Test
public void testOemNetworkPreferencesParcelable() {
- mBuilder.addNetworkPreference(TEST_PREF, mPackages);
+ mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
final OemNetworkPreferences prefs = mBuilder.build();
assertParcelSane(prefs, 1 /* fieldCount */);
}
+
+ @Test
+ public void testAddNetworkPreferenceOverwritesPriorPreference() {
+ final int newPref = OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID;
+ mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
+ Map<String, Integer> networkPreferences =
+ mBuilder.build().getNetworkPreferences();
+
+ assertTrue(networkPreferences.containsKey(TEST_PACKAGE));
+ assertEquals(networkPreferences.get(TEST_PACKAGE).intValue(), TEST_PREF);
+
+ mBuilder.addNetworkPreference(TEST_PACKAGE, newPref);
+ networkPreferences = mBuilder.build().getNetworkPreferences();
+
+ assertTrue(networkPreferences.containsKey(TEST_PACKAGE));
+ assertEquals(networkPreferences.get(TEST_PACKAGE).intValue(), newPref);
+ }
+
+ @Test
+ public void testRemoveNetworkPreferenceRemovesValue() {
+ mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
+ Map<String, Integer> networkPreferences =
+ mBuilder.build().getNetworkPreferences();
+
+ assertTrue(networkPreferences.containsKey(TEST_PACKAGE));
+
+ mBuilder.removeNetworkPreference(TEST_PACKAGE);
+ networkPreferences = mBuilder.build().getNetworkPreferences();
+
+ assertFalse(networkPreferences.containsKey(TEST_PACKAGE));
+ }
+
+ @Test
+ public void testConstructorByOemNetworkPreferencesSetsValue() {
+ mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
+ OemNetworkPreferences networkPreference = mBuilder.build();
+
+ final Map<String, Integer> networkPreferences =
+ new OemNetworkPreferences
+ .Builder(networkPreference)
+ .build()
+ .getNetworkPreferences();
+
+ assertTrue(networkPreferences.containsKey(TEST_PACKAGE));
+ assertEquals(networkPreferences.get(TEST_PACKAGE).intValue(), TEST_PREF);
+ }
}
diff --git a/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt b/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt
new file mode 100644
index 0000000..87cfb34
--- /dev/null
+++ b/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net
+
+import android.os.Build
+import androidx.test.filters.SmallTest
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRunner
+import com.android.testutils.assertParcelSane
+import org.junit.Test
+import org.junit.runner.RunWith
+import kotlin.test.assertEquals
+
+private const val TEST_OWNER_UID = 123
+private const val TEST_IFACE = "test_tun0"
+private val TEST_IFACE_LIST = listOf("wlan0", "rmnet_data0", "eth0")
+
+@SmallTest
+@RunWith(DevSdkIgnoreRunner::class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+class UnderlyingNetworkInfoTest {
+ @Test
+ fun testParcelUnparcel() {
+ val testInfo = UnderlyingNetworkInfo(TEST_OWNER_UID, TEST_IFACE, TEST_IFACE_LIST)
+ assertEquals(TEST_OWNER_UID, testInfo.ownerUid)
+ assertEquals(TEST_IFACE, testInfo.iface)
+ assertEquals(TEST_IFACE_LIST, testInfo.underlyingIfaces)
+ assertParcelSane(testInfo, 3)
+
+ val emptyInfo = UnderlyingNetworkInfo(0, String(), listOf())
+ assertEquals(0, emptyInfo.ownerUid)
+ assertEquals(String(), emptyInfo.iface)
+ assertEquals(listOf(), emptyInfo.underlyingIfaces)
+ assertParcelSane(emptyInfo, 3)
+ }
+}
\ No newline at end of file
diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java
index f2dd27e..c2fddf3 100644
--- a/tests/net/java/android/net/ConnectivityManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityManagerTest.java
@@ -32,6 +32,7 @@
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.NetworkRequest.Type.BACKGROUND_REQUEST;
import static android.net.NetworkRequest.Type.REQUEST;
import static android.net.NetworkRequest.Type.TRACK_DEFAULT;
@@ -368,6 +369,12 @@
eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
eq(testPkgName), eq(null));
reset(mService);
+
+ manager.requestBackgroundNetwork(request, null, callback);
+ verify(mService).requestNetwork(eq(request.networkCapabilities),
+ eq(BACKGROUND_REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
+ eq(testPkgName), eq(null));
+ reset(mService);
}
static Message makeMessage(NetworkRequest req, int messageType) {
diff --git a/tests/net/java/android/net/Ikev2VpnProfileTest.java b/tests/net/java/android/net/Ikev2VpnProfileTest.java
index 076e41d..1abd39a 100644
--- a/tests/net/java/android/net/Ikev2VpnProfileTest.java
+++ b/tests/net/java/android/net/Ikev2VpnProfileTest.java
@@ -30,7 +30,7 @@
import com.android.internal.net.VpnProfile;
import com.android.net.module.util.ProxyUtils;
-import com.android.org.bouncycastle.x509.X509V1CertificateGenerator;
+import com.android.internal.org.bouncycastle.x509.X509V1CertificateGenerator;
import org.junit.Before;
import org.junit.Test;
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index f893e9e..fe19f86 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -64,6 +64,7 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
import static android.net.NetworkCapabilities.NET_CAPABILITY_RCS;
@@ -131,6 +132,7 @@
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.AlarmManager;
import android.app.AppOpsManager;
import android.app.NotificationManager;
@@ -200,6 +202,7 @@
import android.net.SocketKeepalive;
import android.net.UidRange;
import android.net.UidRangeParcel;
+import android.net.UnderlyingNetworkInfo;
import android.net.Uri;
import android.net.VpnManager;
import android.net.metrics.IpConnectivityLog;
@@ -245,12 +248,12 @@
import com.android.internal.app.IBatteryStats;
import com.android.internal.net.VpnConfig;
-import com.android.internal.net.VpnInfo;
import com.android.internal.net.VpnProfile;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.WakeupMessage;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.net.module.util.ArrayTrackRecord;
import com.android.server.ConnectivityService.ConnectivityDiagnosticsCallbackInfo;
import com.android.server.connectivity.ConnectivityConstants;
import com.android.server.connectivity.MockableSystemProperties;
@@ -328,11 +331,12 @@
private static final String TAG = "ConnectivityServiceTest";
private static final int TIMEOUT_MS = 500;
- private static final int TEST_LINGER_DELAY_MS = 300;
- // Chosen to be less than the linger timeout. This ensures that we can distinguish between a
- // LOST callback that arrives immediately and a LOST callback that arrives after the linger
- // timeout. For this, our assertions should run fast enough to leave less than
- // (mService.mLingerDelayMs - TEST_CALLBACK_TIMEOUT_MS) between the time callbacks are
+ private static final int TEST_LINGER_DELAY_MS = 400;
+ private static final int TEST_NASCENT_DELAY_MS = 300;
+ // Chosen to be less than the linger and nascent timeout. This ensures that we can distinguish
+ // between a LOST callback that arrives immediately and a LOST callback that arrives after
+ // the linger/nascent timeout. For this, our assertions should run fast enough to leave
+ // less than (mService.mLingerDelayMs - TEST_CALLBACK_TIMEOUT_MS) between the time callbacks are
// supposedly fired, and the time we call expectCallback.
private static final int TEST_CALLBACK_TIMEOUT_MS = 250;
// Chosen to be less than TEST_CALLBACK_TIMEOUT_MS. This ensures that requests have time to
@@ -379,6 +383,10 @@
private QosCallbackMockHelper mQosCallbackMockHelper;
private QosCallbackTracker mQosCallbackTracker;
+ // State variables required to emulate NetworkPolicyManagerService behaviour.
+ private int mUidRules = RULE_NONE;
+ private boolean mRestrictBackground = false;
+
@Mock DeviceIdleInternal mDeviceIdleInternal;
@Mock INetworkManagementService mNetworkManagementService;
@Mock INetworkStatsService mStatsService;
@@ -901,28 +909,69 @@
}
/**
- * A NetworkFactory that allows tests to wait until any in-flight NetworkRequest add or remove
- * operations have been processed. Before ConnectivityService can add or remove any requests,
- * the factory must be told to expect those operations by calling expectAddRequestsWithScores or
- * expectRemoveRequests.
+ * A NetworkFactory that allows to wait until any in-flight NetworkRequest add or remove
+ * operations have been processed and test for them.
*/
private static class MockNetworkFactory extends NetworkFactory {
private final ConditionVariable mNetworkStartedCV = new ConditionVariable();
private final ConditionVariable mNetworkStoppedCV = new ConditionVariable();
private final AtomicBoolean mNetworkStarted = new AtomicBoolean(false);
- // Used to expect that requests be removed or added on a separate thread, without sleeping.
- // Callers can call either expectAddRequestsWithScores() or expectRemoveRequests() exactly
- // once, then cause some other thread to add or remove requests, then call
- // waitForRequests().
- // It is not possible to wait for both add and remove requests. When adding, the queue
- // contains the expected score. When removing, the value is unused, all matters is the
- // number of objects in the queue.
- private final LinkedBlockingQueue<Integer> mExpectations;
+ static class RequestEntry {
+ @NonNull
+ public final NetworkRequest request;
- // Whether we are currently expecting requests to be added or removed. Valid only if
- // mExpectations is non-empty.
- private boolean mExpectingAdditions;
+ RequestEntry(@NonNull final NetworkRequest request) {
+ this.request = request;
+ }
+
+ static final class Add extends RequestEntry {
+ public final int factorySerialNumber;
+
+ Add(@NonNull final NetworkRequest request, final int factorySerialNumber) {
+ super(request);
+ this.factorySerialNumber = factorySerialNumber;
+ }
+ }
+
+ static final class Remove extends RequestEntry {
+ Remove(@NonNull final NetworkRequest request) {
+ super(request);
+ }
+ }
+ }
+
+ // History of received requests adds and removes.
+ private final ArrayTrackRecord<RequestEntry>.ReadHead mRequestHistory =
+ new ArrayTrackRecord<RequestEntry>().newReadHead();
+
+ private static <T> T failIfNull(@Nullable final T obj, @Nullable final String message) {
+ if (null == obj) fail(null != message ? message : "Must not be null");
+ return obj;
+ }
+
+
+ public RequestEntry.Add expectRequestAdd() {
+ return failIfNull((RequestEntry.Add) mRequestHistory.poll(TIMEOUT_MS,
+ it -> it instanceof RequestEntry.Add), "Expected request add");
+ }
+
+ public void expectRequestAdds(final int count) {
+ for (int i = count; i > 0; --i) {
+ expectRequestAdd();
+ }
+ }
+
+ public RequestEntry.Remove expectRequestRemove() {
+ return failIfNull((RequestEntry.Remove) mRequestHistory.poll(TIMEOUT_MS,
+ it -> it instanceof RequestEntry.Remove), "Expected request remove");
+ }
+
+ public void expectRequestRemoves(final int count) {
+ for (int i = count; i > 0; --i) {
+ expectRequestRemove();
+ }
+ }
// Used to collect the networks requests managed by this factory. This is a duplicate of
// the internal information stored in the NetworkFactory (which is private).
@@ -931,7 +980,6 @@
public MockNetworkFactory(Looper looper, Context context, String logTag,
NetworkCapabilities filter) {
super(looper, context, logTag, filter);
- mExpectations = new LinkedBlockingQueue<>();
}
public int getMyRequestCount() {
@@ -965,95 +1013,33 @@
@Override
protected void handleAddRequest(NetworkRequest request, int score,
int factorySerialNumber) {
- synchronized (mExpectations) {
- final Integer expectedScore = mExpectations.poll(); // null if the queue is empty
-
- assertNotNull("Added more requests than expected (" + request + " score : "
- + score + ")", expectedScore);
- // If we're expecting anything, we must be expecting additions.
- if (!mExpectingAdditions) {
- fail("Can't add requests while expecting requests to be removed");
- }
- if (expectedScore != score) {
- fail("Expected score was " + expectedScore + " but actual was " + score
- + " in added request");
- }
-
- // Add the request.
- mNetworkRequests.put(request.requestId, request);
- super.handleAddRequest(request, score, factorySerialNumber);
- mExpectations.notify();
- }
+ mNetworkRequests.put(request.requestId, request);
+ super.handleAddRequest(request, score, factorySerialNumber);
+ mRequestHistory.add(new RequestEntry.Add(request, factorySerialNumber));
}
@Override
protected void handleRemoveRequest(NetworkRequest request) {
- synchronized (mExpectations) {
- final Integer expectedScore = mExpectations.poll(); // null if the queue is empty
+ mNetworkRequests.remove(request.requestId);
+ super.handleRemoveRequest(request);
+ mRequestHistory.add(new RequestEntry.Remove(request));
+ }
- assertTrue("Removed more requests than expected", expectedScore != null);
- // If we're expecting anything, we must be expecting removals.
- if (mExpectingAdditions) {
- fail("Can't remove requests while expecting requests to be added");
- }
+ public void assertRequestCountEquals(final int count) {
+ assertEquals(count, getMyRequestCount());
+ }
- // Remove the request.
- mNetworkRequests.remove(request.requestId);
- super.handleRemoveRequest(request);
- mExpectations.notify();
- }
+ @Override
+ public void terminate() {
+ super.terminate();
+ // Make sure there are no remaining requests unaccounted for.
+ assertNull(mRequestHistory.poll(TIMEOUT_MS, r -> true));
}
// Trigger releasing the request as unfulfillable
public void triggerUnfulfillable(NetworkRequest r) {
super.releaseRequestAsUnfulfillableByAnyFactory(r);
}
-
- private void assertNoExpectations() {
- if (mExpectations.size() != 0) {
- fail("Can't add expectation, " + mExpectations.size() + " already pending");
- }
- }
-
- // Expects that requests with the specified scores will be added.
- public void expectAddRequestsWithScores(final int... scores) {
- assertNoExpectations();
- mExpectingAdditions = true;
- for (int score : scores) {
- mExpectations.add(score);
- }
- }
-
- // Expects that count requests will be removed.
- public void expectRemoveRequests(final int count) {
- assertNoExpectations();
- mExpectingAdditions = false;
- for (int i = 0; i < count; ++i) {
- mExpectations.add(0); // For removals the score is ignored so any value will do.
- }
- }
-
- // Waits for the expected request additions or removals to happen within a timeout.
- public void waitForRequests() throws InterruptedException {
- final long deadline = SystemClock.elapsedRealtime() + TIMEOUT_MS;
- synchronized (mExpectations) {
- while (mExpectations.size() > 0 && SystemClock.elapsedRealtime() < deadline) {
- mExpectations.wait(deadline - SystemClock.elapsedRealtime());
- }
- }
- final long count = mExpectations.size();
- final String msg = count + " requests still not " +
- (mExpectingAdditions ? "added" : "removed") +
- " after " + TIMEOUT_MS + " ms";
- assertEquals(msg, 0, count);
- }
-
- public SparseArray<NetworkRequest> waitForNetworkRequests(final int count)
- throws InterruptedException {
- waitForRequests();
- assertEquals(count, getMyRequestCount());
- return mNetworkRequests;
- }
}
private Set<UidRange> uidRangesForUid(int uid) {
@@ -1075,7 +1061,7 @@
private boolean mAgentRegistered = false;
private int mVpnType = VpnManager.TYPE_VPN_SERVICE;
- private VpnInfo mVpnInfo;
+ private UnderlyingNetworkInfo mUnderlyingNetworkInfo;
// These ConditionVariables allow tests to wait for LegacyVpnRunner to be stopped/started.
// TODO: this scheme is ad-hoc and error-prone because it does not fail if, for example, the
@@ -1249,14 +1235,15 @@
}
@Override
- public synchronized VpnInfo getVpnInfo() {
- if (mVpnInfo != null) return mVpnInfo;
+ public synchronized UnderlyingNetworkInfo getUnderlyingNetworkInfo() {
+ if (mUnderlyingNetworkInfo != null) return mUnderlyingNetworkInfo;
- return super.getVpnInfo();
+ return super.getUnderlyingNetworkInfo();
}
- private synchronized void setVpnInfo(VpnInfo vpnInfo) {
- mVpnInfo = vpnInfo;
+ private synchronized void setUnderlyingNetworkInfo(
+ UnderlyingNetworkInfo underlyingNetworkInfo) {
+ mUnderlyingNetworkInfo = underlyingNetworkInfo;
}
}
@@ -1276,12 +1263,36 @@
}
}
+ private void processBroadcastForVpn(Intent intent) {
+ // The BroadcastReceiver for this broadcast checks it is being run on the handler thread.
+ final Handler handler = new Handler(mCsHandlerThread.getLooper());
+ handler.post(() -> mServiceContext.sendBroadcast(intent));
+ waitForIdle();
+ }
+
+ private void mockUidNetworkingBlocked() {
+ doAnswer(i -> mContext.getSystemService(NetworkPolicyManager.class)
+ .checkUidNetworkingBlocked(i.getArgument(0) /* uid */, mUidRules,
+ i.getArgument(1) /* metered */, mRestrictBackground)
+ ).when(mNetworkPolicyManager).isUidNetworkingBlocked(anyInt(), anyBoolean());
+
+ doAnswer(inv -> mContext.getSystemService(NetworkPolicyManager.class)
+ .checkUidNetworkingBlocked(inv.getArgument(0) /* uid */,
+ inv.getArgument(1) /* uidRules */,
+ inv.getArgument(2) /* isNetworkMetered */,
+ inv.getArgument(3) /* isBackgroundRestricted */)
+ ).when(mNetworkPolicyManager).checkUidNetworkingBlocked(
+ anyInt(), anyInt(), anyBoolean(), anyBoolean());
+ }
+
private void setUidRulesChanged(int uidRules) throws RemoteException {
- mPolicyListener.onUidRulesChanged(Process.myUid(), uidRules);
+ mUidRules = uidRules;
+ mPolicyListener.onUidRulesChanged(Process.myUid(), mUidRules);
}
private void setRestrictBackgroundChanged(boolean restrictBackground) throws RemoteException {
- mPolicyListener.onRestrictBackgroundChanged(restrictBackground);
+ mRestrictBackground = restrictBackground;
+ mPolicyListener.onRestrictBackgroundChanged(mRestrictBackground);
}
private Nat464Xlat getNat464Xlat(NetworkAgentWrapper mna) {
@@ -1392,6 +1403,7 @@
mMockNetd,
mDeps);
mService.mLingerDelayMs = TEST_LINGER_DELAY_MS;
+ mService.mNascentDelayMs = TEST_NASCENT_DELAY_MS;
verify(mDeps).makeMultinetworkPolicyTracker(any(), any(), any());
final ArgumentCaptor<INetworkPolicyListener> policyListenerCaptor =
@@ -1734,6 +1746,108 @@
verifyNoNetwork();
}
+ /**
+ * Verify a newly created network will be inactive instead of torn down even if no one is
+ * requesting.
+ */
+ @Test
+ public void testNewNetworkInactive() throws Exception {
+ // Create a callback that monitoring the testing network.
+ final TestNetworkCallback listenCallback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(new NetworkRequest.Builder().build(), listenCallback);
+
+ // 1. Create a network that is not requested by anyone, and does not satisfy any of the
+ // default requests. Verify that the network will be inactive instead of torn down.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connectWithoutInternet();
+ listenCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ listenCallback.assertNoCallback();
+
+ // Verify that the network will be torn down after nascent expiry. A small period of time
+ // is added in case of flakiness.
+ final int nascentTimeoutMs =
+ mService.mNascentDelayMs + mService.mNascentDelayMs / 4;
+ listenCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent, nascentTimeoutMs);
+
+ // 2. Create a network that is satisfied by a request comes later.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connectWithoutInternet();
+ listenCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ final NetworkRequest wifiRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_WIFI).build();
+ final TestNetworkCallback wifiCallback = new TestNetworkCallback();
+ mCm.requestNetwork(wifiRequest, wifiCallback);
+ wifiCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+
+ // Verify that the network will be kept since the request is still satisfied. And is able
+ // to get disconnected as usual if the request is released after the nascent timer expires.
+ listenCallback.assertNoCallback(nascentTimeoutMs);
+ mCm.unregisterNetworkCallback(wifiCallback);
+ listenCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+
+ // 3. Create a network that is satisfied by a request comes later.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connectWithoutInternet();
+ listenCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ mCm.requestNetwork(wifiRequest, wifiCallback);
+ wifiCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+
+ // Verify that the network will still be torn down after the request gets removed.
+ mCm.unregisterNetworkCallback(wifiCallback);
+ listenCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+
+ // There is no need to ensure that LOSING is never sent in the common case that the
+ // network immediately satisfies a request that was already present, because it is already
+ // verified anywhere whenever {@code TestNetworkCallback#expectAvailable*} is called.
+
+ mCm.unregisterNetworkCallback(listenCallback);
+ }
+
+ /**
+ * Verify a newly created network will be inactive and switch to background if only background
+ * request is satisfied.
+ */
+ @Test
+ public void testNewNetworkInactive_bgNetwork() throws Exception {
+ // Create a callback that monitoring the wifi network.
+ final TestNetworkCallback wifiListenCallback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_WIFI).build(), wifiListenCallback);
+
+ // Create callbacks that can monitor background and foreground mobile networks.
+ // This is done by granting using background networks permission before registration. Thus,
+ // the service will not add {@code NET_CAPABILITY_FOREGROUND} by default.
+ grantUsingBackgroundNetworksPermissionForUid(Binder.getCallingUid());
+ final TestNetworkCallback bgMobileListenCallback = new TestNetworkCallback();
+ final TestNetworkCallback fgMobileListenCallback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR).build(), bgMobileListenCallback);
+ mCm.registerNetworkCallback(new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .addCapability(NET_CAPABILITY_FOREGROUND).build(), fgMobileListenCallback);
+
+ // Connect wifi, which satisfies default request.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(true);
+ wifiListenCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
+
+ // Connect a cellular network, verify that satisfies only the background callback.
+ setAlwaysOnNetworks(true);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+ bgMobileListenCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ fgMobileListenCallback.assertNoCallback();
+ assertFalse(isForegroundNetwork(mCellNetworkAgent));
+
+ mCellNetworkAgent.disconnect();
+ bgMobileListenCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ fgMobileListenCallback.assertNoCallback();
+
+ mCm.unregisterNetworkCallback(wifiListenCallback);
+ mCm.unregisterNetworkCallback(bgMobileListenCallback);
+ mCm.unregisterNetworkCallback(fgMobileListenCallback);
+ }
+
@Test
public void testValidatedCellularOutscoresUnvalidatedWiFi() throws Exception {
// Test bringing up unvalidated WiFi
@@ -2578,12 +2692,6 @@
callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
}
- private int[] makeIntArray(final int size, final int value) {
- final int[] array = new int[size];
- Arrays.fill(array, value);
- return array;
- }
-
private void tryNetworkFactoryRequests(int capability) throws Exception {
// Verify NOT_RESTRICTED is set appropriately
final NetworkCapabilities nc = new NetworkRequest.Builder().addCapability(capability)
@@ -2605,9 +2713,9 @@
mServiceContext, "testFactory", filter);
testFactory.setScoreFilter(40);
ConditionVariable cv = testFactory.getNetworkStartedCV();
- testFactory.expectAddRequestsWithScores(0);
testFactory.register();
- testFactory.waitForNetworkRequests(1);
+ testFactory.expectRequestAdd();
+ testFactory.assertRequestCountEquals(1);
int expectedRequestCount = 1;
NetworkCallback networkCallback = null;
// For non-INTERNET capabilities we cannot rely on the default request being present, so
@@ -2616,13 +2724,12 @@
assertFalse(testFactory.getMyStartRequested());
NetworkRequest request = new NetworkRequest.Builder().addCapability(capability).build();
networkCallback = new NetworkCallback();
- testFactory.expectAddRequestsWithScores(0); // New request
mCm.requestNetwork(request, networkCallback);
expectedRequestCount++;
- testFactory.waitForNetworkRequests(expectedRequestCount);
+ testFactory.expectRequestAdd();
}
waitFor(cv);
- assertEquals(expectedRequestCount, testFactory.getMyRequestCount());
+ testFactory.assertRequestCountEquals(expectedRequestCount);
assertTrue(testFactory.getMyStartRequested());
// Now bring in a higher scored network.
@@ -2636,15 +2743,14 @@
// When testAgent connects, ConnectivityService will re-send us all current requests with
// the new score. There are expectedRequestCount such requests, and we must wait for all of
// them.
- testFactory.expectAddRequestsWithScores(makeIntArray(expectedRequestCount, 50));
testAgent.connect(false);
testAgent.addCapability(capability);
waitFor(cv);
- testFactory.waitForNetworkRequests(expectedRequestCount);
+ testFactory.expectRequestAdds(expectedRequestCount);
+ testFactory.assertRequestCountEquals(expectedRequestCount);
assertFalse(testFactory.getMyStartRequested());
// Bring in a bunch of requests.
- testFactory.expectAddRequestsWithScores(makeIntArray(10, 50));
assertEquals(expectedRequestCount, testFactory.getMyRequestCount());
ConnectivityManager.NetworkCallback[] networkCallbacks =
new ConnectivityManager.NetworkCallback[10];
@@ -2654,24 +2760,24 @@
builder.addCapability(capability);
mCm.requestNetwork(builder.build(), networkCallbacks[i]);
}
- testFactory.waitForNetworkRequests(10 + expectedRequestCount);
+ testFactory.expectRequestAdds(10);
+ testFactory.assertRequestCountEquals(10 + expectedRequestCount);
assertFalse(testFactory.getMyStartRequested());
// Remove the requests.
- testFactory.expectRemoveRequests(10);
for (int i = 0; i < networkCallbacks.length; i++) {
mCm.unregisterNetworkCallback(networkCallbacks[i]);
}
- testFactory.waitForNetworkRequests(expectedRequestCount);
+ testFactory.expectRequestRemoves(10);
+ testFactory.assertRequestCountEquals(expectedRequestCount);
assertFalse(testFactory.getMyStartRequested());
// Drop the higher scored network.
cv = testFactory.getNetworkStartedCV();
- // With the default network disconnecting, the requests are sent with score 0 to factories.
- testFactory.expectAddRequestsWithScores(makeIntArray(expectedRequestCount, 0));
testAgent.disconnect();
waitFor(cv);
- testFactory.waitForNetworkRequests(expectedRequestCount);
+ testFactory.expectRequestAdds(expectedRequestCount);
+ testFactory.assertRequestCountEquals(expectedRequestCount);
assertEquals(expectedRequestCount, testFactory.getMyRequestCount());
assertTrue(testFactory.getMyStartRequested());
@@ -2714,9 +2820,8 @@
final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
mServiceContext, "testFactory", filter);
// Register the factory and don't be surprised when the default request arrives.
- testFactory.expectAddRequestsWithScores(0);
testFactory.register();
- testFactory.waitForNetworkRequests(1);
+ testFactory.expectRequestAdd();
testFactory.setScoreFilter(42);
testFactory.terminate();
@@ -3692,10 +3797,13 @@
@Test
public void testBackgroundNetworks() throws Exception {
- // Create a background request. We can't do this ourselves because ConnectivityService
- // doesn't have an API for it. So just turn on mobile data always on.
- setAlwaysOnNetworks(true);
+ // Create a cellular background request.
grantUsingBackgroundNetworksPermissionForUid(Binder.getCallingUid());
+ final TestNetworkCallback cellBgCallback = new TestNetworkCallback();
+ mCm.requestBackgroundNetwork(new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR).build(), null, cellBgCallback);
+
+ // Make callbacks for monitoring.
final NetworkRequest request = new NetworkRequest.Builder().build();
final NetworkRequest fgRequest = new NetworkRequest.Builder()
.addCapability(NET_CAPABILITY_FOREGROUND).build();
@@ -3764,6 +3872,7 @@
mCm.unregisterNetworkCallback(callback);
mCm.unregisterNetworkCallback(fgCallback);
+ mCm.unregisterNetworkCallback(cellBgCallback);
}
@Ignore // This test has instrinsic chances of spurious failures: ignore for continuous testing.
@@ -3855,38 +3964,37 @@
testFactory.setScoreFilter(40);
// Register the factory and expect it to start looking for a network.
- testFactory.expectAddRequestsWithScores(0); // Score 0 as the request is not served yet.
testFactory.register();
try {
- testFactory.waitForNetworkRequests(1);
+ testFactory.expectRequestAdd();
+ testFactory.assertRequestCountEquals(1);
assertTrue(testFactory.getMyStartRequested());
// Bring up wifi. The factory stops looking for a network.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
// Score 60 - 40 penalty for not validated yet, then 60 when it validates
- testFactory.expectAddRequestsWithScores(20, 60);
mWiFiNetworkAgent.connect(true);
- testFactory.waitForRequests();
+ // Default request and mobile always on request
+ testFactory.expectRequestAdds(2);
assertFalse(testFactory.getMyStartRequested());
- ContentResolver cr = mServiceContext.getContentResolver();
-
// Turn on mobile data always on. The factory starts looking again.
- testFactory.expectAddRequestsWithScores(0); // Always on requests comes up with score 0
setAlwaysOnNetworks(true);
- testFactory.waitForNetworkRequests(2);
+ testFactory.expectRequestAdd();
+ testFactory.assertRequestCountEquals(2);
+
assertTrue(testFactory.getMyStartRequested());
// Bring up cell data and check that the factory stops looking.
assertLength(1, mCm.getAllNetworks());
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
- testFactory.expectAddRequestsWithScores(10, 50); // Unvalidated, then validated
mCellNetworkAgent.connect(true);
cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
- testFactory.waitForNetworkRequests(2);
- assertFalse(
- testFactory.getMyStartRequested()); // Because the cell network outscores us.
+ testFactory.expectRequestAdds(2); // Unvalidated and validated
+ testFactory.assertRequestCountEquals(2);
+ // The cell network outscores the factory filter, so start is not requested.
+ assertFalse(testFactory.getMyStartRequested());
// Check that cell data stays up.
waitForIdle();
@@ -3894,12 +4002,12 @@
assertLength(2, mCm.getAllNetworks());
// Turn off mobile data always on and expect the request to disappear...
- testFactory.expectRemoveRequests(1);
setAlwaysOnNetworks(false);
- testFactory.waitForNetworkRequests(1);
+ testFactory.expectRequestRemove();
- // ... and cell data to be torn down.
- cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ // ... and cell data to be torn down after nascent network timeout.
+ cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent,
+ mService.mNascentDelayMs + TEST_CALLBACK_TIMEOUT_MS);
assertLength(1, mCm.getAllNetworks());
} finally {
testFactory.terminate();
@@ -4203,46 +4311,33 @@
testFactory.setScoreFilter(40);
// Register the factory and expect it to receive the default request.
- testFactory.expectAddRequestsWithScores(0);
testFactory.register();
- SparseArray<NetworkRequest> requests = testFactory.waitForNetworkRequests(1);
-
- assertEquals(1, requests.size()); // have 1 request at this point
- int origRequestId = requests.valueAt(0).requestId;
+ testFactory.expectRequestAdd();
// Now file the test request and expect it.
- testFactory.expectAddRequestsWithScores(0);
mCm.requestNetwork(nr, networkCallback);
- requests = testFactory.waitForNetworkRequests(2); // have 2 requests at this point
+ final NetworkRequest newRequest = testFactory.expectRequestAdd().request;
- int newRequestId = 0;
- for (int i = 0; i < requests.size(); ++i) {
- if (requests.valueAt(i).requestId != origRequestId) {
- newRequestId = requests.valueAt(i).requestId;
- break;
- }
- }
-
- testFactory.expectRemoveRequests(1);
if (preUnregister) {
mCm.unregisterNetworkCallback(networkCallback);
// Simulate the factory releasing the request as unfulfillable: no-op since
// the callback has already been unregistered (but a test that no exceptions are
// thrown).
- testFactory.triggerUnfulfillable(requests.get(newRequestId));
+ testFactory.triggerUnfulfillable(newRequest);
} else {
// Simulate the factory releasing the request as unfulfillable and expect onUnavailable!
- testFactory.triggerUnfulfillable(requests.get(newRequestId));
+ testFactory.triggerUnfulfillable(newRequest);
networkCallback.expectCallback(CallbackEntry.UNAVAILABLE, (Network) null);
- testFactory.waitForRequests();
// unregister network callback - a no-op (since already freed by the
// on-unavailable), but should not fail or throw exceptions.
mCm.unregisterNetworkCallback(networkCallback);
}
+ testFactory.expectRequestRemove();
+
testFactory.terminate();
handlerThread.quit();
}
@@ -5194,20 +5289,22 @@
private void expectForceUpdateIfaces(Network[] networks, String defaultIface,
Integer vpnUid, String vpnIfname, String[] underlyingIfaces) throws Exception {
ArgumentCaptor<Network[]> networksCaptor = ArgumentCaptor.forClass(Network[].class);
- ArgumentCaptor<VpnInfo[]> vpnInfosCaptor = ArgumentCaptor.forClass(VpnInfo[].class);
+ ArgumentCaptor<UnderlyingNetworkInfo[]> vpnInfosCaptor = ArgumentCaptor.forClass(
+ UnderlyingNetworkInfo[].class);
verify(mStatsService, atLeastOnce()).forceUpdateIfaces(networksCaptor.capture(),
any(NetworkState[].class), eq(defaultIface), vpnInfosCaptor.capture());
assertSameElementsNoDuplicates(networksCaptor.getValue(), networks);
- VpnInfo[] infos = vpnInfosCaptor.getValue();
+ UnderlyingNetworkInfo[] infos = vpnInfosCaptor.getValue();
if (vpnUid != null) {
assertEquals("Should have exactly one VPN:", 1, infos.length);
- VpnInfo info = infos[0];
+ UnderlyingNetworkInfo info = infos[0];
assertEquals("Unexpected VPN owner:", (int) vpnUid, info.ownerUid);
- assertEquals("Unexpected VPN interface:", vpnIfname, info.vpnIface);
- assertSameElementsNoDuplicates(underlyingIfaces, info.underlyingIfaces);
+ assertEquals("Unexpected VPN interface:", vpnIfname, info.iface);
+ assertSameElementsNoDuplicates(underlyingIfaces,
+ info.underlyingIfaces.toArray(new String[0]));
} else {
assertEquals(0, infos.length);
return;
@@ -5268,7 +5365,7 @@
waitForIdle();
verify(mStatsService, never())
.forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
- eq(new VpnInfo[0]));
+ eq(new UnderlyingNetworkInfo[0]));
reset(mStatsService);
// Roaming change should update ifaces
@@ -5315,20 +5412,20 @@
// MOBILE_IFNAME even though the default network is wifi.
// TODO: fix this to pass in the actual default network interface. Whether or not the VPN
// applies to the system server UID should not have any bearing on network stats.
- mService.setUnderlyingNetworksForVpn(onlyCell);
+ mMockVpn.setUnderlyingNetworks(onlyCell);
waitForIdle();
expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
new String[]{MOBILE_IFNAME});
reset(mStatsService);
- mService.setUnderlyingNetworksForVpn(cellAndWifi);
+ mMockVpn.setUnderlyingNetworks(cellAndWifi);
waitForIdle();
expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
new String[]{MOBILE_IFNAME, WIFI_IFNAME});
reset(mStatsService);
// Null underlying networks are ignored.
- mService.setUnderlyingNetworksForVpn(cellNullAndWifi);
+ mMockVpn.setUnderlyingNetworks(cellNullAndWifi);
waitForIdle();
expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
new String[]{MOBILE_IFNAME, WIFI_IFNAME});
@@ -5351,8 +5448,8 @@
// network for the VPN...
verify(mStatsService, never()).forceUpdateIfaces(any(Network[].class),
any(NetworkState[].class), any() /* anyString() doesn't match null */,
- argThat(infos -> infos[0].underlyingIfaces.length == 1
- && WIFI_IFNAME.equals(infos[0].underlyingIfaces[0])));
+ argThat(infos -> infos[0].underlyingIfaces.size() == 1
+ && WIFI_IFNAME.equals(infos[0].underlyingIfaces.get(0))));
verifyNoMoreInteractions(mStatsService);
reset(mStatsService);
@@ -5365,8 +5462,8 @@
waitForIdle();
verify(mStatsService).forceUpdateIfaces(any(Network[].class),
any(NetworkState[].class), any() /* anyString() doesn't match null */,
- argThat(vpnInfos -> vpnInfos[0].underlyingIfaces.length == 1
- && WIFI_IFNAME.equals(vpnInfos[0].underlyingIfaces[0])));
+ argThat(vpnInfos -> vpnInfos[0].underlyingIfaces.size() == 1
+ && WIFI_IFNAME.equals(vpnInfos[0].underlyingIfaces.get(0))));
mEthernetNetworkAgent.disconnect();
waitForIdle();
reset(mStatsService);
@@ -5377,25 +5474,25 @@
// is probably a performance improvement (though it's very unlikely that a VPN would declare
// no underlying networks).
// Also, for the same reason as above, the active interface passed in is null.
- mService.setUnderlyingNetworksForVpn(new Network[0]);
+ mMockVpn.setUnderlyingNetworks(new Network[0]);
waitForIdle();
expectForceUpdateIfaces(wifiAndVpn, null);
reset(mStatsService);
// Specifying only a null underlying network is the same as no networks.
- mService.setUnderlyingNetworksForVpn(onlyNull);
+ mMockVpn.setUnderlyingNetworks(onlyNull);
waitForIdle();
expectForceUpdateIfaces(wifiAndVpn, null);
reset(mStatsService);
// Specifying networks that are all disconnected is the same as specifying no networks.
- mService.setUnderlyingNetworksForVpn(onlyCell);
+ mMockVpn.setUnderlyingNetworks(onlyCell);
waitForIdle();
expectForceUpdateIfaces(wifiAndVpn, null);
reset(mStatsService);
// Passing in null again means follow the default network again.
- mService.setUnderlyingNetworksForVpn(null);
+ mMockVpn.setUnderlyingNetworks(null);
waitForIdle();
expectForceUpdateIfaces(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME,
new String[]{WIFI_IFNAME});
@@ -5870,7 +5967,7 @@
mMockVpn.establishForMyUid(false, true, false);
assertUidRangesUpdatedForMyUid(true);
final Network wifiNetwork = new Network(mNetIdManager.peekNextNetId());
- mService.setUnderlyingNetworksForVpn(new Network[]{wifiNetwork});
+ mMockVpn.setUnderlyingNetworks(new Network[]{wifiNetwork});
callback.expectAvailableCallbacksUnvalidated(mMockVpn);
assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
.hasTransport(TRANSPORT_VPN));
@@ -5963,23 +6060,18 @@
callback.expectCapabilitiesThat(mMockVpn,
nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
&& nc.hasTransport(TRANSPORT_WIFI));
-
- // BUG: the VPN is no longer suspended, so a RESUMED callback should have been sent.
- // callback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
+ callback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
callback.assertNoCallback();
assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
- assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED); // BUG: VPN caps have NOT_SUSPENDED.
+ assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
- // BUG: the device has connectivity, so this should return true.
- assertGetNetworkInfoOfGetActiveNetworkIsConnected(false);
+ assertGetNetworkInfoOfGetActiveNetworkIsConnected(true);
- // Unsuspend cellular and then switch back to it.
- // The same bug happens in the opposite direction: the VPN's capabilities correctly have
- // NOT_SUSPENDED, but the VPN's NetworkInfo is in state SUSPENDED.
+ // Unsuspend cellular and then switch back to it. The VPN remains not suspended.
mCellNetworkAgent.resume();
callback.assertNoCallback();
mWiFiNetworkAgent.disconnect();
@@ -5996,12 +6088,11 @@
.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
- assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED); // BUG: VPN caps have NOT_SUSPENDED.
+ assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
- // BUG: the device has connectivity, so this should return true.
- assertGetNetworkInfoOfGetActiveNetworkIsConnected(false);
+ assertGetNetworkInfoOfGetActiveNetworkIsConnected(true);
- // Re-suspending the current network fixes the problem.
+ // Suspend cellular and expect no connectivity.
mCellNetworkAgent.suspend();
callback.expectCapabilitiesThat(mMockVpn,
nc -> !nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
@@ -6017,6 +6108,7 @@
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED);
assertGetNetworkInfoOfGetActiveNetworkIsConnected(false);
+ // Resume cellular and expect that connectivity comes back.
mCellNetworkAgent.resume();
callback.expectCapabilitiesThat(mMockVpn,
nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
@@ -6069,7 +6161,7 @@
final Set<UidRange> ranges = uidRangesForUid(uid);
mMockVpn.registerAgent(ranges);
- mService.setUnderlyingNetworksForVpn(new Network[0]);
+ mMockVpn.setUnderlyingNetworks(new Network[0]);
// VPN networks do not satisfy the default request and are automatically validated
// by NetworkMonitor
@@ -6317,7 +6409,7 @@
mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
mCellNetworkAgent.connect(true);
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mCellNetworkAgent.getNetwork() });
vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6332,7 +6424,7 @@
mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
mWiFiNetworkAgent.connect(true);
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6343,7 +6435,7 @@
assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
// Don't disconnect, but note the VPN is not using wifi any more.
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mCellNetworkAgent.getNetwork() });
vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6374,7 +6466,7 @@
vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
// Use Wifi but not cell. Note the VPN is now unmetered and not suspended.
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mWiFiNetworkAgent.getNetwork() });
vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6385,7 +6477,7 @@
assertDefaultNetworkCapabilities(userId, mWiFiNetworkAgent);
// Use both again.
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6400,21 +6492,18 @@
vpnNetworkCallback.assertNoCallback();
// Stop using WiFi. The VPN is suspended again.
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mCellNetworkAgent.getNetwork() });
vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
&& !caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
- // While the SUSPENDED callback should in theory be sent here, it is not. This is
- // a bug in ConnectivityService, but as the SUSPENDED and RESUMED callbacks have never
- // been public and are deprecated and slated for removal, there is no sense in spending
- // resources fixing this bug now.
+ vpnNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn);
assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
// Use both again.
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6422,8 +6511,7 @@
&& caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
&& caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
- // As above, the RESUMED callback not being sent here is a bug, but not a bug that's
- // worth anybody's time to fix.
+ vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
// Disconnect cell. Receive update without even removing the dead network from the
@@ -6550,9 +6638,7 @@
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
// Send a USER_ADDED broadcast for it.
- // The BroadcastReceiver for this broadcast checks that is being run on the handler thread.
- final Handler handler = new Handler(mCsHandlerThread.getLooper());
- handler.post(() -> mServiceContext.sendBroadcast(addedIntent));
+ processBroadcastForVpn(addedIntent);
// Expect that the VPN UID ranges contain both |uid| and the UID range for the newly-added
// restricted user.
@@ -6576,7 +6662,7 @@
// Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user.
final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
- handler.post(() -> mServiceContext.sendBroadcast(removedIntent));
+ processBroadcastForVpn(removedIntent);
// Expect that the VPN gains the UID range for the restricted user, and that the capability
// change made just before that (i.e., loss of TRANSPORT_WIFI) is preserved.
@@ -6633,9 +6719,7 @@
// TODO: check that VPN app within restricted profile still has access, etc.
final Intent addedIntent = new Intent(ACTION_USER_ADDED);
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
- final Handler handler = new Handler(mCsHandlerThread.getLooper());
- handler.post(() -> mServiceContext.sendBroadcast(addedIntent));
- waitForIdle();
+ processBroadcastForVpn(addedIntent);
assertNull(mCm.getActiveNetworkForUid(uid));
assertNull(mCm.getActiveNetworkForUid(restrictedUid));
@@ -6645,8 +6729,7 @@
// Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user.
final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
- handler.post(() -> mServiceContext.sendBroadcast(removedIntent));
- waitForIdle();
+ processBroadcastForVpn(removedIntent);
assertNull(mCm.getActiveNetworkForUid(uid));
assertNotNull(mCm.getActiveNetworkForUid(restrictedUid));
@@ -6748,7 +6831,7 @@
// Ensure VPN is now the active network.
assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
// VPN is using Cell
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mCellNetworkAgent.getNetwork() });
waitForIdle();
@@ -6756,7 +6839,7 @@
assertTrue(mCm.isActiveNetworkMetered());
// VPN is now using WiFi
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mWiFiNetworkAgent.getNetwork() });
waitForIdle();
@@ -6764,7 +6847,7 @@
assertFalse(mCm.isActiveNetworkMetered());
// VPN is using Cell | WiFi.
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
waitForIdle();
@@ -6772,7 +6855,7 @@
assertTrue(mCm.isActiveNetworkMetered());
// VPN is using WiFi | Cell.
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mWiFiNetworkAgent.getNetwork(), mCellNetworkAgent.getNetwork() });
waitForIdle();
@@ -6780,7 +6863,7 @@
assertTrue(mCm.isActiveNetworkMetered());
// VPN is not using any underlying networks.
- mService.setUnderlyingNetworksForVpn(new Network[0]);
+ mMockVpn.setUnderlyingNetworks(new Network[0]);
waitForIdle();
// VPN without underlying networks is treated as metered.
@@ -6807,7 +6890,7 @@
assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
// VPN is tracking current platform default (WiFi).
- mService.setUnderlyingNetworksForVpn(null);
+ mMockVpn.setUnderlyingNetworks(null);
waitForIdle();
// Despite VPN using WiFi (which is unmetered), VPN itself is marked as always metered.
@@ -6815,7 +6898,7 @@
// VPN explicitly declares WiFi as its underlying network.
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mWiFiNetworkAgent.getNetwork() });
waitForIdle();
@@ -6839,13 +6922,20 @@
.addTransportType(TRANSPORT_CELLULAR)
.build();
mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
+ mockUidNetworkingBlocked();
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
setUidRulesChanged(RULE_REJECT_ALL);
cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+ assertNull(mCm.getActiveNetwork());
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
// ConnectivityService should cache it not to invoke the callback again.
setUidRulesChanged(RULE_REJECT_METERED);
@@ -6853,32 +6943,60 @@
setUidRulesChanged(RULE_NONE);
cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+ assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
setUidRulesChanged(RULE_REJECT_METERED);
cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+ assertNull(mCm.getActiveNetwork());
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
// Restrict the network based on UID rule and NOT_METERED capability change.
mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
cellNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_METERED, mCellNetworkAgent);
cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+ assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+
mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED);
cellNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED,
mCellNetworkAgent);
cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+ assertNull(mCm.getActiveNetwork());
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+
setUidRulesChanged(RULE_ALLOW_METERED);
cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+ assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
setUidRulesChanged(RULE_NONE);
cellNetworkCallback.assertNoCallback();
- // Restrict the network based on BackgroundRestricted.
+ // Restrict background data. Networking is not blocked because the network is unmetered.
setRestrictBackgroundChanged(true);
cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+ assertNull(mCm.getActiveNetwork());
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
setRestrictBackgroundChanged(true);
cellNetworkCallback.assertNoCallback();
- setRestrictBackgroundChanged(false);
+
+ setUidRulesChanged(RULE_ALLOW_METERED);
cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+
+ setRestrictBackgroundChanged(false);
cellNetworkCallback.assertNoCallback();
+ assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
+ assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
mCm.unregisterNetworkCallback(cellNetworkCallback);
}
@@ -6887,6 +7005,7 @@
public void testNetworkBlockedStatusBeforeAndAfterConnect() throws Exception {
final TestNetworkCallback defaultCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(defaultCallback);
+ mockUidNetworkingBlocked();
// No Networkcallbacks invoked before any network is active.
setUidRulesChanged(RULE_REJECT_ALL);
@@ -7156,6 +7275,13 @@
when(mKeyStore.get(Credentials.VPN + profileName)).thenReturn(encodedProfile);
}
+ private void establishLegacyLockdownVpn() throws Exception {
+ // The legacy lockdown VPN only supports userId 0.
+ final Set<UidRange> ranges = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
+ mMockVpn.registerAgent(ranges);
+ mMockVpn.connect(true);
+ }
+
@Test
public void testLegacyLockdownVpn() throws Exception {
mServiceContext.setPermission(
@@ -7180,9 +7306,7 @@
final int userId = UserHandle.getUserId(Process.myUid());
final Intent addedIntent = new Intent(ACTION_USER_UNLOCKED);
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- final Handler handler = new Handler(mCsHandlerThread.getLooper());
- handler.post(() -> mServiceContext.sendBroadcast(addedIntent));
- waitForIdle();
+ processBroadcastForVpn(addedIntent);
// Lockdown VPN disables teardown and enables lockdown.
assertFalse(mMockVpn.getEnableTeardown());
@@ -7250,22 +7374,30 @@
mMockVpn.expectStartLegacyVpnRunner();
b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED);
ExpectedBroadcast b2 = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED);
- mMockVpn.establishForMyUid();
+ establishLegacyLockdownVpn();
callback.expectAvailableThenValidatedCallbacks(mMockVpn);
defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
+ NetworkCapabilities vpnNc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
b1.expectBroadcast();
b2.expectBroadcast();
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+ assertTrue(vpnNc.hasTransport(TRANSPORT_VPN));
+ assertTrue(vpnNc.hasTransport(TRANSPORT_CELLULAR));
+ assertFalse(vpnNc.hasTransport(TRANSPORT_WIFI));
+ assertFalse(vpnNc.hasCapability(NET_CAPABILITY_NOT_METERED));
// Switch default network from cell to wifi. Expect VPN to disconnect and reconnect.
final LinkProperties wifiLp = new LinkProperties();
wifiLp.setInterfaceName("wlan0");
wifiLp.addLinkAddress(new LinkAddress("192.0.2.163/25"));
wifiLp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"), null, "wlan0"));
- mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp);
+ final NetworkCapabilities wifiNc = new NetworkCapabilities();
+ wifiNc.addTransportType(TRANSPORT_WIFI);
+ wifiNc.addCapability(NET_CAPABILITY_NOT_METERED);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp, wifiNc);
b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.DISCONNECTED);
// Wifi is CONNECTING because the VPN isn't up yet.
@@ -7298,16 +7430,20 @@
// The VPN comes up again on wifi.
b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED);
b2 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED);
- mMockVpn.establishForMyUid();
+ establishLegacyLockdownVpn();
callback.expectAvailableThenValidatedCallbacks(mMockVpn);
defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
b1.expectBroadcast();
b2.expectBroadcast();
-
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+ vpnNc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
+ assertTrue(vpnNc.hasTransport(TRANSPORT_VPN));
+ assertTrue(vpnNc.hasTransport(TRANSPORT_WIFI));
+ assertFalse(vpnNc.hasTransport(TRANSPORT_CELLULAR));
+ assertTrue(vpnNc.hasCapability(NET_CAPABILITY_NOT_METERED));
// Disconnect cell. Nothing much happens since it's not the default network.
// Whenever LockdownVpnTracker is connected, it will send a connected broadcast any time any
@@ -7335,39 +7471,68 @@
b2.expectBroadcast();
}
+ /**
+ * Test mutable and requestable network capabilities such as
+ * {@link NetworkCapabilities#NET_CAPABILITY_TRUSTED} and
+ * {@link NetworkCapabilities#NET_CAPABILITY_NOT_VCN_MANAGED}. Verify that the
+ * {@code ConnectivityService} re-assign the networks accordingly.
+ */
@Test
- public final void testLoseTrusted() throws Exception {
- final NetworkRequest trustedRequest = new NetworkRequest.Builder()
- .addCapability(NET_CAPABILITY_TRUSTED)
- .build();
- final TestNetworkCallback trustedCallback = new TestNetworkCallback();
- mCm.requestNetwork(trustedRequest, trustedCallback);
+ public final void testLoseMutableAndRequestableCaps() throws Exception {
+ final int[] testCaps = new int [] {
+ NET_CAPABILITY_TRUSTED,
+ NET_CAPABILITY_NOT_VCN_MANAGED
+ };
+ for (final int testCap : testCaps) {
+ // Create requests with and without the testing capability.
+ final TestNetworkCallback callbackWithCap = new TestNetworkCallback();
+ final TestNetworkCallback callbackWithoutCap = new TestNetworkCallback();
+ mCm.requestNetwork(new NetworkRequest.Builder().addCapability(testCap).build(),
+ callbackWithCap);
+ mCm.requestNetwork(new NetworkRequest.Builder().removeCapability(testCap).build(),
+ callbackWithoutCap);
- mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
- mCellNetworkAgent.connect(true);
- trustedCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
- verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
- reset(mMockNetd);
+ // Setup networks with testing capability and verify the default network changes.
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.addCapability(testCap);
+ mCellNetworkAgent.connect(true);
+ callbackWithCap.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ callbackWithoutCap.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
+ reset(mMockNetd);
- mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- mWiFiNetworkAgent.connect(true);
- trustedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
- verify(mMockNetd).networkSetDefault(eq(mWiFiNetworkAgent.getNetwork().netId));
- reset(mMockNetd);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.addCapability(testCap);
+ mWiFiNetworkAgent.connect(true);
+ callbackWithCap.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+ callbackWithoutCap.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+ verify(mMockNetd).networkSetDefault(eq(mWiFiNetworkAgent.getNetwork().netId));
+ reset(mMockNetd);
- mWiFiNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED);
- trustedCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
- verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
- reset(mMockNetd);
+ // Remove the testing capability on wifi, verify the callback and default network
+ // changes back to cellular.
+ mWiFiNetworkAgent.removeCapability(testCap);
+ callbackWithCap.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ callbackWithoutCap.expectCapabilitiesWithout(testCap, mWiFiNetworkAgent);
+ // TODO: Test default network changes for NOT_VCN_MANAGED once the default request has
+ // it.
+ if (testCap == NET_CAPABILITY_TRUSTED) {
+ verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
+ reset(mMockNetd);
+ }
- mCellNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED);
- trustedCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
- verify(mMockNetd).networkClearDefault();
+ mCellNetworkAgent.removeCapability(testCap);
+ callbackWithCap.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ callbackWithoutCap.assertNoCallback();
+ if (testCap == NET_CAPABILITY_TRUSTED) {
+ verify(mMockNetd).networkClearDefault();
+ }
- mCm.unregisterNetworkCallback(trustedCallback);
+ mCm.unregisterNetworkCallback(callbackWithCap);
+ mCm.unregisterNetworkCallback(callbackWithoutCap);
+ }
}
- @Ignore // 40%+ flakiness : figure out why and re-enable.
@Test
public final void testBatteryStatsNetworkType() throws Exception {
final LinkProperties cellLp = new LinkProperties();
@@ -7375,8 +7540,8 @@
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
mCellNetworkAgent.connect(true);
waitForIdle();
- verify(mBatteryStatsService).noteNetworkInterfaceType(cellLp.getInterfaceName(),
- TYPE_MOBILE);
+ verify(mBatteryStatsService).noteNetworkInterfaceForTransports(cellLp.getInterfaceName(),
+ new int[] { TRANSPORT_CELLULAR });
reset(mBatteryStatsService);
final LinkProperties wifiLp = new LinkProperties();
@@ -7384,18 +7549,20 @@
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp);
mWiFiNetworkAgent.connect(true);
waitForIdle();
- verify(mBatteryStatsService).noteNetworkInterfaceType(wifiLp.getInterfaceName(),
- TYPE_WIFI);
+ verify(mBatteryStatsService).noteNetworkInterfaceForTransports(wifiLp.getInterfaceName(),
+ new int[] { TRANSPORT_WIFI });
reset(mBatteryStatsService);
mCellNetworkAgent.disconnect();
+ mWiFiNetworkAgent.disconnect();
cellLp.setInterfaceName("wifi0");
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
mCellNetworkAgent.connect(true);
waitForIdle();
- verify(mBatteryStatsService).noteNetworkInterfaceType(cellLp.getInterfaceName(),
- TYPE_MOBILE);
+ verify(mBatteryStatsService).noteNetworkInterfaceForTransports(cellLp.getInterfaceName(),
+ new int[] { TRANSPORT_CELLULAR });
+ mCellNetworkAgent.disconnect();
}
/**
@@ -7468,8 +7635,8 @@
assertRoutesAdded(cellNetId, ipv6Subnet, defaultRoute);
verify(mMockDnsResolver, times(1)).createNetworkCache(eq(cellNetId));
verify(mMockNetd, times(1)).networkAddInterface(cellNetId, MOBILE_IFNAME);
- verify(mBatteryStatsService).noteNetworkInterfaceType(cellLp.getInterfaceName(),
- TYPE_MOBILE);
+ verify(mBatteryStatsService).noteNetworkInterfaceForTransports(cellLp.getInterfaceName(),
+ new int[] { TRANSPORT_CELLULAR });
networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId);
@@ -7489,7 +7656,8 @@
// Make sure BatteryStats was not told about any v4- interfaces, as none should have
// come online yet.
waitForIdle();
- verify(mBatteryStatsService, never()).noteNetworkInterfaceType(startsWith("v4-"), anyInt());
+ verify(mBatteryStatsService, never()).noteNetworkInterfaceForTransports(startsWith("v4-"),
+ any());
verifyNoMoreInteractions(mMockNetd);
verifyNoMoreInteractions(mMockDnsResolver);
@@ -7542,8 +7710,8 @@
assertTrue(ArrayUtils.contains(resolvrParams.servers, "8.8.8.8"));
for (final LinkProperties stackedLp : stackedLpsAfterChange) {
- verify(mBatteryStatsService).noteNetworkInterfaceType(stackedLp.getInterfaceName(),
- TYPE_MOBILE);
+ verify(mBatteryStatsService).noteNetworkInterfaceForTransports(
+ stackedLp.getInterfaceName(), new int[] { TRANSPORT_CELLULAR });
}
reset(mMockNetd);
when(mMockNetd.interfaceGetCfg(CLAT_PREFIX + MOBILE_IFNAME))
@@ -8319,13 +8487,14 @@
private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
throws Exception {
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
+ mMockVpn.setVpnType(vpnType);
mMockVpn.establish(new LinkProperties(), vpnOwnerUid, vpnRange);
assertVpnUidRangesUpdated(true, vpnRange, vpnOwnerUid);
- mMockVpn.setVpnType(vpnType);
- final VpnInfo vpnInfo = new VpnInfo();
- vpnInfo.ownerUid = vpnOwnerUid;
- mMockVpn.setVpnInfo(vpnInfo);
+ final UnderlyingNetworkInfo underlyingNetworkInfo =
+ new UnderlyingNetworkInfo(vpnOwnerUid, VPN_IFNAME, new ArrayList<String>());
+ mMockVpn.setUnderlyingNetworkInfo(underlyingNetworkInfo);
+ when(mDeps.getConnectionOwnerUid(anyInt(), any(), any())).thenReturn(42);
}
private void setupConnectionOwnerUidAsVpnApp(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
@@ -8374,8 +8543,7 @@
final int myUid = Process.myUid();
setupConnectionOwnerUidAsVpnApp(myUid, VpnManager.TYPE_VPN_SERVICE);
- // TODO: Test the returned UID
- mService.getConnectionOwnerUid(getTestConnectionInfo());
+ assertEquals(42, mService.getConnectionOwnerUid(getTestConnectionInfo()));
}
@Test
@@ -8385,8 +8553,7 @@
mServiceContext.setPermission(
android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED);
- // TODO: Test the returned UID
- mService.getConnectionOwnerUid(getTestConnectionInfo());
+ assertEquals(42, mService.getConnectionOwnerUid(getTestConnectionInfo()));
}
@Test
@@ -8397,8 +8564,7 @@
mServiceContext.setPermission(
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, PERMISSION_GRANTED);
- // TODO: Test the returned UID
- mService.getConnectionOwnerUid(getTestConnectionInfo());
+ assertEquals(42, mService.getConnectionOwnerUid(getTestConnectionInfo()));
}
private static PackageInfo buildPackageInfo(boolean hasSystemPermission, int uid) {
@@ -8582,7 +8748,7 @@
setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION);
- assertTrue(mService.setUnderlyingNetworksForVpn(new Network[] {naiWithoutUid.network}));
+ assertTrue(mMockVpn.setUnderlyingNetworks(new Network[] {naiWithoutUid.network}));
waitForIdle();
assertTrue(
"Active VPN permission not applied",
@@ -8590,7 +8756,7 @@
Process.myPid(), Process.myUid(), naiWithoutUid,
mContext.getOpPackageName()));
- assertTrue(mService.setUnderlyingNetworksForVpn(null));
+ assertTrue(mMockVpn.setUnderlyingNetworks(null));
waitForIdle();
assertFalse(
"VPN shouldn't receive callback on non-underlying network",
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 68aaaed..73cc9f1 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -49,6 +49,7 @@
import android.annotation.UserIdInt;
import android.app.AppOpsManager;
import android.app.NotificationManager;
+import android.app.PendingIntent;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -119,6 +120,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
@@ -148,6 +150,7 @@
managedProfileA.profileGroupId = primaryUser.id;
}
+ static final Network EGRESS_NETWORK = new Network(101);
static final String EGRESS_IFACE = "wlan0";
static final String TEST_VPN_PKG = "com.testvpn.vpn";
private static final String TEST_VPN_SERVER = "1.2.3.4";
@@ -212,6 +215,8 @@
when(mContext.getPackageName()).thenReturn(TEST_VPN_PKG);
when(mContext.getOpPackageName()).thenReturn(TEST_VPN_PKG);
+ when(mContext.getSystemServiceName(UserManager.class))
+ .thenReturn(Context.USER_SERVICE);
when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager);
when(mContext.getSystemService(eq(Context.APP_OPS_SERVICE))).thenReturn(mAppOps);
when(mContext.getSystemServiceName(NotificationManager.class))
@@ -252,12 +257,14 @@
@Test
public void testRestrictedProfilesAreAddedToVpn() {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
setMockedUsers(primaryUser, secondaryUser, restrictedProfileA, restrictedProfileB);
final Vpn vpn = createVpn(primaryUser.id);
- final Set<UidRange> ranges = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
- null, null);
+
+ // Assume the user can have restricted profiles.
+ doReturn(true).when(mUserManager).canHaveRestrictedProfile();
+ final Set<UidRange> ranges =
+ vpn.createUserAndRestrictedProfilesRanges(primaryUser.id, null, null);
assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
PRI_USER_RANGE, UidRange.createForUser(restrictedProfileA.id)
@@ -266,7 +273,6 @@
@Test
public void testManagedProfilesAreNotAddedToVpn() {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
setMockedUsers(primaryUser, managedProfileA);
final Vpn vpn = createVpn(primaryUser.id);
@@ -289,7 +295,6 @@
@Test
public void testUidAllowAndDenylist() throws Exception {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
final Vpn vpn = createVpn(primaryUser.id);
final UidRange user = PRI_USER_RANGE;
final String[] packages = {PKGS[0], PKGS[1], PKGS[2]};
@@ -315,7 +320,6 @@
@Test
public void testGetAlwaysAndOnGetLockDown() throws Exception {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
final Vpn vpn = createVpn(primaryUser.id);
// Default state.
@@ -340,7 +344,6 @@
@Test
public void testLockdownChangingPackage() throws Exception {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
final Vpn vpn = createVpn(primaryUser.id);
final UidRange user = PRI_USER_RANGE;
@@ -368,7 +371,6 @@
@Test
public void testLockdownAllowlist() throws Exception {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
final Vpn vpn = createVpn(primaryUser.id);
final UidRange user = PRI_USER_RANGE;
@@ -443,7 +445,6 @@
@Test
public void testLockdownRuleRepeatability() throws Exception {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
final Vpn vpn = createVpn(primaryUser.id);
final UidRangeParcel[] primaryUserRangeParcel = new UidRangeParcel[] {
new UidRangeParcel(PRI_USER_RANGE.start, PRI_USER_RANGE.stop)};
@@ -476,7 +477,6 @@
@Test
public void testLockdownRuleReversibility() throws Exception {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
final Vpn vpn = createVpn(primaryUser.id);
final UidRangeParcel[] entireUser = {
new UidRangeParcel(PRI_USER_RANGE.start, PRI_USER_RANGE.stop)
@@ -963,7 +963,7 @@
InetAddresses.parseNumericAddress("192.0.2.0"), EGRESS_IFACE);
lp.addRoute(defaultRoute);
- vpn.startLegacyVpn(vpnProfile, mKeyStore, lp);
+ vpn.startLegacyVpn(vpnProfile, mKeyStore, EGRESS_NETWORK, lp);
return vpn;
}
@@ -996,14 +996,12 @@
profile.ipsecIdentifier = "id";
profile.ipsecSecret = "secret";
profile.l2tpSecret = "l2tpsecret";
+
when(mConnectivityManager.getAllNetworks())
.thenReturn(new Network[] { new Network(101) });
+
when(mConnectivityManager.registerNetworkAgent(any(), any(), any(), any(),
- anyInt(), any(), anyInt())).thenAnswer(invocation -> {
- // The runner has registered an agent and is now ready.
- legacyRunnerReady.open();
- return new Network(102);
- });
+ anyInt(), any(), anyInt())).thenReturn(new Network(102));
final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), profile);
final TestDeps deps = (TestDeps) vpn.mDeps;
try {
@@ -1019,14 +1017,20 @@
"linkname", "vpn", "refuse-eap", "nodefaultroute", "usepeerdns",
"idle", "1800", "mtu", "1270", "mru", "1270" },
deps.mtpdArgs.get(10, TimeUnit.SECONDS));
+
// Now wait for the runner to be ready before testing for the route.
- legacyRunnerReady.block(10_000);
- // In this test the expected address is always v4 so /32
+ ArgumentCaptor<LinkProperties> lpCaptor = ArgumentCaptor.forClass(LinkProperties.class);
+ verify(mConnectivityManager, timeout(10_000)).registerNetworkAgent(any(), any(),
+ lpCaptor.capture(), any(), anyInt(), any(), anyInt());
+
+ // In this test the expected address is always v4 so /32.
+ // Note that the interface needs to be specified because RouteInfo objects stored in
+ // LinkProperties objects always acquire the LinkProperties' interface.
final RouteInfo expectedRoute = new RouteInfo(new IpPrefix(expectedAddr + "/32"),
- RouteInfo.RTN_THROW);
- assertTrue("Routes lack the expected throw route (" + expectedRoute + ") : "
- + vpn.mConfig.routes,
- vpn.mConfig.routes.contains(expectedRoute));
+ null, EGRESS_IFACE, RouteInfo.RTN_THROW);
+ final List<RouteInfo> actualRoutes = lpCaptor.getValue().getRoutes();
+ assertTrue("Expected throw route (" + expectedRoute + ") not found in " + actualRoutes,
+ actualRoutes.contains(expectedRoute));
} finally {
// Now interrupt the thread, unblock the runner and clean up.
vpn.mVpnRunner.exitVpnRunner();
@@ -1082,6 +1086,11 @@
}
@Override
+ public PendingIntent getIntentForStatusPanel(Context context) {
+ return null;
+ }
+
+ @Override
public void sendArgumentsToDaemon(
final String daemon, final LocalSocket socket, final String[] arguments,
final Vpn.RetryScheduler interruptChecker) throws IOException {
@@ -1178,11 +1187,6 @@
final int id = (int) invocation.getArguments()[0];
return userMap.get(id);
}).when(mUserManager).getUserInfo(anyInt());
-
- doAnswer(invocation -> {
- final int id = (int) invocation.getArguments()[0];
- return (userMap.get(id).flags & UserInfo.FLAG_ADMIN) != 0;
- }).when(mUserManager).canHaveRestrictedProfile();
}
/**
diff --git a/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java b/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java
index 3aafe0b..a058a46 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java
@@ -33,8 +33,9 @@
import static org.junit.Assert.assertEquals;
import android.net.NetworkStats;
+import android.net.UnderlyingNetworkInfo;
-import com.android.internal.net.VpnInfo;
+import java.util.Arrays;
/** Superclass with utilities for NetworkStats(Service|Factory)Test */
abstract class NetworkStatsBaseTest {
@@ -108,15 +109,11 @@
assertEquals("unexpected operations", operations, entry.operations);
}
- static VpnInfo createVpnInfo(String[] underlyingIfaces) {
+ static UnderlyingNetworkInfo createVpnInfo(String[] underlyingIfaces) {
return createVpnInfo(TUN_IFACE, underlyingIfaces);
}
- static VpnInfo createVpnInfo(String vpnIface, String[] underlyingIfaces) {
- VpnInfo info = new VpnInfo();
- info.ownerUid = UID_VPN;
- info.vpnIface = vpnIface;
- info.underlyingIfaces = underlyingIfaces;
- return info;
+ static UnderlyingNetworkInfo createVpnInfo(String vpnIface, String[] underlyingIfaces) {
+ return new UnderlyingNetworkInfo(UID_VPN, vpnIface, Arrays.asList(underlyingIfaces));
}
}
diff --git a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java b/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
index e4996d9..f3ae9b0 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
@@ -36,13 +36,13 @@
import android.content.res.Resources;
import android.net.NetworkStats;
import android.net.TrafficStats;
+import android.net.UnderlyingNetworkInfo;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.frameworks.tests.net.R;
-import com.android.internal.net.VpnInfo;
import libcore.io.IoUtils;
import libcore.io.Streams;
@@ -79,7 +79,7 @@
// related to networkStatsFactory is compiled to a minimal native library and loaded here.
System.loadLibrary("networkstatsfactorytestjni");
mFactory = new NetworkStatsFactory(mTestProc, false);
- mFactory.updateVpnInfos(new VpnInfo[0]);
+ mFactory.updateUnderlyingNetworkInfos(new UnderlyingNetworkInfo[0]);
}
@After
@@ -105,8 +105,9 @@
@Test
public void testVpnRewriteTrafficThroughItself() throws Exception {
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
- mFactory.updateVpnInfos(vpnInfos);
+ UnderlyingNetworkInfo[] underlyingNetworkInfos =
+ new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
+ mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
@@ -134,8 +135,9 @@
@Test
public void testVpnWithClat() throws Exception {
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {CLAT_PREFIX + TEST_IFACE})};
- mFactory.updateVpnInfos(vpnInfos);
+ final UnderlyingNetworkInfo[] underlyingNetworkInfos = new UnderlyingNetworkInfo[] {
+ createVpnInfo(new String[] {CLAT_PREFIX + TEST_IFACE})};
+ mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos);
mFactory.noteStackedIface(CLAT_PREFIX + TEST_IFACE, TEST_IFACE);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
@@ -167,8 +169,9 @@
@Test
public void testVpnWithOneUnderlyingIface() throws Exception {
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
- mFactory.updateVpnInfos(vpnInfos);
+ final UnderlyingNetworkInfo[] underlyingNetworkInfos =
+ new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
+ mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
@@ -191,8 +194,9 @@
@Test
public void testVpnWithOneUnderlyingIfaceAndOwnTraffic() throws Exception {
// WiFi network is connected and VPN is using WiFi (which has TEST_IFACE).
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
- mFactory.updateVpnInfos(vpnInfos);
+ final UnderlyingNetworkInfo[] underlyingNetworkInfos =
+ new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
+ mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
@@ -219,8 +223,9 @@
@Test
public void testVpnWithOneUnderlyingIface_withCompression() throws Exception {
// WiFi network is connected and VPN is using WiFi (which has TEST_IFACE).
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
- mFactory.updateVpnInfos(vpnInfos);
+ final UnderlyingNetworkInfo[] underlyingNetworkInfos =
+ new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
+ mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
@@ -242,8 +247,9 @@
// WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and
// Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set.
// Additionally, VPN is duplicating traffic across both WiFi and Cell.
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})};
- mFactory.updateVpnInfos(vpnInfos);
+ final UnderlyingNetworkInfo[] underlyingNetworkInfos =
+ new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})};
+ mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
@@ -267,10 +273,10 @@
public void testConcurrentVpns() throws Exception {
// Assume two VPNs are connected on two different network interfaces. VPN1 is using
// TEST_IFACE and VPN2 is using TEST_IFACE2.
- final VpnInfo[] vpnInfos = new VpnInfo[] {
+ final UnderlyingNetworkInfo[] underlyingNetworkInfos = new UnderlyingNetworkInfo[] {
createVpnInfo(TUN_IFACE, new String[] {TEST_IFACE}),
createVpnInfo(TUN_IFACE2, new String[] {TEST_IFACE2})};
- mFactory.updateVpnInfos(vpnInfos);
+ mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
@@ -308,8 +314,9 @@
// WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and
// Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set.
// Additionally, VPN is arbitrarily splitting traffic across WiFi and Cell.
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})};
- mFactory.updateVpnInfos(vpnInfos);
+ final UnderlyingNetworkInfo[] underlyingNetworkInfos =
+ new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})};
+ mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
@@ -335,8 +342,9 @@
// WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and
// Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set.
// Additionally, VPN is arbitrarily splitting compressed traffic across WiFi and Cell.
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})};
- mFactory.updateVpnInfos(vpnInfos);
+ final UnderlyingNetworkInfo[] underlyingNetworkInfos =
+ new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})};
+ mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface:
// 1000 bytes (100 packets) were sent/received by UID_RED over VPN.
@@ -357,8 +365,9 @@
public void testVpnWithIncorrectUnderlyingIface() throws Exception {
// WiFi and Cell networks are connected and VPN is using Cell (which has TEST_IFACE2),
// but has declared only WiFi (TEST_IFACE) in its underlying network set.
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
- mFactory.updateVpnInfos(vpnInfos);
+ final UnderlyingNetworkInfo[] underlyingNetworkInfos =
+ new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
+ mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos);
// create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
// overhead per packet):
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index 19f9641..dde78aa 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -86,6 +86,7 @@
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
+import android.net.UnderlyingNetworkInfo;
import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.os.ConditionVariable;
import android.os.Handler;
@@ -104,7 +105,6 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.net.VpnInfo;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.server.net.NetworkStatsService.NetworkStatsSettings;
@@ -286,7 +286,8 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
// verify service has empty history for wifi
assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
@@ -328,7 +329,8 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
// verify service has empty history for wifi
assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
@@ -401,7 +403,8 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
// modify some number on wifi, and trigger poll event
incrementCurrentTime(2 * HOUR_IN_MILLIS);
@@ -441,7 +444,8 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
// create some traffic on first network
incrementCurrentTime(HOUR_IN_MILLIS);
@@ -475,7 +479,8 @@
.insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
.insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
- mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
forcePollAndWaitForIdle();
@@ -514,7 +519,8 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
// create some traffic
incrementCurrentTime(HOUR_IN_MILLIS);
@@ -581,7 +587,7 @@
setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_UMTS);
mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states),
- new VpnInfo[0]);
+ new UnderlyingNetworkInfo[0]);
// Create some traffic.
incrementCurrentTime(MINUTE_IN_MILLIS);
@@ -655,7 +661,8 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
// create some traffic for two apps
incrementCurrentTime(HOUR_IN_MILLIS);
@@ -713,7 +720,8 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
NetworkStats.Entry entry1 = new NetworkStats.Entry(
TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L);
@@ -756,7 +764,8 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
NetworkStats.Entry uidStats = new NetworkStats.Entry(
TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xF00D, 1024L, 8L, 512L, 4L, 0L);
@@ -810,7 +819,8 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
// create some initial traffic
incrementCurrentTime(HOUR_IN_MILLIS);
@@ -867,7 +877,8 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
// create some initial traffic
incrementCurrentTime(HOUR_IN_MILLIS);
@@ -906,7 +917,8 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
// Create some traffic
incrementCurrentTime(HOUR_IN_MILLIS);
@@ -943,7 +955,8 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
// create some tethering traffic
incrementCurrentTime(HOUR_IN_MILLIS);
@@ -999,7 +1012,8 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
// verify service has empty history for wifi
assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
@@ -1104,7 +1118,8 @@
mService.registerNetworkStatsProvider("TEST", provider);
assertNotNull(cb);
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
// Verifies that one requestStatsUpdate will be called during iface update.
provider.expectOnRequestStatsUpdate(0 /* unused */);
@@ -1155,7 +1170,8 @@
expectDefaultSettings();
NetworkState[] states =
new NetworkState[]{buildWifiState(true /* isMetered */, TEST_IFACE)};
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
// Register custom provider and retrieve callback.
final TestableNetworkStatsProviderBinder provider =
@@ -1204,7 +1220,7 @@
// 3G network comes online.
setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_UMTS);
mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states),
- new VpnInfo[0]);
+ new UnderlyingNetworkInfo[0]);
// Create some traffic.
incrementCurrentTime(MINUTE_IN_MILLIS);
@@ -1274,7 +1290,8 @@
NetworkState[] states = new NetworkState[]{
buildWifiState(true /*isMetered*/, TEST_IFACE2), buildMobile3gState(IMSI_1)};
expectNetworkStatsUidDetail(buildEmptyStats());
- mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
+ mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
// Create some traffic on mobile network.
incrementCurrentTime(HOUR_IN_MILLIS);
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index dfd0c8a..3e659d0 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -28,6 +28,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Arrays;
import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
@@ -39,6 +40,12 @@
NetworkCapabilities.NET_CAPABILITY_INTERNET, NetworkCapabilities.NET_CAPABILITY_MMS
};
public static final int[] UNDERLYING_CAPS = new int[] {NetworkCapabilities.NET_CAPABILITY_DUN};
+
+ static {
+ Arrays.sort(EXPOSED_CAPS);
+ Arrays.sort(UNDERLYING_CAPS);
+ }
+
public static final long[] RETRY_INTERVALS_MS =
new long[] {
TimeUnit.SECONDS.toMillis(5),
@@ -52,12 +59,17 @@
// Public for use in VcnGatewayConnectionTest
public static VcnGatewayConnectionConfig buildTestConfig() {
+ return buildTestConfigWithExposedCaps(EXPOSED_CAPS);
+ }
+
+ // Public for use in VcnGatewayConnectionTest
+ public static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps(int... exposedCaps) {
final VcnGatewayConnectionConfig.Builder builder =
new VcnGatewayConnectionConfig.Builder()
.setRetryInterval(RETRY_INTERVALS_MS)
.setMaxMtu(MAX_MTU);
- for (int caps : EXPOSED_CAPS) {
+ for (int caps : exposedCaps) {
builder.addExposedCapability(caps);
}
@@ -124,12 +136,13 @@
public void testBuilderAndGetters() {
final VcnGatewayConnectionConfig config = buildTestConfig();
- for (int cap : EXPOSED_CAPS) {
- config.hasExposedCapability(cap);
- }
- for (int cap : UNDERLYING_CAPS) {
- config.requiresUnderlyingCapability(cap);
- }
+ int[] exposedCaps = config.getExposedCapabilities();
+ Arrays.sort(exposedCaps);
+ assertArrayEquals(EXPOSED_CAPS, exposedCaps);
+
+ int[] underlyingCaps = config.getRequiredUnderlyingCapabilities();
+ Arrays.sort(underlyingCaps);
+ assertArrayEquals(UNDERLYING_CAPS, underlyingCaps);
assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMs());
assertEquals(MAX_MTU, config.getMaxMtu());
diff --git a/tests/vcn/java/android/net/vcn/VcnManagerTest.java b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
index 9c6b719..f9db408 100644
--- a/tests/vcn/java/android/net/vcn/VcnManagerTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
@@ -18,14 +18,19 @@
import static androidx.test.InstrumentationRegistry.getContext;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.content.Context;
+import android.net.LinkProperties;
+import android.net.NetworkCapabilities;
import android.net.vcn.VcnManager.VcnUnderlyingNetworkPolicyListener;
import org.junit.Before;
@@ -103,4 +108,28 @@
public void testRemoveVcnUnderlyingNetworkPolicyListenerNullListener() {
mVcnManager.removeVcnUnderlyingNetworkPolicyListener(null);
}
+
+ @Test
+ public void testGetUnderlyingNetworkPolicy() throws Exception {
+ NetworkCapabilities nc = new NetworkCapabilities();
+ LinkProperties lp = new LinkProperties();
+ when(mMockVcnManagementService.getUnderlyingNetworkPolicy(eq(nc), eq(lp)))
+ .thenReturn(new VcnUnderlyingNetworkPolicy(false /* isTearDownRequested */, nc));
+
+ VcnUnderlyingNetworkPolicy policy = mVcnManager.getUnderlyingNetworkPolicy(nc, lp);
+
+ assertFalse(policy.isTeardownRequested());
+ assertEquals(nc, policy.getMergedNetworkCapabilities());
+ verify(mMockVcnManagementService).getUnderlyingNetworkPolicy(eq(nc), eq(lp));
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testGetUnderlyingNetworkPolicyNullNetworkCapabilities() throws Exception {
+ mVcnManager.getUnderlyingNetworkPolicy(null, new LinkProperties());
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testGetUnderlyingNetworkPolicyNullLinkProperties() throws Exception {
+ mVcnManager.getUnderlyingNetworkPolicy(new NetworkCapabilities(), null);
+ }
}
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index f0cdde3..4859644 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -21,6 +21,7 @@
import static com.android.server.vcn.VcnTestUtils.setupSystemService;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -28,6 +29,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.argThat;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
@@ -37,13 +39,20 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
import android.content.Context;
import android.net.ConnectivityManager;
+import android.net.LinkProperties;
+import android.net.NetworkCapabilities;
+import android.net.NetworkCapabilities.Transport;
+import android.net.TelephonyNetworkSpecifier;
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
import android.net.vcn.VcnConfig;
import android.net.vcn.VcnConfigTest;
+import android.net.vcn.VcnUnderlyingNetworkPolicy;
+import android.net.wifi.WifiInfo;
import android.os.IBinder;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
@@ -57,12 +66,14 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.VcnManagementService.VcnSafemodeCallback;
import com.android.server.vcn.TelephonySubscriptionTracker;
import com.android.server.vcn.Vcn;
import com.android.server.vcn.VcnContext;
import com.android.server.vcn.VcnNetworkProvider;
import com.android.server.vcn.util.PersistableBundleUtils;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -132,6 +143,9 @@
private final TelephonySubscriptionTracker mSubscriptionTracker =
mock(TelephonySubscriptionTracker.class);
+ private final ArgumentCaptor<VcnSafemodeCallback> mSafemodeCallbackCaptor =
+ ArgumentCaptor.forClass(VcnSafemodeCallback.class);
+
private final VcnManagementService mVcnMgmtSvc;
private final IVcnUnderlyingNetworkPolicyListener mMockPolicyListener =
@@ -174,7 +188,7 @@
doAnswer((invocation) -> {
// Mock-within a doAnswer is safe, because it doesn't actually run nested.
return mock(Vcn.class);
- }).when(mMockDeps).newVcn(any(), any(), any());
+ }).when(mMockDeps).newVcn(any(), any(), any(), any(), any());
final PersistableBundle bundle =
PersistableBundleUtils.fromMap(
@@ -192,6 +206,14 @@
mTestLooper.dispatchAll();
}
+ @Before
+ public void setUp() {
+ doNothing()
+ .when(mMockContext)
+ .enforceCallingOrSelfPermission(
+ eq(android.Manifest.permission.NETWORK_FACTORY), any());
+ }
+
private void setupMockedCarrierPrivilege(boolean isPrivileged) {
doReturn(Collections.singletonList(TEST_SUBSCRIPTION_INFO))
.when(mSubMgr)
@@ -239,7 +261,14 @@
verify(mConfigReadWriteHelper).readFromDisk();
}
- private void triggerSubscriptionTrackerCallback(Set<ParcelUuid> activeSubscriptionGroups) {
+ private TelephonySubscriptionSnapshot triggerSubscriptionTrackerCbAndGetSnapshot(
+ Set<ParcelUuid> activeSubscriptionGroups) {
+ return triggerSubscriptionTrackerCbAndGetSnapshot(
+ activeSubscriptionGroups, Collections.emptyMap());
+ }
+
+ private TelephonySubscriptionSnapshot triggerSubscriptionTrackerCbAndGetSnapshot(
+ Set<ParcelUuid> activeSubscriptionGroups, Map<Integer, ParcelUuid> subIdToGroupMap) {
final TelephonySubscriptionSnapshot snapshot = mock(TelephonySubscriptionSnapshot.class);
doReturn(activeSubscriptionGroups).when(snapshot).getActiveSubscriptionGroups();
@@ -253,8 +282,14 @@
argThat(val -> activeSubscriptionGroups.contains(val)),
eq(TEST_PACKAGE_NAME));
+ doAnswer(invocation -> {
+ return subIdToGroupMap.get(invocation.getArgument(0));
+ }).when(snapshot).getGroupForSubId(anyInt());
+
final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
cb.onNewSnapshot(snapshot);
+
+ return snapshot;
}
private TelephonySubscriptionTrackerCallback getTelephonySubscriptionTrackerCallback() {
@@ -273,21 +308,25 @@
@Test
public void testTelephonyNetworkTrackerCallbackStartsInstances() throws Exception {
- triggerSubscriptionTrackerCallback(Collections.singleton(TEST_UUID_1));
- verify(mMockDeps).newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG));
+ TelephonySubscriptionSnapshot snapshot =
+ triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
+ verify(mMockDeps)
+ .newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG), eq(snapshot), any());
}
@Test
public void testTelephonyNetworkTrackerCallbackStopsInstances() throws Exception {
final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2);
+ mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
- triggerSubscriptionTrackerCallback(Collections.emptySet());
+ triggerSubscriptionTrackerCbAndGetSnapshot(Collections.emptySet());
// Verify teardown after delay
mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
mTestLooper.dispatchAll();
verify(vcn).teardownAsynchronously();
+ verify(mMockPolicyListener).onPolicyChanged();
}
@Test
@@ -297,13 +336,13 @@
final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2);
// Simulate SIM unloaded
- triggerSubscriptionTrackerCallback(Collections.emptySet());
+ triggerSubscriptionTrackerCbAndGetSnapshot(Collections.emptySet());
// Simulate new SIM loaded right during teardown delay.
mTestLooper.moveTimeForward(
VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS / 2);
mTestLooper.dispatchAll();
- triggerSubscriptionTrackerCallback(Collections.singleton(TEST_UUID_2));
+ triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_2));
// Verify that even after the full timeout duration, the VCN instance is not torn down
mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
@@ -317,7 +356,7 @@
final Vcn oldInstance = startAndGetVcnInstance(TEST_UUID_2);
// Simulate SIM unloaded
- triggerSubscriptionTrackerCallback(Collections.emptySet());
+ triggerSubscriptionTrackerCbAndGetSnapshot(Collections.emptySet());
// Config cleared, SIM reloaded & config re-added right before teardown delay, staring new
// vcnInstance.
@@ -358,6 +397,7 @@
mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
fail("Expected security exception for non system user");
} catch (SecurityException expected) {
+ verify(mMockPolicyListener, never()).onPolicyChanged();
}
}
@@ -369,6 +409,7 @@
mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
fail("Expected security exception for missing carrier privileges");
} catch (SecurityException expected) {
+ verify(mMockPolicyListener, never()).onPolicyChanged();
}
}
@@ -378,6 +419,7 @@
mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, TEST_VCN_CONFIG, "IncorrectPackage");
fail("Expected exception due to mismatched packages in config and method call");
} catch (IllegalArgumentException expected) {
+ verify(mMockPolicyListener, never()).onPolicyChanged();
}
}
@@ -442,7 +484,13 @@
verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class));
// Verify Vcn is started
- verify(mMockDeps).newVcn(eq(mVcnContext), eq(TEST_UUID_2), eq(TEST_VCN_CONFIG));
+ verify(mMockDeps)
+ .newVcn(
+ eq(mVcnContext),
+ eq(TEST_UUID_2),
+ eq(TEST_VCN_CONFIG),
+ eq(TelephonySubscriptionSnapshot.EMPTY_SNAPSHOT),
+ any());
// Verify Vcn is updated if it was previously started
mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
@@ -455,10 +503,6 @@
@Test
public void testAddVcnUnderlyingNetworkPolicyListener() throws Exception {
- doNothing()
- .when(mMockContext)
- .enforceCallingPermission(eq(android.Manifest.permission.NETWORK_FACTORY), any());
-
mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
verify(mMockIBinder).linkToDeath(any(), anyInt());
@@ -468,17 +512,14 @@
public void testAddVcnUnderlyingNetworkPolicyListenerInvalidPermission() {
doThrow(new SecurityException())
.when(mMockContext)
- .enforceCallingPermission(eq(android.Manifest.permission.NETWORK_FACTORY), any());
+ .enforceCallingOrSelfPermission(
+ eq(android.Manifest.permission.NETWORK_FACTORY), any());
mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
}
@Test
public void testRemoveVcnUnderlyingNetworkPolicyListener() {
- // verify listener added
- doNothing()
- .when(mMockContext)
- .enforceCallingPermission(eq(android.Manifest.permission.NETWORK_FACTORY), any());
mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
@@ -488,4 +529,135 @@
public void testRemoveVcnUnderlyingNetworkPolicyListenerNeverRegistered() {
mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
}
+
+ private void setUpVcnSubscription(int subId, ParcelUuid subGroup) {
+ mVcnMgmtSvc.setVcnConfig(subGroup, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
+
+ triggerSubscriptionTrackerCbAndGetSnapshot(
+ Collections.singleton(subGroup), Collections.singletonMap(subId, subGroup));
+ }
+
+ private void verifyMergedNetworkCapabilitiesIsVcnManaged(
+ NetworkCapabilities mergedCapabilities, @Transport int transportType) {
+ assertTrue(mergedCapabilities.hasTransport(transportType));
+ assertFalse(
+ mergedCapabilities.hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED));
+ }
+
+ @Test
+ public void testGetUnderlyingNetworkPolicyCellular() throws Exception {
+ setUpVcnSubscription(TEST_SUBSCRIPTION_ID, TEST_UUID_2);
+
+ NetworkCapabilities nc =
+ new NetworkCapabilities.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID))
+ .build();
+
+ VcnUnderlyingNetworkPolicy policy =
+ mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties());
+
+ assertFalse(policy.isTeardownRequested());
+ verifyMergedNetworkCapabilitiesIsVcnManaged(
+ policy.getMergedNetworkCapabilities(), NetworkCapabilities.TRANSPORT_CELLULAR);
+ }
+
+ @Test
+ public void testGetUnderlyingNetworkPolicyWifi() throws Exception {
+ setUpVcnSubscription(TEST_SUBSCRIPTION_ID, TEST_UUID_2);
+
+ WifiInfo wifiInfo = mock(WifiInfo.class);
+ when(wifiInfo.makeCopy(anyBoolean())).thenReturn(wifiInfo);
+ when(mMockDeps.getSubIdForWifiInfo(eq(wifiInfo))).thenReturn(TEST_SUBSCRIPTION_ID);
+ NetworkCapabilities nc =
+ new NetworkCapabilities.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .setTransportInfo(wifiInfo)
+ .build();
+
+ VcnUnderlyingNetworkPolicy policy =
+ mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties());
+
+ assertFalse(policy.isTeardownRequested());
+ verifyMergedNetworkCapabilitiesIsVcnManaged(
+ policy.getMergedNetworkCapabilities(), NetworkCapabilities.TRANSPORT_WIFI);
+ }
+
+ @Test
+ public void testGetUnderlyingNetworkPolicyNonVcnNetwork() throws Exception {
+ NetworkCapabilities nc =
+ new NetworkCapabilities.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID))
+ .build();
+
+ VcnUnderlyingNetworkPolicy policy =
+ mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties());
+
+ assertFalse(policy.isTeardownRequested());
+ assertEquals(nc, policy.getMergedNetworkCapabilities());
+ }
+
+ @Test(expected = SecurityException.class)
+ public void testGetUnderlyingNetworkPolicyInvalidPermission() {
+ doThrow(new SecurityException())
+ .when(mMockContext)
+ .enforceCallingOrSelfPermission(
+ eq(android.Manifest.permission.NETWORK_FACTORY), any());
+
+ mVcnMgmtSvc.getUnderlyingNetworkPolicy(new NetworkCapabilities(), new LinkProperties());
+ }
+
+ @Test
+ public void testSubscriptionSnapshotUpdateNotifiesVcn() {
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
+ final Map<ParcelUuid, Vcn> vcnInstances = mVcnMgmtSvc.getAllVcns();
+ final Vcn vcnInstance = vcnInstances.get(TEST_UUID_2);
+
+ TelephonySubscriptionSnapshot snapshot =
+ triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_2));
+
+ verify(vcnInstance).updateSubscriptionSnapshot(eq(snapshot));
+ }
+
+ @Test
+ public void testAddNewVcnUpdatesPolicyListener() throws Exception {
+ mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
+
+ verify(mMockPolicyListener).onPolicyChanged();
+ }
+
+ @Test
+ public void testRemoveVcnUpdatesPolicyListener() throws Exception {
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
+ mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+ mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2);
+
+ verify(mMockPolicyListener).onPolicyChanged();
+ }
+
+ @Test
+ public void testVcnSafemodeCallbackOnEnteredSafemode() throws Exception {
+ TelephonySubscriptionSnapshot snapshot =
+ triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
+ verify(mMockDeps)
+ .newVcn(
+ eq(mVcnContext),
+ eq(TEST_UUID_1),
+ eq(TEST_VCN_CONFIG),
+ eq(snapshot),
+ mSafemodeCallbackCaptor.capture());
+
+ mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+ VcnSafemodeCallback safemodeCallback = mSafemodeCallbackCaptor.getValue();
+ safemodeCallback.onEnteredSafemode();
+
+ assertFalse(mVcnMgmtSvc.getAllVcns().get(TEST_UUID_1).isActive());
+ verify(mMockPolicyListener).onPolicyChanged();
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
new file mode 100644
index 0000000..1d459a3
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn;
+
+import static com.android.server.vcn.VcnTestUtils.setupSystemService;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.eq;
+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.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.net.TelephonyNetworkSpecifier;
+import android.os.ParcelUuid;
+import android.os.test.TestLooper;
+import android.telephony.SubscriptionInfo;
+import android.util.ArraySet;
+
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import com.android.server.vcn.UnderlyingNetworkTracker.NetworkBringupCallback;
+import com.android.server.vcn.UnderlyingNetworkTracker.RouteSelectionCallback;
+import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
+import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Set;
+import java.util.UUID;
+
+public class UnderlyingNetworkTrackerTest {
+ private static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0));
+ private static final int INITIAL_SUB_ID_1 = 1;
+ private static final int INITIAL_SUB_ID_2 = 2;
+ private static final int UPDATED_SUB_ID = 3;
+
+ private static final Set<Integer> INITIAL_SUB_IDS =
+ new ArraySet<>(Arrays.asList(INITIAL_SUB_ID_1, INITIAL_SUB_ID_2));
+ private static final Set<Integer> UPDATED_SUB_IDS =
+ new ArraySet<>(Arrays.asList(UPDATED_SUB_ID));
+
+ private static final NetworkCapabilities INITIAL_NETWORK_CAPABILITIES =
+ new NetworkCapabilities.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+ .build();
+ private static final NetworkCapabilities SUSPENDED_NETWORK_CAPABILITIES =
+ new NetworkCapabilities.Builder(INITIAL_NETWORK_CAPABILITIES)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)
+ .build();
+ private static final NetworkCapabilities UPDATED_NETWORK_CAPABILITIES =
+ new NetworkCapabilities.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .build();
+
+ private static final LinkProperties INITIAL_LINK_PROPERTIES =
+ getLinkPropertiesWithName("initial_iface");
+ private static final LinkProperties UPDATED_LINK_PROPERTIES =
+ getLinkPropertiesWithName("updated_iface");
+
+ @Mock private Context mContext;
+ @Mock private VcnNetworkProvider mVcnNetworkProvider;
+ @Mock private ConnectivityManager mConnectivityManager;
+ @Mock private TelephonySubscriptionSnapshot mSubscriptionSnapshot;
+ @Mock private UnderlyingNetworkTrackerCallback mNetworkTrackerCb;
+ @Mock private Network mNetwork;
+
+ @Captor private ArgumentCaptor<RouteSelectionCallback> mRouteSelectionCallbackCaptor;
+
+ private TestLooper mTestLooper;
+ private VcnContext mVcnContext;
+ private UnderlyingNetworkTracker mUnderlyingNetworkTracker;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mTestLooper = new TestLooper();
+ mVcnContext = spy(new VcnContext(mContext, mTestLooper.getLooper(), mVcnNetworkProvider));
+ doNothing().when(mVcnContext).ensureRunningOnLooperThread();
+
+ setupSystemService(
+ mContext,
+ mConnectivityManager,
+ Context.CONNECTIVITY_SERVICE,
+ ConnectivityManager.class);
+
+ when(mSubscriptionSnapshot.getAllSubIdsInGroup(eq(SUB_GROUP))).thenReturn(INITIAL_SUB_IDS);
+
+ mUnderlyingNetworkTracker =
+ new UnderlyingNetworkTracker(
+ mVcnContext,
+ SUB_GROUP,
+ mSubscriptionSnapshot,
+ Collections.singleton(NetworkCapabilities.NET_CAPABILITY_INTERNET),
+ mNetworkTrackerCb);
+ }
+
+ private static LinkProperties getLinkPropertiesWithName(String iface) {
+ LinkProperties linkProperties = new LinkProperties();
+ linkProperties.setInterfaceName(iface);
+ return linkProperties;
+ }
+
+ private SubscriptionInfo getSubscriptionInfoForSubId(int subId) {
+ SubscriptionInfo subInfo = mock(SubscriptionInfo.class);
+ when(subInfo.getSubscriptionId()).thenReturn(subId);
+ return subInfo;
+ }
+
+ @Test
+ public void testNetworkCallbacksRegisteredOnStartup() {
+ // verify NetworkCallbacks registered when instantiated
+ verify(mConnectivityManager)
+ .requestBackgroundNetwork(
+ eq(getWifiRequest()),
+ any(),
+ any(NetworkBringupCallback.class));
+ verifyBackgroundCellRequests(mSubscriptionSnapshot, SUB_GROUP, INITIAL_SUB_IDS);
+
+ verify(mConnectivityManager)
+ .requestBackgroundNetwork(
+ eq(getRouteSelectionRequest()),
+ any(),
+ any(RouteSelectionCallback.class));
+ }
+
+ private void verifyBackgroundCellRequests(
+ TelephonySubscriptionSnapshot snapshot,
+ ParcelUuid subGroup,
+ Set<Integer> expectedSubIds) {
+ verify(snapshot).getAllSubIdsInGroup(eq(subGroup));
+
+ for (final int subId : expectedSubIds) {
+ verify(mConnectivityManager)
+ .requestBackgroundNetwork(
+ eq(getCellRequestForSubId(subId)),
+ any(),
+ any(NetworkBringupCallback.class));
+ }
+ }
+
+ @Test
+ public void testUpdateSubscriptionSnapshot() {
+ // Verify initial cell background requests filed
+ verifyBackgroundCellRequests(mSubscriptionSnapshot, SUB_GROUP, INITIAL_SUB_IDS);
+
+ TelephonySubscriptionSnapshot subscriptionUpdate =
+ mock(TelephonySubscriptionSnapshot.class);
+ when(subscriptionUpdate.getAllSubIdsInGroup(eq(SUB_GROUP))).thenReturn(UPDATED_SUB_IDS);
+
+ mUnderlyingNetworkTracker.updateSubscriptionSnapshot(subscriptionUpdate);
+
+ // verify that initially-filed bringup requests are unregistered
+ verify(mConnectivityManager, times(INITIAL_SUB_IDS.size()))
+ .unregisterNetworkCallback(any(NetworkBringupCallback.class));
+ verifyBackgroundCellRequests(subscriptionUpdate, SUB_GROUP, UPDATED_SUB_IDS);
+ }
+
+ private NetworkRequest getWifiRequest() {
+ return getExpectedRequestBase()
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .build();
+ }
+
+ private NetworkRequest getCellRequestForSubId(int subId) {
+ return getExpectedRequestBase()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .setNetworkSpecifier(new TelephonyNetworkSpecifier(subId))
+ .build();
+ }
+
+ private NetworkRequest getRouteSelectionRequest() {
+ return getExpectedRequestBase().build();
+ }
+
+ private NetworkRequest.Builder getExpectedRequestBase() {
+ return new NetworkRequest.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
+ .addUnwantedCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
+ }
+
+ @Test
+ public void testTeardown() {
+ mUnderlyingNetworkTracker.teardown();
+
+ // Expect 3 NetworkBringupCallbacks to be unregistered: 1 for WiFi and 2 for Cellular (1x
+ // for each subId)
+ verify(mConnectivityManager, times(3))
+ .unregisterNetworkCallback(any(NetworkBringupCallback.class));
+ verify(mConnectivityManager).unregisterNetworkCallback(any(RouteSelectionCallback.class));
+ }
+
+ @Test
+ public void testUnderlyingNetworkRecordEquals() {
+ UnderlyingNetworkRecord recordA =
+ new UnderlyingNetworkRecord(
+ mNetwork,
+ INITIAL_NETWORK_CAPABILITIES,
+ INITIAL_LINK_PROPERTIES,
+ false /* isBlocked */);
+ UnderlyingNetworkRecord recordB =
+ new UnderlyingNetworkRecord(
+ mNetwork,
+ INITIAL_NETWORK_CAPABILITIES,
+ INITIAL_LINK_PROPERTIES,
+ false /* isBlocked */);
+ UnderlyingNetworkRecord recordC =
+ new UnderlyingNetworkRecord(
+ mNetwork,
+ UPDATED_NETWORK_CAPABILITIES,
+ UPDATED_LINK_PROPERTIES,
+ false /* isBlocked */);
+
+ assertEquals(recordA, recordB);
+ assertNotEquals(recordA, recordC);
+ }
+
+ @Test
+ public void testRecordTrackerCallbackNotifiedForNetworkChange() {
+ verifyRegistrationOnAvailableAndGetCallback();
+ }
+
+ private RouteSelectionCallback verifyRegistrationOnAvailableAndGetCallback() {
+ return verifyRegistrationOnAvailableAndGetCallback(INITIAL_NETWORK_CAPABILITIES);
+ }
+
+ private RouteSelectionCallback verifyRegistrationOnAvailableAndGetCallback(
+ NetworkCapabilities networkCapabilities) {
+ verify(mConnectivityManager)
+ .requestBackgroundNetwork(
+ eq(getRouteSelectionRequest()),
+ any(),
+ mRouteSelectionCallbackCaptor.capture());
+
+ RouteSelectionCallback cb = mRouteSelectionCallbackCaptor.getValue();
+ cb.onAvailable(mNetwork);
+ cb.onCapabilitiesChanged(mNetwork, networkCapabilities);
+ cb.onLinkPropertiesChanged(mNetwork, INITIAL_LINK_PROPERTIES);
+ cb.onBlockedStatusChanged(mNetwork, false /* isFalse */);
+
+ UnderlyingNetworkRecord expectedRecord =
+ new UnderlyingNetworkRecord(
+ mNetwork,
+ networkCapabilities,
+ INITIAL_LINK_PROPERTIES,
+ false /* isBlocked */);
+ verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ return cb;
+ }
+
+ @Test
+ public void testRecordTrackerCallbackNotifiedForNetworkCapabilitiesChange() {
+ RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
+
+ cb.onCapabilitiesChanged(mNetwork, UPDATED_NETWORK_CAPABILITIES);
+
+ UnderlyingNetworkRecord expectedRecord =
+ new UnderlyingNetworkRecord(
+ mNetwork,
+ UPDATED_NETWORK_CAPABILITIES,
+ INITIAL_LINK_PROPERTIES,
+ false /* isBlocked */);
+ verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ }
+
+ @Test
+ public void testRecordTrackerCallbackNotifiedForLinkPropertiesChange() {
+ RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
+
+ cb.onLinkPropertiesChanged(mNetwork, UPDATED_LINK_PROPERTIES);
+
+ UnderlyingNetworkRecord expectedRecord =
+ new UnderlyingNetworkRecord(
+ mNetwork,
+ INITIAL_NETWORK_CAPABILITIES,
+ UPDATED_LINK_PROPERTIES,
+ false /* isBlocked */);
+ verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ }
+
+ @Test
+ public void testRecordTrackerCallbackNotifiedForNetworkSuspended() {
+ RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
+
+ cb.onNetworkSuspended(mNetwork);
+
+ UnderlyingNetworkRecord expectedRecord =
+ new UnderlyingNetworkRecord(
+ mNetwork,
+ SUSPENDED_NETWORK_CAPABILITIES,
+ INITIAL_LINK_PROPERTIES,
+ false /* isBlocked */);
+ verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ }
+
+ @Test
+ public void testRecordTrackerCallbackNotifiedForNetworkResumed() {
+ RouteSelectionCallback cb =
+ verifyRegistrationOnAvailableAndGetCallback(SUSPENDED_NETWORK_CAPABILITIES);
+
+ cb.onNetworkResumed(mNetwork);
+
+ UnderlyingNetworkRecord expectedRecord =
+ new UnderlyingNetworkRecord(
+ mNetwork,
+ INITIAL_NETWORK_CAPABILITIES,
+ INITIAL_LINK_PROPERTIES,
+ false /* isBlocked */);
+ verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ }
+
+ @Test
+ public void testRecordTrackerCallbackNotifiedForBlocked() {
+ RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
+
+ cb.onBlockedStatusChanged(mNetwork, true /* isBlocked */);
+
+ UnderlyingNetworkRecord expectedRecord =
+ new UnderlyingNetworkRecord(
+ mNetwork,
+ INITIAL_NETWORK_CAPABILITIES,
+ INITIAL_LINK_PROPERTIES,
+ true /* isBlocked */);
+ verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ }
+
+ @Test
+ public void testRecordTrackerCallbackNotifiedForNetworkLoss() {
+ RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
+
+ cb.onLost(mNetwork);
+
+ verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(null);
+ }
+
+ @Test
+ public void testRecordTrackerCallbackIgnoresDuplicateRecord() {
+ RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
+
+ cb.onCapabilitiesChanged(mNetwork, INITIAL_NETWORK_CAPABILITIES);
+
+ // Verify no more calls to the UnderlyingNetworkTrackerCallback when the
+ // UnderlyingNetworkRecord does not actually change
+ verifyNoMoreInteractions(mNetworkTrackerCb);
+ }
+}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
new file mode 100644
index 0000000..e20070e
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -0,0 +1,127 @@
+/*
+ * 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.server.vcn;
+
+import static android.net.IpSecManager.DIRECTION_IN;
+import static android.net.IpSecManager.DIRECTION_OUT;
+
+import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests for VcnGatewayConnection.ConnectedState */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnectionTestBase {
+ private VcnIkeSession mIkeSession;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1);
+
+ mIkeSession = mGatewayConnection.buildIkeSession();
+ mGatewayConnection.setIkeSession(mIkeSession);
+
+ mGatewayConnection.transitionTo(mGatewayConnection.mConnectedState);
+ mTestLooper.dispatchAll();
+ }
+
+ @Test
+ public void testEnterStateCreatesNewIkeSession() throws Exception {
+ verify(mDeps).newIkeSession(any(), any(), any(), any(), any());
+ }
+
+ @Test
+ public void testNullNetworkDoesNotTriggerDisconnect() throws Exception {
+ mGatewayConnection
+ .getUnderlyingNetworkTrackerCallback()
+ .onSelectedUnderlyingNetworkChanged(null);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+ verify(mIkeSession, never()).close();
+ }
+
+ @Test
+ public void testNewNetworkTriggersMigration() throws Exception {
+ mGatewayConnection
+ .getUnderlyingNetworkTrackerCallback()
+ .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+ verify(mIkeSession, never()).close();
+ verify(mIkeSession).setNetwork(TEST_UNDERLYING_NETWORK_RECORD_2.network);
+ }
+
+ @Test
+ public void testSameNetworkDoesNotTriggerMigration() throws Exception {
+ mGatewayConnection
+ .getUnderlyingNetworkTrackerCallback()
+ .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+ }
+
+ @Test
+ public void testCreatedTransformsAreApplied() throws Exception {
+ for (int direction : new int[] {DIRECTION_IN, DIRECTION_OUT}) {
+ getChildSessionCallback().onIpSecTransformCreated(makeDummyIpSecTransform(), direction);
+ mTestLooper.dispatchAll();
+
+ verify(mIpSecSvc)
+ .applyTunnelModeTransform(
+ eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(direction), anyInt(), any());
+ }
+
+ assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+ }
+
+ @Test
+ public void testChildSessionClosedTriggersDisconnect() throws Exception {
+ getChildSessionCallback().onClosed();
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+ }
+
+ @Test
+ public void testIkeSessionClosedTriggersDisconnect() throws Exception {
+ getIkeSessionCallback().onClosed();
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState());
+ verify(mIkeSession).close();
+ }
+
+ // TODO: Add tests for childOpened() when ChildSessionConfiguration can be mocked or created
+}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
new file mode 100644
index 0000000..d936183
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.server.vcn;
+
+import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests for VcnGatewayConnection.ConnectingState */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectionTestBase {
+ private VcnIkeSession mIkeSession;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1);
+ mGatewayConnection.transitionTo(mGatewayConnection.mConnectingState);
+ mTestLooper.dispatchAll();
+
+ mIkeSession = mGatewayConnection.getIkeSession();
+ }
+
+ @Test
+ public void testEnterStateCreatesNewIkeSession() throws Exception {
+ verify(mDeps).newIkeSession(any(), any(), any(), any(), any());
+ }
+
+ @Test
+ public void testNullNetworkTriggersDisconnect() throws Exception {
+ mGatewayConnection
+ .getUnderlyingNetworkTrackerCallback()
+ .onSelectedUnderlyingNetworkChanged(null);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+ verify(mIkeSession).kill();
+ }
+
+ @Test
+ public void testNewNetworkTriggersReconnect() throws Exception {
+ mGatewayConnection
+ .getUnderlyingNetworkTrackerCallback()
+ .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+ verify(mIkeSession).close();
+ verify(mIkeSession, never()).kill();
+ }
+
+ @Test
+ public void testSameNetworkDoesNotTriggerReconnect() throws Exception {
+ mGatewayConnection
+ .getUnderlyingNetworkTrackerCallback()
+ .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState());
+ }
+
+ @Test
+ public void testChildSessionClosedTriggersDisconnect() throws Exception {
+ getChildSessionCallback().onClosed();
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+ verify(mIkeSession).close();
+ }
+
+ @Test
+ public void testIkeSessionClosedTriggersDisconnect() throws Exception {
+ getIkeSessionCallback().onClosed();
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState());
+ verify(mIkeSession).close();
+ }
+}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
index 4ecd215..8643d8a 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
@@ -44,7 +44,13 @@
@Test
public void testEnterWhileNotRunningTriggersQuit() throws Exception {
final VcnGatewayConnection vgc =
- new VcnGatewayConnection(mVcnContext, TEST_SUB_GRP, mConfig, mDeps);
+ new VcnGatewayConnection(
+ mVcnContext,
+ TEST_SUB_GRP,
+ TEST_SUBSCRIPTION_SNAPSHOT,
+ mConfig,
+ mGatewayStatusCallback,
+ mDeps);
vgc.setIsRunning(false);
vgc.transitionTo(vgc.mDisconnectedState);
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
new file mode 100644
index 0000000..d0fec55
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.server.vcn;
+
+import static com.android.server.vcn.VcnGatewayConnection.TEARDOWN_TIMEOUT_SECONDS;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.verify;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.TimeUnit;
+
+/** Tests for VcnGatewayConnection.DisconnectedState */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VcnGatewayConnectionDisconnectingStateTest extends VcnGatewayConnectionTestBase {
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mGatewayConnection.setIkeSession(mGatewayConnection.buildIkeSession());
+
+ mGatewayConnection.transitionTo(mGatewayConnection.mDisconnectingState);
+ mTestLooper.dispatchAll();
+ }
+
+ @Test
+ public void testIkeSessionClosed() throws Exception {
+ getIkeSessionCallback().onClosed();
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState());
+ }
+
+ @Test
+ public void testTimeoutExpired() throws Exception {
+ mTestLooper.moveTimeForward(TimeUnit.SECONDS.toMillis(TEARDOWN_TIMEOUT_SECONDS));
+ mTestLooper.dispatchAll();
+
+ verify(mMockIkeSession).kill();
+ }
+
+ @Test
+ public void testTeardown() throws Exception {
+ mGatewayConnection.teardownAsynchronously();
+ mTestLooper.dispatchAll();
+
+ // Should do nothing; already tearing down.
+ assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+ }
+}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
index d741e5c..bc6bee2 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
@@ -16,20 +16,36 @@
package com.android.server.vcn;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
-import android.annotation.NonNull;
-import android.content.Context;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.net.LinkProperties;
+import android.net.Network;
import android.net.NetworkCapabilities;
+import android.net.TelephonyNetworkSpecifier;
import android.net.vcn.VcnGatewayConnectionConfigTest;
+import android.net.vcn.VcnTransportInfo;
+import android.net.wifi.WifiInfo;
import android.os.ParcelUuid;
-import android.os.test.TestLooper;
+import android.os.Process;
import android.telephony.SubscriptionInfo;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
+
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -41,7 +57,9 @@
/** Tests for TelephonySubscriptionTracker */
@RunWith(AndroidJUnit4.class)
@SmallTest
-public class VcnGatewayConnectionTest {
+public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {
+ private static final int TEST_UID = Process.myUid();
+
private static final ParcelUuid TEST_PARCEL_UUID = new ParcelUuid(UUID.randomUUID());
private static final int TEST_SIM_SLOT_INDEX = 1;
private static final int TEST_SUBSCRIPTION_ID_1 = 2;
@@ -57,26 +75,67 @@
TEST_SUBID_TO_GROUP_MAP = Collections.unmodifiableMap(subIdToGroupMap);
}
- @NonNull private final Context mContext;
- @NonNull private final TestLooper mTestLooper;
- @NonNull private final VcnNetworkProvider mVcnNetworkProvider;
- @NonNull private final VcnGatewayConnection.Dependencies mDeps;
+ private WifiInfo mWifiInfo;
- public VcnGatewayConnectionTest() {
- mContext = mock(Context.class);
- mTestLooper = new TestLooper();
- mVcnNetworkProvider = mock(VcnNetworkProvider.class);
- mDeps = mock(VcnGatewayConnection.Dependencies.class);
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mWifiInfo = mock(WifiInfo.class);
+ }
+
+ private void verifyBuildNetworkCapabilitiesCommon(int transportType) {
+ final NetworkCapabilities underlyingCaps = new NetworkCapabilities();
+ underlyingCaps.addTransportType(transportType);
+ underlyingCaps.addCapability(NET_CAPABILITY_NOT_METERED);
+ underlyingCaps.addCapability(NET_CAPABILITY_NOT_ROAMING);
+
+ if (transportType == TRANSPORT_WIFI) {
+ underlyingCaps.setTransportInfo(mWifiInfo);
+ underlyingCaps.setOwnerUid(TEST_UID);
+ } else if (transportType == TRANSPORT_CELLULAR) {
+ underlyingCaps.setAdministratorUids(new int[] {TEST_UID});
+ underlyingCaps.setNetworkSpecifier(
+ new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID_1));
+ }
+
+ UnderlyingNetworkRecord record =
+ new UnderlyingNetworkRecord(
+ new Network(0), underlyingCaps, new LinkProperties(), false);
+ final NetworkCapabilities vcnCaps =
+ VcnGatewayConnection.buildNetworkCapabilities(
+ VcnGatewayConnectionConfigTest.buildTestConfig(), record);
+
+ assertTrue(vcnCaps.hasTransport(TRANSPORT_CELLULAR));
+ assertTrue(vcnCaps.hasCapability(NET_CAPABILITY_NOT_METERED));
+ assertTrue(vcnCaps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+ assertArrayEquals(new int[] {TEST_UID}, vcnCaps.getAdministratorUids());
+ assertTrue(vcnCaps.getTransportInfo() instanceof VcnTransportInfo);
+
+ final VcnTransportInfo info = (VcnTransportInfo) vcnCaps.getTransportInfo();
+ if (transportType == TRANSPORT_WIFI) {
+ assertEquals(mWifiInfo, info.getWifiInfo());
+ } else if (transportType == TRANSPORT_CELLULAR) {
+ assertEquals(TEST_SUBSCRIPTION_ID_1, info.getSubId());
+ }
}
@Test
- public void testBuildNetworkCapabilities() throws Exception {
- final NetworkCapabilities caps =
- VcnGatewayConnection.buildNetworkCapabilities(
- VcnGatewayConnectionConfigTest.buildTestConfig());
+ public void testBuildNetworkCapabilitiesUnderlyingWifi() throws Exception {
+ verifyBuildNetworkCapabilitiesCommon(TRANSPORT_WIFI);
+ }
- for (int exposedCapability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
- assertTrue(caps.hasCapability(exposedCapability));
- }
+ @Test
+ public void testBuildNetworkCapabilitiesUnderlyingCell() throws Exception {
+ verifyBuildNetworkCapabilitiesCommon(TRANSPORT_CELLULAR);
+ }
+
+ @Test
+ public void testSubscriptionSnapshotUpdateNotifiesUnderlyingNetworkTracker() {
+ final TelephonySubscriptionSnapshot updatedSnapshot =
+ mock(TelephonySubscriptionSnapshot.class);
+ mGatewayConnection.updateSubscriptionSnapshot(updatedSnapshot);
+
+ verify(mUnderlyingNetworkTracker).updateSubscriptionSnapshot(eq(updatedSnapshot));
}
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index 1725dd9..333b5b9 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -17,33 +17,47 @@
package com.android.server.vcn;
import static com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
+import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
import static com.android.server.vcn.VcnTestUtils.setupIpSecManager;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import android.annotation.NonNull;
import android.content.Context;
+import android.net.IpSecConfig;
import android.net.IpSecManager;
+import android.net.IpSecTransform;
import android.net.IpSecTunnelInterfaceResponse;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
+import android.net.ipsec.ike.ChildSessionCallback;
+import android.net.ipsec.ike.IkeSessionCallback;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.os.ParcelUuid;
import android.os.test.TestLooper;
import com.android.server.IpSecService;
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
import org.junit.Before;
+import org.mockito.ArgumentCaptor;
+import java.util.Collections;
import java.util.UUID;
public class VcnGatewayConnectionTestBase {
protected static final ParcelUuid TEST_SUB_GRP = new ParcelUuid(UUID.randomUUID());
- protected static final int TEST_IPSEC_TUNNEL_RESOURCE_ID = 1;
+ protected static final int TEST_IPSEC_SPI_VALUE = 0x1234;
+ protected static final int TEST_IPSEC_SPI_RESOURCE_ID = 1;
+ protected static final int TEST_IPSEC_TRANSFORM_RESOURCE_ID = 2;
+ protected static final int TEST_IPSEC_TUNNEL_RESOURCE_ID = 3;
+ protected static final int TEST_SUB_ID = 5;
protected static final String TEST_IPSEC_TUNNEL_IFACE = "IPSEC_IFACE";
protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_1 =
new UnderlyingNetworkRecord(
@@ -58,16 +72,22 @@
new LinkProperties(),
false /* blocked */);
+ protected static final TelephonySubscriptionSnapshot TEST_SUBSCRIPTION_SNAPSHOT =
+ new TelephonySubscriptionSnapshot(
+ Collections.singletonMap(TEST_SUB_ID, TEST_SUB_GRP), Collections.EMPTY_MAP);
+
@NonNull protected final Context mContext;
@NonNull protected final TestLooper mTestLooper;
@NonNull protected final VcnNetworkProvider mVcnNetworkProvider;
@NonNull protected final VcnContext mVcnContext;
@NonNull protected final VcnGatewayConnectionConfig mConfig;
+ @NonNull protected final VcnGatewayStatusCallback mGatewayStatusCallback;
@NonNull protected final VcnGatewayConnection.Dependencies mDeps;
@NonNull protected final UnderlyingNetworkTracker mUnderlyingNetworkTracker;
@NonNull protected final IpSecService mIpSecSvc;
+ protected VcnIkeSession mMockIkeSession;
protected VcnGatewayConnection mGatewayConnection;
public VcnGatewayConnectionTestBase() {
@@ -76,6 +96,7 @@
mVcnNetworkProvider = mock(VcnNetworkProvider.class);
mVcnContext = mock(VcnContext.class);
mConfig = VcnGatewayConnectionConfigTest.buildTestConfig();
+ mGatewayStatusCallback = mock(VcnGatewayStatusCallback.class);
mDeps = mock(VcnGatewayConnection.Dependencies.class);
mUnderlyingNetworkTracker = mock(UnderlyingNetworkTracker.class);
@@ -88,7 +109,7 @@
doReturn(mUnderlyingNetworkTracker)
.when(mDeps)
- .newUnderlyingNetworkTracker(any(), any(), any());
+ .newUnderlyingNetworkTracker(any(), any(), any(), any(), any());
}
@Before
@@ -100,6 +121,34 @@
TEST_IPSEC_TUNNEL_IFACE);
doReturn(resp).when(mIpSecSvc).createTunnelInterface(any(), any(), any(), any(), any());
- mGatewayConnection = new VcnGatewayConnection(mVcnContext, TEST_SUB_GRP, mConfig, mDeps);
+ mMockIkeSession = mock(VcnIkeSession.class);
+ doReturn(mMockIkeSession).when(mDeps).newIkeSession(any(), any(), any(), any(), any());
+
+ mGatewayConnection =
+ new VcnGatewayConnection(
+ mVcnContext,
+ TEST_SUB_GRP,
+ TEST_SUBSCRIPTION_SNAPSHOT,
+ mConfig,
+ mGatewayStatusCallback,
+ mDeps);
+ }
+
+ protected IpSecTransform makeDummyIpSecTransform() throws Exception {
+ return new IpSecTransform(mContext, new IpSecConfig());
+ }
+
+ protected IkeSessionCallback getIkeSessionCallback() {
+ ArgumentCaptor<IkeSessionCallback> captor =
+ ArgumentCaptor.forClass(IkeSessionCallback.class);
+ verify(mDeps).newIkeSession(any(), any(), any(), captor.capture(), any());
+ return captor.getValue();
+ }
+
+ protected ChildSessionCallback getChildSessionCallback() {
+ ArgumentCaptor<ChildSessionCallback> captor =
+ ArgumentCaptor.forClass(ChildSessionCallback.class);
+ verify(mDeps).newIkeSession(any(), any(), any(), any(), captor.capture());
+ return captor.getValue();
}
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnTest.java b/tests/vcn/java/com/android/server/vcn/VcnTest.java
new file mode 100644
index 0000000..66cbf84
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnTest.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.net.NetworkRequest;
+import android.net.vcn.VcnConfig;
+import android.net.vcn.VcnGatewayConnectionConfig;
+import android.net.vcn.VcnGatewayConnectionConfigTest;
+import android.os.ParcelUuid;
+import android.os.test.TestLooper;
+
+import com.android.server.VcnManagementService.VcnSafemodeCallback;
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
+import com.android.server.vcn.VcnNetworkProvider.NetworkRequestListener;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.util.Set;
+import java.util.UUID;
+
+public class VcnTest {
+ private static final String PKG_NAME = VcnTest.class.getPackage().getName();
+ private static final ParcelUuid TEST_SUB_GROUP = new ParcelUuid(new UUID(0, 0));
+ private static final int NETWORK_SCORE = 0;
+ private static final int PROVIDER_ID = 5;
+
+ private Context mContext;
+ private VcnContext mVcnContext;
+ private TelephonySubscriptionSnapshot mSubscriptionSnapshot;
+ private VcnNetworkProvider mVcnNetworkProvider;
+ private VcnSafemodeCallback mVcnSafemodeCallback;
+ private Vcn.Dependencies mDeps;
+
+ private ArgumentCaptor<VcnGatewayStatusCallback> mGatewayStatusCallbackCaptor;
+
+ private TestLooper mTestLooper;
+ private VcnGatewayConnectionConfig mGatewayConnectionConfig;
+ private VcnConfig mConfig;
+ private Vcn mVcn;
+
+ @Before
+ public void setUp() {
+ mContext = mock(Context.class);
+ mVcnContext = mock(VcnContext.class);
+ mSubscriptionSnapshot = mock(TelephonySubscriptionSnapshot.class);
+ mVcnNetworkProvider = mock(VcnNetworkProvider.class);
+ mVcnSafemodeCallback = mock(VcnSafemodeCallback.class);
+ mDeps = mock(Vcn.Dependencies.class);
+
+ mTestLooper = new TestLooper();
+
+ doReturn(PKG_NAME).when(mContext).getOpPackageName();
+ doReturn(mContext).when(mVcnContext).getContext();
+ doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper();
+ doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider();
+
+ // Setup VcnGatewayConnection instance generation
+ doAnswer((invocation) -> {
+ // Mock-within a doAnswer is safe, because it doesn't actually run nested.
+ return mock(VcnGatewayConnection.class);
+ }).when(mDeps).newVcnGatewayConnection(any(), any(), any(), any(), any());
+
+ mGatewayStatusCallbackCaptor = ArgumentCaptor.forClass(VcnGatewayStatusCallback.class);
+
+ final VcnConfig.Builder configBuilder = new VcnConfig.Builder(mContext);
+ for (final int capability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
+ configBuilder.addGatewayConnectionConfig(
+ VcnGatewayConnectionConfigTest.buildTestConfigWithExposedCaps(capability));
+ }
+ configBuilder.addGatewayConnectionConfig(VcnGatewayConnectionConfigTest.buildTestConfig());
+ mConfig = configBuilder.build();
+
+ mVcn =
+ new Vcn(
+ mVcnContext,
+ TEST_SUB_GROUP,
+ mConfig,
+ mSubscriptionSnapshot,
+ mVcnSafemodeCallback,
+ mDeps);
+ }
+
+ private NetworkRequestListener verifyAndGetRequestListener() {
+ ArgumentCaptor<NetworkRequestListener> mNetworkRequestListenerCaptor =
+ ArgumentCaptor.forClass(NetworkRequestListener.class);
+ verify(mVcnNetworkProvider).registerListener(mNetworkRequestListenerCaptor.capture());
+
+ return mNetworkRequestListenerCaptor.getValue();
+ }
+
+ private void startVcnGatewayWithCapabilities(
+ NetworkRequestListener requestListener, int... netCapabilities) {
+ final NetworkRequest.Builder requestBuilder = new NetworkRequest.Builder();
+ for (final int netCapability : netCapabilities) {
+ requestBuilder.addCapability(netCapability);
+ }
+
+ requestListener.onNetworkRequested(requestBuilder.build(), NETWORK_SCORE, PROVIDER_ID);
+ mTestLooper.dispatchAll();
+ }
+
+ @Test
+ public void testSubscriptionSnapshotUpdatesVcnGatewayConnections() {
+ final NetworkRequestListener requestListener = verifyAndGetRequestListener();
+ startVcnGatewayWithCapabilities(
+ requestListener, VcnGatewayConnectionConfigTest.EXPOSED_CAPS);
+
+ final Set<VcnGatewayConnection> gatewayConnections = mVcn.getVcnGatewayConnections();
+ assertFalse(gatewayConnections.isEmpty());
+
+ final TelephonySubscriptionSnapshot updatedSnapshot =
+ mock(TelephonySubscriptionSnapshot.class);
+
+ mVcn.updateSubscriptionSnapshot(updatedSnapshot);
+ mTestLooper.dispatchAll();
+
+ for (final VcnGatewayConnection gateway : gatewayConnections) {
+ verify(gateway).updateSubscriptionSnapshot(eq(updatedSnapshot));
+ }
+ }
+
+ @Test
+ public void testGatewayEnteringSafemodeNotifiesVcn() {
+ final NetworkRequestListener requestListener = verifyAndGetRequestListener();
+ for (final int capability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
+ startVcnGatewayWithCapabilities(requestListener, capability);
+ }
+
+ // Each Capability in EXPOSED_CAPS was split into a separate VcnGatewayConnection in #setUp.
+ // Expect one VcnGatewayConnection per capability.
+ final int numExpectedGateways = VcnGatewayConnectionConfigTest.EXPOSED_CAPS.length;
+
+ final Set<VcnGatewayConnection> gatewayConnections = mVcn.getVcnGatewayConnections();
+ assertEquals(numExpectedGateways, gatewayConnections.size());
+ verify(mDeps, times(numExpectedGateways))
+ .newVcnGatewayConnection(
+ eq(mVcnContext),
+ eq(TEST_SUB_GROUP),
+ eq(mSubscriptionSnapshot),
+ any(),
+ mGatewayStatusCallbackCaptor.capture());
+
+ // Doesn't matter which callback this gets - any Gateway entering Safemode should shut down
+ // all Gateways
+ final VcnGatewayStatusCallback statusCallback = mGatewayStatusCallbackCaptor.getValue();
+ statusCallback.onEnteredSafemode();
+ mTestLooper.dispatchAll();
+
+ for (final VcnGatewayConnection gatewayConnection : gatewayConnections) {
+ verify(gatewayConnection).teardownAsynchronously();
+ }
+ verify(mVcnNetworkProvider).unregisterListener(requestListener);
+ verify(mVcnSafemodeCallback).onEnteredSafemode();
+ }
+}