diff options
257 files changed, 7568 insertions, 2880 deletions
diff --git a/Android.bp b/Android.bp index 6a0bdc3f7fde..2ab33aced0e7 100644 --- a/Android.bp +++ b/Android.bp @@ -363,6 +363,7 @@ filegroup { ":framework-statsd-sources", ":framework-tethering-srcs", ":framework-wifi-updatable-sources", + ":ike-srcs", ":updatable-media-srcs", ], visibility: ["//visibility:private"], @@ -371,6 +372,7 @@ filegroup { java_library { name: "framework-updatable-stubs-module_libs_api", static_libs: [ + "android.net.ipsec.ike.stubs.module_lib", "framework-media.stubs.module_lib", "framework-mediaprovider.stubs.module_lib", "framework-permission.stubs.module_lib", @@ -387,6 +389,7 @@ java_library { name: "framework-all", installable: false, static_libs: [ + "android.net.ipsec.ike.impl", "framework-minus-apex", "framework-mediaprovider.impl", "framework-permission.impl", @@ -484,7 +487,7 @@ java_library { "android.hardware.vibrator-V1.3-java", "android.security.apc-java", "android.security.authorization-java", - "android.system.keystore2-java", + "android.system.keystore2-V1-java", "android.system.suspend.control.internal-java", "devicepolicyprotosnano", @@ -678,6 +681,7 @@ gensrcs { srcs: [ ":ipconnectivity-proto-src", ":libstats_atom_enum_protos", + ":libtombstone_proto-src", "core/proto/**/*.proto", "libs/incident/**/*.proto", ], diff --git a/StubLibraries.bp b/StubLibraries.bp index 6afed7a78a08..6cece60c484c 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -79,12 +79,13 @@ stubs_defaults { "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, @@ -302,6 +303,7 @@ java_library_static { name: "android_stubs_current", srcs: [ ":api-stubs-docs-non-updatable" ], static_libs: [ + "android.net.ipsec.ike.stubs", "art.module.public.api.stubs", "conscrypt.module.public.api.stubs", "framework-media.stubs", @@ -321,6 +323,7 @@ java_library_static { name: "android_system_stubs_current", srcs: [ ":system-api-stubs-docs-non-updatable" ], static_libs: [ + "android.net.ipsec.ike.stubs.system", "art.module.public.api.stubs", "conscrypt.module.public.api.stubs", "framework-media.stubs.system", @@ -356,6 +359,7 @@ java_library_static { static_libs: [ // Modules do not have test APIs, but we want to include their SystemApis, like we include // the SystemApi of framework-non-updatable-sources. + "android.net.ipsec.ike.stubs.system", "art.module.public.api.stubs", "conscrypt.module.public.api.stubs", "framework-media.stubs.system", @@ -392,7 +396,11 @@ java_library_static { "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 000000000000..18486af9d12c --- /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 000000000000..4f168ceb3c55 --- /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/apex/Android.bp b/apex/Android.bp index 1876110c9355..8310ba769f55 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -15,189 +15,3 @@ package { default_visibility: [":__subpackages__"], } - -mainline_stubs_args = - "--error UnhiddenSystemApi " + - "--hide BroadcastBehavior " + - "--hide CallbackInterface " + - "--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", - }, - - // installable implies we'll create a non-apex (platform) variant, which - // we shouldn't ordinarily need (and it can create issues), so disable that. - installable: false, - - // 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_visibility: [ - ":__subpackages__", - "//frameworks/base/libs/hwui", - "//frameworks/base/wifi", - "//packages/modules:__subpackages__", - "//packages/providers/MediaProvider:__subpackages__", - ], -} - -// 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", - }, - defaults_visibility: [ - ":__subpackages__", - "//packages/modules:__subpackages__", - ], -} - -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", - }, -} diff --git a/apex/OWNERS b/apex/OWNERS index bde2bec0816b..b3e81b925ddc 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/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java index bb94275fc409..15052f8b12a4 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 @@ public final class ConnectivityController extends RestrictingController implemen 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 @@ public final class ConnectivityController extends RestrictingController implemen 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 @@ public final class ConnectivityController extends RestrictingController implemen 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 @@ public final class ConnectivityController extends RestrictingController implemen } // 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/api/Android.bp b/api/Android.bp index fdfef4cb8a74..74e021108577 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -28,6 +28,7 @@ genrule { genrule { name: "frameworks-base-api-current.txt", srcs: [ + ":android.net.ipsec.ike{.public.api.txt}", ":art.module.public.api{.public.api.txt}", ":conscrypt.module.public.api{.public.api.txt}", ":framework-media{.public.api.txt}", @@ -61,6 +62,7 @@ genrule { genrule { name: "frameworks-base-api-current.srcjar", srcs: [ + ":android.net.ipsec.ike{.public.stubs.source}", ":api-stubs-docs-non-updatable", ":art.module.public.api{.public.stubs.source}", ":conscrypt.module.public.api{.public.stubs.source}", @@ -82,6 +84,7 @@ genrule { genrule { name: "frameworks-base-api-removed.txt", srcs: [ + ":android.net.ipsec.ike{.public.removed-api.txt}", ":art.module.public.api{.public.removed-api.txt}", ":conscrypt.module.public.api{.public.removed-api.txt}", ":framework-media{.public.removed-api.txt}", @@ -114,6 +117,7 @@ genrule { genrule { name: "frameworks-base-api-system-current.txt", srcs: [ + ":android.net.ipsec.ike{.system.api.txt}", ":framework-media{.system.api.txt}", ":framework-mediaprovider{.system.api.txt}", ":framework-permission{.system.api.txt}", @@ -144,6 +148,7 @@ genrule { genrule { name: "frameworks-base-api-system-removed.txt", srcs: [ + ":android.net.ipsec.ike{.system.removed-api.txt}", ":framework-media{.system.removed-api.txt}", ":framework-mediaprovider{.system.removed-api.txt}", ":framework-permission{.system.removed-api.txt}", @@ -174,6 +179,7 @@ genrule { genrule { name: "frameworks-base-api-module-lib-current.txt", srcs: [ + ":android.net.ipsec.ike{.module-lib.api.txt}", ":framework-media{.module-lib.api.txt}", ":framework-mediaprovider{.module-lib.api.txt}", ":framework-permission{.module-lib.api.txt}", @@ -203,6 +209,7 @@ genrule { genrule { name: "frameworks-base-api-module-lib-removed.txt", srcs: [ + ":android.net.ipsec.ike{.module-lib.removed-api.txt}", ":framework-media{.module-lib.removed-api.txt}", ":framework-mediaprovider{.module-lib.removed-api.txt}", ":framework-permission{.module-lib.removed-api.txt}", diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp index e21a6b288fb3..50f400122fe1 100644 --- a/cmds/idmap2/Android.bp +++ b/cmds/idmap2/Android.bp @@ -132,9 +132,6 @@ cc_test { "tests/XmlParserTests.cpp", "tests/ZipFileTests.cpp", ], - required: [ - "idmap2", - ], static_libs: ["libgmock"], target: { android: { @@ -163,9 +160,19 @@ cc_test { shared_libs: [ "libz", ], + data: [ + ":libz", + ":idmap2", + ], }, }, - data: ["tests/data/**/*.apk"], + data: [ + "tests/data/**/*.apk", + ], + compile_multilib: "first", + test_options: { + unit_test: true, + }, } cc_binary { diff --git a/cmds/idmap2/AndroidTest.xml b/cmds/idmap2/AndroidTest.xml deleted file mode 100644 index 5147f4e6cb4c..000000000000 --- a/cmds/idmap2/AndroidTest.xml +++ /dev/null @@ -1,26 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2018 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<configuration description="Config for idmap2_tests"> - <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> - <option name="cleanup" value="true" /> - <option name="push" value="idmap2_tests->/data/local/tmp/idmap2_tests" /> - </target_preparer> - <option name="test-suite-tag" value="idmap2_tests" /> - <test class="com.android.tradefed.testtype.GTest" > - <option name="native-test-device-path" value="/data/local/tmp" /> - <option name="module-name" value="idmap2_tests" /> - </test> -</configuration> diff --git a/config/preloaded-classes-denylist b/config/preloaded-classes-denylist index 8ab5273cd755..2360ef12efff 100644 --- a/config/preloaded-classes-denylist +++ b/config/preloaded-classes-denylist @@ -4,4 +4,3 @@ android.os.FileObserver 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 285506f0556d..7d30edee3a13 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -1142,6 +1142,7 @@ package android { 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 @@ package android.bluetooth.le { 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 @@ package android.content.pm { 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"; @@ -34322,6 +34325,55 @@ package android.provider { 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(); } @@ -36204,6 +36256,7 @@ package android.security.keystore { 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(); @@ -36240,6 +36293,7 @@ package android.security.keystore { 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); @@ -36262,6 +36316,7 @@ package android.security.keystore { 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(); @@ -36331,6 +36386,7 @@ package android.security.keystore { 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 { @@ -36340,6 +36396,7 @@ package android.security.keystore { 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(); @@ -36365,6 +36422,7 @@ package android.security.keystore { 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); @@ -39678,6 +39736,7 @@ package android.telephony { 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"; @@ -41154,7 +41213,9 @@ package android.telephony { 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"; @@ -41913,7 +41974,11 @@ package android.telephony.ims { } public class ImsRcsManager { + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @NonNull public android.telephony.ims.RcsUceAdapter getUceAdapter(); + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException; + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback); field public static final String ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN = "android.telephony.ims.action.SHOW_CAPABILITY_DISCOVERY_OPT_IN"; } diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index 854e8fd8a6f6..c7d96effba11 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -15,6 +15,7 @@ package android.net { } public class ConnectivityManager { + method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler); 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); } @@ -61,6 +62,16 @@ package android.net { 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 72fcc72169c3..e4173a53c82c 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -1613,6 +1613,7 @@ package android.bluetooth { field public static final int UUID_BYTES_128_BIT = 16; // 0x10 field public static final int UUID_BYTES_16_BIT = 2; // 0x2 field public static final int UUID_BYTES_32_BIT = 4; // 0x4 + field @NonNull public static final android.os.ParcelUuid VOLUME_CONTROL; } public final class BufferConstraint implements android.os.Parcelable { @@ -2131,6 +2132,7 @@ package android.content.pm { 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 @@ -6004,11 +6006,15 @@ package android.net { method public long getExpiryTimeMillis(); method public long getRefreshTimeMillis(); method @Nullable public android.net.Uri getUserPortalUrl(); + method public int getUserPortalUrlSource(); method @Nullable public String getVenueFriendlyName(); method @Nullable public android.net.Uri getVenueInfoUrl(); + method public int getVenueInfoUrlSource(); method public boolean isCaptive(); method public boolean isSessionExtendable(); method public void writeToParcel(@NonNull android.os.Parcel, int); + field public static final int CAPTIVE_PORTAL_DATA_SOURCE_OTHER = 0; // 0x0 + field public static final int CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT = 1; // 0x1 field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortalData> CREATOR; } @@ -6022,8 +6028,10 @@ package android.net { method @NonNull public android.net.CaptivePortalData.Builder setRefreshTime(long); method @NonNull public android.net.CaptivePortalData.Builder setSessionExtendable(boolean); method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri); + method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri, int); method @NonNull public android.net.CaptivePortalData.Builder setVenueFriendlyName(@Nullable String); method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri); + method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri, int); } public class ConnectivityManager { @@ -7592,6 +7600,7 @@ package android.os { 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(); @@ -8289,6 +8298,24 @@ package android.provider { 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 @@ -8416,6 +8443,11 @@ package android.security.keystore { 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 { @@ -10730,7 +10762,6 @@ package android.telephony.data { method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setSlotIndex(int); method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setThrottleExpiryTimeMillis(long); method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setTransportType(int); - field public static final long NO_THROTTLE_EXPIRY_TIME = -1L; // 0xffffffffffffffffL } public final class DataCallResponse implements android.os.Parcelable { @@ -11521,6 +11552,17 @@ package android.telephony.ims { ctor @Deprecated public ImsMmTelManager.RegistrationCallback(); } + public class ImsRcsManager { + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void addOnAvailabilityChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsRcsManager.OnAvailabilityChangedListener) throws android.telephony.ims.ImsException; + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAvailable(int, int) throws android.telephony.ims.ImsException; + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isCapable(int, int) throws android.telephony.ims.ImsException; + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void removeOnAvailabilityChangedListener(@NonNull android.telephony.ims.ImsRcsManager.OnAvailabilityChangedListener); + } + + public static interface ImsRcsManager.OnAvailabilityChangedListener { + method public void onAvailabilityChanged(int); + } + public final class ImsReasonInfo implements android.os.Parcelable { field public static final String EXTRA_MSG_SERVICE_NOT_AUTHORIZED = "Forbidden. Not Authorized for Service"; } @@ -12161,11 +12203,24 @@ package android.telephony.ims.feature { ctor public RcsFeature(@NonNull java.util.concurrent.Executor); method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy); method @NonNull public android.telephony.ims.stub.RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.stub.CapabilityExchangeEventListener); + method public final void notifyCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.RcsFeature.RcsImsCapabilities); method public void onFeatureReady(); method public void onFeatureRemoved(); + method public boolean queryCapabilityConfiguration(int, int); + method @NonNull public final android.telephony.ims.feature.RcsFeature.RcsImsCapabilities queryCapabilityStatus(); method public void removeCapabilityExchangeImpl(@NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase); } + public static class RcsFeature.RcsImsCapabilities extends android.telephony.ims.feature.ImsFeature.Capabilities { + ctor public RcsFeature.RcsImsCapabilities(int); + method public void addCapabilities(int); + method public boolean isCapable(int); + method public void removeCapabilities(int); + field public static final int CAPABILITY_TYPE_NONE = 0; // 0x0 + field public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1; // 0x1 + field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2 + } + } package android.telephony.ims.stub { @@ -12375,11 +12430,13 @@ package android.telephony.ims.stub { 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; diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 546e72b8f834..786eab212aed 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -12,12 +12,14 @@ package android { field public static final String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS"; field public static final String CONTROL_DEVICE_LIGHTS = "android.permission.CONTROL_DEVICE_LIGHTS"; field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES"; + field public static final String KEEP_UNINSTALLED_PACKAGES = "android.permission.KEEP_UNINSTALLED_PACKAGES"; field public static final String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS"; field public static final String MANAGE_CRATES = "android.permission.MANAGE_CRATES"; field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS"; 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.bp b/core/java/Android.bp index fb27f74211fb..af5df769ffad 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -7,3 +7,8 @@ filegroup { name: "IDropBoxManagerService.aidl", srcs: ["com/android/internal/os/IDropBoxManagerService.aidl"], } + +filegroup { + name: "ITracingServiceProxy.aidl", + srcs: ["android/tracing/ITracingServiceProxy.aidl"], +} diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS index e6aa7a77357c..1ff64dbe6d2e 100644 --- a/core/java/android/app/OWNERS +++ b/core/java/android/app/OWNERS @@ -59,7 +59,7 @@ per-file IInstantAppResolver.aidl = file:/services/core/java/com/android/server/ per-file InstantAppResolveInfo.aidl = file:/services/core/java/com/android/server/pm/OWNERS # ResourcesManager -per-file ResourcesManager.java = 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 000000000000..21c39a8828ad --- /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/compat/OWNERS b/core/java/android/app/compat/OWNERS new file mode 100644 index 000000000000..f8c3520e9fa8 --- /dev/null +++ b/core/java/android/app/compat/OWNERS @@ -0,0 +1 @@ +include /services/core/java/com/android/server/compat/OWNERS diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java index 1ddfe0d2479a..1d5dc1d5df1c 100644 --- a/core/java/android/app/usage/NetworkStatsManager.java +++ b/core/java/android/app/usage/NetworkStatsManager.java @@ -28,7 +28,6 @@ import android.content.Context; 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.DataUnit; 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 @@ public class NetworkStatsManager { 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/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java index c0736a6b7bba..d82cf19e8822 100644 --- a/core/java/android/bluetooth/BluetoothUuid.java +++ b/core/java/android/bluetooth/BluetoothUuid.java @@ -167,6 +167,11 @@ public final class BluetoothUuid { /** @hide */ @NonNull @SystemApi + public static final ParcelUuid VOLUME_CONTROL = + ParcelUuid.fromString("00001844-0000-1000-8000-00805F9B34FB"); + /** @hide */ + @NonNull + @SystemApi public static final ParcelUuid BASE_UUID = ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB"); diff --git a/core/java/android/bluetooth/le/AdvertiseData.java b/core/java/android/bluetooth/le/AdvertiseData.java index 573b93232642..fa7ac2b27c92 100644 --- a/core/java/android/bluetooth/le/AdvertiseData.java +++ b/core/java/android/bluetooth/le/AdvertiseData.java @@ -44,7 +44,7 @@ public final class AdvertiseData implements Parcelable { @Nullable private final List<ParcelUuid> mServiceUuids; - @Nullable + @NonNull private final List<ParcelUuid> mServiceSolicitationUuids; private final SparseArray<byte[]> mManufacturerSpecificData; @@ -77,7 +77,7 @@ public final class AdvertiseData implements Parcelable { /** * 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 final class AdvertiseData implements Parcelable { 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 80a7b16ee761..0581ed5cbf22 100644 --- a/core/java/android/content/AutofillOptions.java +++ b/core/java/android/content/AutofillOptions.java @@ -17,6 +17,7 @@ package android.content; 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 @@ public final class AutofillOptions implements Parcelable { * List of allowlisted activities. */ @Nullable + @SuppressLint("NullableCollection") public ArraySet<ComponentName> whitelistedActivitiesForAugmentedAutofill; /** @@ -73,6 +75,7 @@ public final class AutofillOptions implements Parcelable { * 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 ef49e029db13..c296bb52e73d 100644 --- a/core/java/android/content/ContentCaptureOptions.java +++ b/core/java/android/content/ContentCaptureOptions.java @@ -17,6 +17,7 @@ package android.content; 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 @@ public final class ContentCaptureOptions implements Parcelable { * for all acitivites in the package). */ @Nullable + @SuppressLint("NullableCollection") public final ArraySet<ComponentName> whitelistedComponents; /** @@ -96,6 +98,7 @@ public final class ContentCaptureOptions implements Parcelable { */ 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/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 31beb6e6a565..04e0468fde90 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -59,6 +59,7 @@ import android.content.res.XmlResourceParser; 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.incremental.IncrementalManager; 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 abstract class PackageManager { 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 @@ public abstract class PackageManager { @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 @@ public abstract class PackageManager { * * @hide */ - @SuppressWarnings("HiddenAbstractMethod") + @SuppressWarnings({"HiddenAbstractMethod", "NullableCollection"}) @TestApi public abstract @Nullable String[] getNamesForUids(int[] uids); diff --git a/core/java/android/inputmethodservice/OWNERS b/core/java/android/inputmethodservice/OWNERS index e6a04dad25c2..d7db7c741364 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 0ac8f7e794dc..b06d515b3acf 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 @@ public final class DataUsageRequest implements Parcelable { } @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 ef0a436235eb..82ba156b08d0 100644 --- a/core/java/android/net/DhcpResults.java +++ b/core/java/android/net/DhcpResults.java @@ -160,7 +160,7 @@ public final class DhcpResults implements Parcelable { } @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 fe9141cb6a20..dfb1e996c55a 100644 --- a/core/java/android/net/INetworkPolicyListener.aidl +++ b/core/java/android/net/INetworkPolicyListener.aidl @@ -23,6 +23,6 @@ oneway interface INetworkPolicyListener { 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 29a3fdf59e8b..b016ed67c4d9 100644 --- a/core/java/android/net/INetworkPolicyManager.aidl +++ b/core/java/android/net/INetworkPolicyManager.aidl @@ -76,10 +76,11 @@ interface INetworkPolicyManager { 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/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java index 63fc5f288e61..183f500572bd 100644 --- a/core/java/android/net/Ikev2VpnProfile.java +++ b/core/java/android/net/Ikev2VpnProfile.java @@ -360,7 +360,7 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { } @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/IpSecTransform.java b/core/java/android/net/IpSecTransform.java index aa7811a3a1d0..b48c1fdaf1b2 100644 --- a/core/java/android/net/IpSecTransform.java +++ b/core/java/android/net/IpSecTransform.java @@ -19,6 +19,7 @@ import static android.net.IpSecManager.INVALID_RESOURCE_ID; 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 @@ public final class IpSecTransform implements AutoCloseable { /** * 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 011a04c26137..9a11be00663b 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; @@ -44,7 +45,7 @@ public final class MatchAllNetworkSpecifier extends NetworkSpecifier implements } @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 b644ed56ad8b..32b19a462218 100644 --- a/core/java/android/net/NetworkIdentity.java +++ b/core/java/android/net/NetworkIdentity.java @@ -18,19 +18,21 @@ package android.net; 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; /** * Network definition that includes strong identity. Analogous to combining - * {@link NetworkInfo} and an IMSI. + * {@link NetworkCapabilities} and an IMSI. * * @hide */ @@ -66,7 +68,7 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { } @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 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { 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 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { // 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 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { } /** - * 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 @@ -182,7 +160,7 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { */ public static NetworkIdentity buildNetworkIdentity(Context context, NetworkState state, boolean defaultNetwork, @NetworkType int subType) { - final int type = state.networkInfo.getType(); + final int legacyType = state.legacyNetworkType; String subscriberId = null; String networkId = null; @@ -193,7 +171,7 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { subscriberId = state.subscriberId; - if (type == TYPE_WIFI) { + if (legacyType == TYPE_WIFI) { if (state.networkCapabilities.getSsid() != null) { networkId = state.networkCapabilities.getSsid(); if (networkId == null) { @@ -206,7 +184,7 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { } } - return new NetworkIdentity(type, subType, subscriberId, networkId, roaming, metered, + return new NetworkIdentity(legacyType, subType, subscriberId, networkId, roaming, metered, defaultNetwork); } diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java index 4f05c9bbb2e3..8a0211c2ec2f 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 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> { } @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 82b035b08428..11146bd45fe4 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -34,6 +34,7 @@ import android.net.wifi.WifiInfo; 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 @@ public class NetworkPolicyManager { * @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 @@ public class NetworkPolicyManager { * @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 @@ public class NetworkPolicyManager { } /** + * 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 @@ public class NetworkPolicyManager { * @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 @@ public class NetworkPolicyManager { @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 @@ public class NetworkPolicyManager { @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 116e39ec53aa..caa6e455abc0 100644 --- a/core/java/android/net/NetworkScorerAppData.java +++ b/core/java/android/net/NetworkScorerAppData.java @@ -110,7 +110,7 @@ public final class NetworkScorerAppData implements Parcelable { } @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 e1ef8b5ea5c9..e466d2e626be 100644 --- a/core/java/android/net/NetworkState.java +++ b/core/java/android/net/NetworkState.java @@ -17,6 +17,7 @@ package android.net; import android.annotation.NonNull; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Parcel; @@ -41,6 +42,7 @@ public class NetworkState implements Parcelable { public final Network network; public final String subscriberId; public final String networkId; + public final int legacyNetworkType; private NetworkState() { networkInfo = null; @@ -49,17 +51,35 @@ public class NetworkState implements Parcelable { network = null; subscriberId = null; networkId = null; + legacyNetworkType = 0; } + public NetworkState(int legacyNetworkType, @NonNull LinkProperties linkProperties, + @NonNull NetworkCapabilities networkCapabilities, @NonNull Network network, + @Nullable String subscriberId, @Nullable String networkId) { + this(legacyNetworkType, new NetworkInfo(legacyNetworkType, 0, null, null), linkProperties, + networkCapabilities, network, subscriberId, networkId); + } + + // Constructor that used internally in ConnectivityService mainline module. public NetworkState(@NonNull NetworkInfo networkInfo, @NonNull LinkProperties linkProperties, @NonNull NetworkCapabilities networkCapabilities, @NonNull Network network, String subscriberId, String networkId) { + this(networkInfo.getType(), networkInfo, linkProperties, + networkCapabilities, network, subscriberId, networkId); + } + + public NetworkState(int legacyNetworkType, @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; this.network = network; this.subscriberId = subscriberId; this.networkId = networkId; + this.legacyNetworkType = legacyNetworkType; // This object is an atomic view of a network, so the various components // should always agree on roaming state. @@ -80,6 +100,7 @@ public class NetworkState implements Parcelable { network = in.readParcelable(null); subscriberId = in.readString(); networkId = in.readString(); + legacyNetworkType = in.readInt(); } @Override @@ -95,6 +116,7 @@ public class NetworkState implements Parcelable { out.writeParcelable(network, flags); out.writeString(subscriberId); out.writeString(networkId); + out.writeInt(legacyNetworkType); } @UnsupportedAppUsage diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java index cf40ce58d49d..d42beae601ed 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -412,7 +412,7 @@ public final class NetworkStats implements Parcelable { /** @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 72be835a1cea..aa61e03b285c 100644 --- a/core/java/android/net/NetworkTemplate.java +++ b/core/java/android/net/NetworkTemplate.java @@ -48,6 +48,7 @@ import android.util.Log; 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 @@ public class NetworkTemplate implements Parcelable { 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 @@ public class NetworkTemplate implements Parcelable { } @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 6a8e3f9c01f2..5e56164cc82c 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 @@ package android.net; 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 final class OemNetworkPreferences implements Parcelable { 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 @@ public final class OemNetworkPreferences implements Parcelable { * @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 @@ public final class OemNetworkPreferences implements Parcelable { } } + 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 @@ public final class OemNetworkPreferences implements Parcelable { @Override public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { - dest.writeSparseArray(mNetworkMappings); + dest.writeBundle(mNetworkMappings); } @Override @@ -187,7 +214,7 @@ public final class OemNetworkPreferences implements Parcelable { @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 3f2aa17f263e..012410b6b7b7 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 @@ public final class StringNetworkSpecifier extends NetworkSpecifier implements Pa } @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 33c71d5b312a..334823373e0d 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 @@ public final class TelephonyNetworkSpecifier extends NetworkSpecifier implements } @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 d75c43ddb318..3bc0f9ca4e6a 100644 --- a/core/java/android/net/UidRange.java +++ b/core/java/android/net/UidRange.java @@ -18,6 +18,7 @@ package android.net; import static android.os.UserHandle.PER_USER_RANGE; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -81,7 +82,7 @@ public final class UidRange implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/net/UnderlyingNetworkInfo.java b/core/java/android/net/UnderlyingNetworkInfo.java index 8fb4832e06c8..7bf923123910 100644 --- a/core/java/android/net/UnderlyingNetworkInfo.java +++ b/core/java/android/net/UnderlyingNetworkInfo.java @@ -16,11 +16,15 @@ 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; @@ -30,6 +34,7 @@ import java.util.Objects; * * @hide */ +@SystemApi(client = MODULE_LIBRARIES) public final class UnderlyingNetworkInfo implements Parcelable { /** The owner of this network. */ public final int ownerUid; @@ -46,7 +51,7 @@ public final class UnderlyingNetworkInfo implements Parcelable { Objects.requireNonNull(underlyingIfaces); this.ownerUid = ownerUid; this.iface = iface; - this.underlyingIfaces = underlyingIfaces; + this.underlyingIfaces = Collections.unmodifiableList(new ArrayList<>(underlyingIfaces)); } private UnderlyingNetworkInfo(@NonNull Parcel in) { diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java index 1cb4fe8cf4e7..ffe4f3c688cf 100644 --- a/core/java/android/net/Uri.java +++ b/core/java/android/net/Uri.java @@ -343,7 +343,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { * 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/VpnTransportInfo.java b/core/java/android/net/VpnTransportInfo.java new file mode 100644 index 000000000000..082fa58f8ac2 --- /dev/null +++ b/core/java/android/net/VpnTransportInfo.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.SparseArray; + +import com.android.internal.util.MessageUtils; + +import java.util.Objects; + +/** @hide */ +public final class VpnTransportInfo implements TransportInfo, Parcelable { + private static final SparseArray<String> sTypeToString = + MessageUtils.findMessageNames(new Class[]{VpnManager.class}, new String[]{"TYPE_VPN_"}); + + /** Type of this VPN. */ + @VpnManager.VpnType public final int type; + + public VpnTransportInfo(@VpnManager.VpnType int type) { + this.type = type; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof VpnTransportInfo)) return false; + + VpnTransportInfo that = (VpnTransportInfo) o; + return this.type == that.type; + } + + @Override + public int hashCode() { + return Objects.hash(type); + } + + @Override + public String toString() { + final String typeString = sTypeToString.get(type, "VPN_TYPE_???"); + return String.format("VpnTransportInfo{%s}", typeString); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(type); + } + + public static final @NonNull Creator<VpnTransportInfo> CREATOR = + new Creator<VpnTransportInfo>() { + public VpnTransportInfo createFromParcel(Parcel in) { + return new VpnTransportInfo(in.readInt()); + } + public VpnTransportInfo[] newArray(int size) { + return new VpnTransportInfo[size]; + } + }; +} diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java index fa090f59a8b9..1a38338c26aa 100644 --- a/core/java/android/net/vcn/VcnManager.java +++ b/core/java/android/net/vcn/VcnManager.java @@ -28,8 +28,10 @@ import android.os.RemoteException; import android.os.ServiceSpecificException; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.annotations.VisibleForTesting.Visibility; import java.io.IOException; +import java.util.Collections; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; @@ -67,8 +69,7 @@ import java.util.concurrent.Executor; public class VcnManager { @NonNull private static final String TAG = VcnManager.class.getSimpleName(); - @VisibleForTesting - public static final Map< + private static final Map< VcnUnderlyingNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder> REGISTERED_POLICY_LISTENERS = new ConcurrentHashMap<>(); @@ -88,6 +89,18 @@ public class VcnManager { mService = requireNonNull(service, "missing service"); } + /** + * Get all currently registered VcnUnderlyingNetworkPolicyListeners for testing purposes. + * + * @hide + */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + @NonNull + public static Map<VcnUnderlyingNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder> + getAllPolicyListeners() { + return Collections.unmodifiableMap(REGISTERED_POLICY_LISTENERS); + } + // TODO: Make setVcnConfig(), clearVcnConfig() Public API /** * Sets the VCN configuration for a given subscription group. diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java index d7c2e0522b0f..64ab074f397a 100644 --- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java +++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java @@ -97,6 +97,11 @@ public final class ApduServiceInfo implements Parcelable { 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 @@ public final class ApduServiceInfo implements Parcelable { 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 @@ public final class ApduServiceInfo implements Parcelable { 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 @@ public final class ApduServiceInfo implements Parcelable { 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 @@ public final class ApduServiceInfo implements Parcelable { 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 @@ public final class ApduServiceInfo implements Parcelable { 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 @@ public final class ApduServiceInfo implements Parcelable { 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 @@ public final class ApduServiceInfo implements Parcelable { 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 @@ public final class ApduServiceInfo implements Parcelable { } } 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 0185ba444ca4..16d041ac60f2 100644 --- a/core/java/android/os/BinderProxy.java +++ b/core/java/android/os/BinderProxy.java @@ -547,7 +547,8 @@ public final class BinderProxy implements IBinder { } 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 @@ public final class BinderProxy implements IBinder { * 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/Debug.java b/core/java/android/os/Debug.java index 27dc6e0ada43..c82149e390ba 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -2551,6 +2551,14 @@ public final class Debug public static native long getZramFreeKb(); /** + * Return total memory size in kilobytes for exported DMA-BUFs or -1 if + * the DMA-BUF sysfs stats at /sys/kernel/dmabuf/buffers could not be read. + * + * @hide + */ + public static native long getDmabufTotalExportedKb(); + + /** * Return memory size in kilobytes allocated for ION heaps or -1 if * /sys/kernel/ion/total_heaps_kb could not be read. * @@ -2559,6 +2567,14 @@ public final class Debug public static native long getIonHeapsSizeKb(); /** + * Return memory size in kilobytes allocated for DMA-BUF heap pools or -1 if + * /sys/kernel/dma_heap/total_pools_kb could not be read. + * + * @hide + */ + public static native long getDmabufHeapPoolsSizeKb(); + + /** * Return memory size in kilobytes allocated for ION pools or -1 if * /sys/kernel/ion/total_pools_kb could not be read. * @@ -2567,13 +2583,13 @@ public final class Debug public static native long getIonPoolsSizeKb(); /** - * Return ION memory mapped by processes in kB. + * Return DMA-BUF memory mapped by processes in kB. * Notes: * * Warning: Might impact performance as it reads /proc/<pid>/maps files for each process. * * @hide */ - public static native long getIonMappedSizeKb(); + public static native long getDmabufMappedSizeKb(); /** * Return memory size in kilobytes used by GPU. diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 6acdcc4722d2..f0a99ed5c2fd 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -3691,4 +3691,9 @@ public final class Parcel { public long getBlobAshmemSize() { return nativeGetBlobAshmemSize(mNativePtr); } + + /** @hide */ + /*package*/ boolean ownsNativeParcelObject() { + return mOwnsNativeParcelObject; + } } diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java index bef876f92a01..3d539a604b46 100644 --- a/core/java/android/os/UserHandle.java +++ b/core/java/android/os/UserHandle.java @@ -295,6 +295,19 @@ public final class UserHandle implements Parcelable { } /** + * 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 cb1509af66ac..cb06515910bf 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 000000000000..2efc21229422 --- /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 f994d2930cd9..a79b197d3faa 100644 --- a/core/java/android/security/keymaster/KeymasterDefs.java +++ b/core/java/android/security/keymaster/KeymasterDefs.java @@ -80,6 +80,7 @@ public final class KeymasterDefs { 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 final class KeymasterDefs { 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; @@ -316,6 +326,10 @@ public final class KeymasterDefs { 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/resumeonreboot/ResumeOnRebootService.java b/core/java/android/service/resumeonreboot/ResumeOnRebootService.java index 4ebaa96f4be2..ad49ffd57dc2 100644 --- a/core/java/android/service/resumeonreboot/ResumeOnRebootService.java +++ b/core/java/android/service/resumeonreboot/ResumeOnRebootService.java @@ -87,7 +87,9 @@ public abstract class ResumeOnRebootService extends Service { * 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 @@ public abstract class ResumeOnRebootService extends Service { * 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 @@ public abstract class ResumeOnRebootService extends Service { * 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/tracing/ITracingServiceProxy.aidl b/core/java/android/tracing/ITracingServiceProxy.aidl new file mode 100644 index 000000000000..4520db3915a2 --- /dev/null +++ b/core/java/android/tracing/ITracingServiceProxy.aidl @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.tracing; + +/** + * Binder interface for the TracingServiceProxy running in system_server. + * + * {@hide} + */ +interface ITracingServiceProxy +{ + /** + * Notifies system tracing app that a tracing session has ended. If a session is repurposed + * for use in a bugreport, sessionStolen can be set to indicate that tracing has ended but + * there is no buffer available to dump. + */ + oneway void notifyTraceSessionEnded(boolean sessionStolen); +} diff --git a/core/java/android/tracing/OWNERS b/core/java/android/tracing/OWNERS new file mode 100644 index 000000000000..f5de4eb05c54 --- /dev/null +++ b/core/java/android/tracing/OWNERS @@ -0,0 +1,2 @@ +cfijalkovich@google.com +carmenjackson@google.com diff --git a/core/java/android/uwb/RangingManager.java b/core/java/android/uwb/RangingManager.java index 5ac95d49c1bb..c0d818774ba0 100644 --- a/core/java/android/uwb/RangingManager.java +++ b/core/java/android/uwb/RangingManager.java @@ -94,7 +94,8 @@ public class RangingManager extends android.uwb.IUwbRangingCallbacks.Stub { synchronized (this) { if (!hasSession(sessionHandle)) { Log.w(TAG, - "onRangingOpened - received unexpected SessionHandle: " + sessionHandle); + "onRangingOpenedFailed - received unexpected SessionHandle: " + + sessionHandle); return; } @@ -124,7 +125,7 @@ public class RangingManager extends android.uwb.IUwbRangingCallbacks.Stub { @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/uwb/SessionHandle.java b/core/java/android/uwb/SessionHandle.java index 928fcbdcf1c7..b23f5ad603ff 100644 --- a/core/java/android/uwb/SessionHandle.java +++ b/core/java/android/uwb/SessionHandle.java @@ -19,6 +19,8 @@ package android.uwb; import android.os.Parcel; import android.os.Parcelable; +import java.util.Objects; + /** * @hide */ @@ -73,6 +75,11 @@ public final class SessionHandle implements Parcelable { } @Override + public int hashCode() { + return Objects.hashCode(mId); + } + + @Override public String toString() { return "SessionHandle [id=" + mId + "]"; } diff --git a/core/java/android/view/FrameMetrics.java b/core/java/android/view/FrameMetrics.java index f8500fa9f0aa..6511a9429eea 100644 --- a/core/java/android/view/FrameMetrics.java +++ b/core/java/android/view/FrameMetrics.java @@ -149,7 +149,7 @@ public final class FrameMetrics { * <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 1c703ecf06ca..a17c012d16ad 100644 --- a/core/java/android/view/inputmethod/InlineSuggestionInfo.java +++ b/core/java/android/view/inputmethod/InlineSuggestionInfo.java @@ -82,6 +82,7 @@ public final class InlineSuggestionInfo implements Parcelable { 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 e6a04dad25c2..d7db7c741364 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/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java index 502680de9bcf..7b4fcbf16754 100644 --- a/core/java/android/window/TaskOrganizer.java +++ b/core/java/android/window/TaskOrganizer.java @@ -20,6 +20,7 @@ import android.annotation.BinderThread; 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 @@ public class TaskOrganizer extends WindowOrganizer { /** 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 @@ public class TaskOrganizer extends WindowOrganizer { /** 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/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 16991b472037..1270185e1ea5 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -1210,25 +1210,6 @@ public class ResolverActivity extends Activity implements 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/os/CPU_OWNERS b/core/java/com/android/internal/os/CPU_OWNERS new file mode 100644 index 000000000000..5d3473cc0c9e --- /dev/null +++ b/core/java/com/android/internal/os/CPU_OWNERS @@ -0,0 +1,3 @@ +connoro@google.com +dplotnikov@google.com +rslawik@google.com # Android Telemetry diff --git a/core/java/com/android/internal/os/OWNERS b/core/java/com/android/internal/os/OWNERS index 1b07aa0cf0b7..0aa54f556b92 100644 --- a/core/java/com/android/internal/os/OWNERS +++ b/core/java/com/android/internal/os/OWNERS @@ -1,5 +1,6 @@ per-file *Power* = file:/services/core/java/com/android/server/power/OWNERS per-file *Zygote* = file:/ZYGOTE_OWNERS +per-file *Cpu* = file:CPU_OWNERS # BatteryStats per-file BatterySipper.java = file:/BATTERY_STATS_OWNERS diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index 0381a75d722b..a41018000d77 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -103,7 +103,7 @@ public final class Zygote { */ 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 final class Zygote { */ 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. @@ -1103,4 +1096,11 @@ public final class Zygote { * fully-feature Memory Tagging, rather than the static Tagged Pointers. */ public static native boolean nativeSupportsTaggedPointers(); + + /** + * Returns the current native tagging level, as one of the + * MEMORY_TAG_LEVEL_* constants. Returns zero if no tagging is present, or + * we failed to determine the level. + */ + public static native int nativeCurrentTaggingLevel(); } diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 6335baa97e57..7fde5474ec92 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -791,9 +791,19 @@ public class ZygoteInit { Zygote.applyInvokeWithSystemProperty(parsedArgs); if (Zygote.nativeSupportsMemoryTagging()) { - /* The system server is more privileged than regular app processes, so it has async - * tag checks enabled on hardware that supports memory tagging. */ - parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_ASYNC; + /* The system server has ASYNC MTE by default, in order to allow + * system services to specify their own MTE level later, as you + * can't re-enable MTE once it's disabled. */ + String mode = SystemProperties.get("arm64.memtag.process.system_server", "async"); + if (mode.equals("async")) { + parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_ASYNC; + } else if (mode.equals("sync")) { + parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_SYNC; + } else if (!mode.equals("off")) { + /* When we have an invalid memory tag level, keep the current level. */ + parsedArgs.mRuntimeFlags |= Zygote.nativeCurrentTaggingLevel(); + Slog.e(TAG, "Unknown memory tag level for the system server: \"" + mode + "\""); + } } else if (Zygote.nativeSupportsTaggedPointers()) { /* Enable pointer tagging in the system server. Hardware support for this is present * in all ARMv8 CPUs. */ diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java index 0dd8b599a122..db7cbbca450e 100644 --- a/core/java/com/android/internal/os/ZygoteServer.java +++ b/core/java/com/android/internal/os/ZygoteServer.java @@ -492,10 +492,12 @@ class ZygoteServer { 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 @@ class ZygoteServer { } 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 ae566c3988cd..d284d5167843 100644 --- a/core/java/com/android/internal/widget/OWNERS +++ b/core/java/com/android/internal/widget/OWNERS @@ -5,3 +5,17 @@ per-file *LockPattern* = file:/services/core/java/com/android/server/locksetting 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 ac2361dff560..c46d7086ca80 100644 --- a/core/java/com/android/server/BootReceiver.java +++ b/core/java/com/android/server/BootReceiver.java @@ -23,7 +23,6 @@ import android.content.pm.IPackageManager; 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 @@ public class BootReceiver extends BroadcastReceiver { 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 @@ public class BootReceiver extends BroadcastReceiver { 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 @@ public class BootReceiver extends BroadcastReceiver { 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 @@ public class BootReceiver extends BroadcastReceiver { } } - 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 @@ public class BootReceiver extends BroadcastReceiver { } - private String getBootHeadersToLogAndUpdate() throws IOException { + private static String getBootHeadersToLogAndUpdate() throws IOException { final String oldHeaders = getPreviousBootHeaders(); final String newHeaders = getCurrentBootHeaders(); @@ -248,38 +243,27 @@ public class BootReceiver extends BroadcastReceiver { logFsMountTime(); addFsckErrorsToDropBoxAndLogFsStat(db, timestamps, headers, -LOG_SIZE, "SYSTEM_FSCK"); logSystemServerShutdownTimeMetrics(); + writeTimestamps(timestamps); + } - // 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"); - } + /** + * 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); - - // 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(); } private static void addLastkToDropBox( @@ -764,7 +748,7 @@ public class BootReceiver extends BroadcastReceiver { } } - 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/OWNERS b/core/jni/OWNERS index 2279d5796c02..35d1d7bd7946 100644 --- a/core/jni/OWNERS +++ b/core/jni/OWNERS @@ -5,6 +5,9 @@ per-file *Camera*,*camera* = shuzhenwang@google.com, yinchiayeh@google.com, zhij # Connectivity per-file android_net_* = codewiz@google.com, jchalard@google.com, lorenzo@google.com, reminv@google.com, satk@google.com +# CPU +per-file *Cpu* = file:/core/java/com/android/internal/os/CPU_OWNERS + # Display per-file android_hardware_display_* = file:/services/core/java/com/android/server/display/OWNERS diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index 28fc8ede1653..2a93b4629e27 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -43,6 +43,7 @@ #include <nativehelper/JNIPlatformHelp.h> #include <nativehelper/ScopedUtfChars.h> #include "jni.h" +#include <dmabufinfo/dmabuf_sysfs_stats.h> #include <dmabufinfo/dmabufinfo.h> #include <meminfo/procmeminfo.h> #include <meminfo/sysmeminfo.h> @@ -802,6 +803,16 @@ static jlong android_os_Debug_getIonHeapsSizeKb(JNIEnv* env, jobject clazz) { return heapsSizeKb; } +static jlong android_os_Debug_getDmabufTotalExportedKb(JNIEnv* env, jobject clazz) { + jlong dmabufTotalSizeKb = -1; + uint64_t size; + + if (dmabufinfo::GetDmabufTotalExportedKb(&size)) { + dmabufTotalSizeKb = size; + } + return dmabufTotalSizeKb; +} + static jlong android_os_Debug_getIonPoolsSizeKb(JNIEnv* env, jobject clazz) { jlong poolsSizeKb = -1; uint64_t size; @@ -813,8 +824,19 @@ static jlong android_os_Debug_getIonPoolsSizeKb(JNIEnv* env, jobject clazz) { return poolsSizeKb; } -static jlong android_os_Debug_getIonMappedSizeKb(JNIEnv* env, jobject clazz) { - jlong ionPss = 0; +static jlong android_os_Debug_getDmabufHeapPoolsSizeKb(JNIEnv* env, jobject clazz) { + jlong poolsSizeKb = -1; + uint64_t size; + + if (meminfo::ReadDmabufHeapPoolsSizeKb(&size)) { + poolsSizeKb = size; + } + + return poolsSizeKb; +} + +static jlong android_os_Debug_getDmabufMappedSizeKb(JNIEnv* env, jobject clazz) { + jlong dmabufPss = 0; std::vector<dmabufinfo::DmaBuffer> dmabufs; std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir("/proc"), closedir); @@ -838,10 +860,10 @@ static jlong android_os_Debug_getIonMappedSizeKb(JNIEnv* env, jobject clazz) { } for (const dmabufinfo::DmaBuffer& buf : dmabufs) { - ionPss += buf.size() / 1024; + dmabufPss += buf.size() / 1024; } - return ionPss; + return dmabufPss; } static jlong android_os_Debug_getGpuTotalUsageKb(JNIEnv* env, jobject clazz) { @@ -919,10 +941,14 @@ static const JNINativeMethod gMethods[] = { (void*)android_os_Debug_getFreeZramKb }, { "getIonHeapsSizeKb", "()J", (void*)android_os_Debug_getIonHeapsSizeKb }, + { "getDmabufTotalExportedKb", "()J", + (void*)android_os_Debug_getDmabufTotalExportedKb }, { "getIonPoolsSizeKb", "()J", (void*)android_os_Debug_getIonPoolsSizeKb }, - { "getIonMappedSizeKb", "()J", - (void*)android_os_Debug_getIonMappedSizeKb }, + { "getDmabufMappedSizeKb", "()J", + (void*)android_os_Debug_getDmabufMappedSizeKb }, + { "getDmabufHeapPoolsSizeKb", "()J", + (void*)android_os_Debug_getDmabufHeapPoolsSizeKb }, { "getGpuTotalUsageKb", "()J", (void*)android_os_Debug_getGpuTotalUsageKb }, { "isVmapStack", "()Z", diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp index 241570a7f9d3..0fdab722dd05 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 jobject android_os_Parcel_readFileDescriptor(JNIEnv* env, jclass clazz, j 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 jlong android_os_Parcel_freeBuffer(JNIEnv* env, jclass clazz, jlong nativ 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 581dc0848a28..f71b42c4793f 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 bool should_time_binder_calls() { } 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 @@ static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj, 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 @@ static const JNINativeMethod gBinderProxyMethods[] = { {"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 d7001d8d36ea..903ecaef4938 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -2526,6 +2526,36 @@ static jboolean com_android_internal_os_Zygote_nativeSupportsTaggedPointers(JNIE #endif } +static jint com_android_internal_os_Zygote_nativeCurrentTaggingLevel(JNIEnv* env, jclass) { +#if defined(__aarch64__) + int level = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0); + if (level < 0) { + ALOGE("Failed to get memory tag level: %s", strerror(errno)); + return 0; + } else if (!(level & PR_TAGGED_ADDR_ENABLE)) { + return 0; + } + // TBI is only possible on non-MTE hardware. + if (!mte_supported()) { + return MEMORY_TAG_LEVEL_TBI; + } + + switch (level & PR_MTE_TCF_MASK) { + case PR_MTE_TCF_NONE: + return 0; + case PR_MTE_TCF_SYNC: + return MEMORY_TAG_LEVEL_SYNC; + case PR_MTE_TCF_ASYNC: + return MEMORY_TAG_LEVEL_ASYNC; + default: + ALOGE("Unknown memory tagging level: %i", level); + return 0; + } +#else // defined(__aarch64__) + return 0; +#endif // defined(__aarch64__) +} + static const JNINativeMethod gMethods[] = { {"nativeForkAndSpecialize", "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/" @@ -2565,6 +2595,8 @@ static const JNINativeMethod gMethods[] = { (void*)com_android_internal_os_Zygote_nativeSupportsMemoryTagging}, {"nativeSupportsTaggedPointers", "()Z", (void*)com_android_internal_os_Zygote_nativeSupportsTaggedPointers}, + {"nativeCurrentTaggingLevel", "()I", + (void*)com_android_internal_os_Zygote_nativeCurrentTaggingLevel}, }; int register_com_android_internal_os_Zygote(JNIEnv* env) { diff --git a/core/proto/OWNERS b/core/proto/OWNERS index 748b4b4f5743..99fd21592411 100644 --- a/core/proto/OWNERS +++ b/core/proto/OWNERS @@ -16,6 +16,7 @@ ogunwale@google.com 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/net/networkrequest.proto b/core/proto/android/net/networkrequest.proto index 6794c8cd8acb..0041f199b448 100644 --- a/core/proto/android/net/networkrequest.proto +++ b/core/proto/android/net/networkrequest.proto @@ -63,6 +63,9 @@ message NetworkRequestProto { // higher-scoring network will not go into the background immediately, // but will linger and go into the background after the linger timeout. TYPE_BACKGROUND_REQUEST = 5; + // Like TRACK_DEFAULT, but tracks the system default network, instead of + // the default network of the calling application. + TYPE_TRACK_SYSTEM_DEFAULT = 6; } // The type of the request. This is only used by the system and is always // NONE elsewhere. diff --git a/core/proto/android/server/apphibernationservice.proto b/core/proto/android/server/apphibernationservice.proto new file mode 100644 index 000000000000..d341c4b2f0a8 --- /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 a668e8e6dd0f..43c87892c97b 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1673,7 +1673,7 @@ @SystemApi <p>Not for use by third-party applications. @hide --> <permission android:name="android.permission.RESTART_WIFI_SUBSYSTEM" - android:protectionLevel="signature|setup" /> + android:protectionLevel="signature|privileged" /> <!-- @SystemApi @hide Allows applications to toggle airplane mode. <p>Not for use by third-party or privileged applications. @@ -2499,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" @@ -3793,6 +3797,11 @@ <permission android:name="android.permission.MOVE_PACKAGE" android:protectionLevel="signature|privileged" /> + <!-- @TestApi Allows an application to keep uninstalled packages as apks. + @hide --> + <permission android:name="android.permission.KEEP_UNINSTALLED_PACKAGES" + android:protectionLevel="signature" /> + <!-- Allows an application to change whether an application component (other than its own) is enabled or not. <p>Not for use by third-party applications. --> diff --git a/core/res/OWNERS b/core/res/OWNERS index a30111b44382..9d739b90bcc5 100644 --- a/core/res/OWNERS +++ b/core/res/OWNERS @@ -6,9 +6,12 @@ dsandler@google.com 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/res/values/attrs.xml b/core/res/res/values/attrs.xml index 4b3d82a04b8b..9cc0690126e9 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/public.xml b/core/res/res/values/public.xml index a0be0681bd38..0874a77815b5 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/tests/coretests/assets/fonts/OWNERS b/core/tests/coretests/assets/fonts/OWNERS new file mode 100644 index 000000000000..b0e0b9deaddb --- /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 000000000000..b0e0b9deaddb --- /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 000000000000..b0e0b9deaddb --- /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/provider/OWNERS b/core/tests/coretests/src/android/provider/OWNERS new file mode 100644 index 000000000000..581da714f326 --- /dev/null +++ b/core/tests/coretests/src/android/provider/OWNERS @@ -0,0 +1 @@ +include /core/java/android/provider/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 000000000000..be3826007aa3 --- /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/coretests/src/com/android/internal/os/LooperStatsTest.java b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java index 7917a06cb9b7..2c1bbf0d0b83 100644 --- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java @@ -232,7 +232,7 @@ public final class LooperStatsTest { assertThat(entry3.handlerClassName).isEqualTo( "com.android.internal.os.LooperStatsTest$TestHandlerSecond"); assertThat(entry3.messageName).startsWith( - "com.android.internal.os.-$$Lambda$LooperStatsTest$"); + "com.android.internal.os.LooperStatsTest-$$ExternalSyntheticLambda"); assertThat(entry3.messageCount).isEqualTo(1); assertThat(entry3.recordedMessageCount).isEqualTo(1); assertThat(entry3.exceptionCount).isEqualTo(0); diff --git a/core/tests/coretests/src/com/android/internal/os/OWNERS b/core/tests/coretests/src/com/android/internal/os/OWNERS index 4068e2bc03b7..3f8f9e29c6a0 100644 --- a/core/tests/coretests/src/com/android/internal/os/OWNERS +++ b/core/tests/coretests/src/com/android/internal/os/OWNERS @@ -1 +1,4 @@ include /BATTERY_STATS_OWNERS + +# CPU +per-file *Cpu* = file:/core/java/com/android/internal/os/CPU_OWNERS diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index a664ee3a6d19..5b32641cc811 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -446,6 +446,7 @@ applications that come with the platform <!-- 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"> @@ -459,6 +460,8 @@ applications that come with the platform <permission name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND"/> <!-- Permissions required for quick settings tile --> <permission name="android.permission.STATUS_BAR"/> + <!-- Permissions required to query Betterbug --> + <permission name="android.permission.QUERY_ALL_PACKAGES"/> </privapp-permissions> <privapp-permissions package="com.android.tv"> diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl index 97da3cc6f80f..1ae6a631dbcb 100644 --- a/keystore/java/android/security/IKeyChainService.aidl +++ b/keystore/java/android/security/IKeyChainService.aidl @@ -46,6 +46,7 @@ interface IKeyChainService { 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/KeyStoreSecurityLevel.java b/keystore/java/android/security/KeyStoreSecurityLevel.java index 372add9b7ecb..d188b6525579 100644 --- a/keystore/java/android/security/KeyStoreSecurityLevel.java +++ b/keystore/java/android/security/KeyStoreSecurityLevel.java @@ -190,7 +190,7 @@ public class KeyStoreSecurityLevel { keyDescriptor.blob = wrappedKey; keyDescriptor.domain = wrappedKeyDescriptor.domain; - return handleExceptions(() -> mSecurityLevel.importWrappedKey(wrappedKeyDescriptor, + return handleExceptions(() -> mSecurityLevel.importWrappedKey(keyDescriptor, wrappingKeyDescriptor, maskingKey, args.toArray(new KeyParameter[args.size()]), authenticatorSpecs)); } diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java index b1b6306e0cf6..087151711138 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java @@ -23,7 +23,6 @@ import android.security.KeyStore; 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 @@ public class AndroidKeyStoreProvider extends Provider { 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 @@ public class AndroidKeyStoreProvider extends Provider { * @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 @@ public class AndroidKeyStoreProvider extends Provider { // 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) { @@ -443,14 +434,16 @@ public class AndroidKeyStoreProvider extends Provider { @NonNull public static java.security.KeyStore getKeyStoreForUid(int uid) throws KeyStoreException, NoSuchProviderException { - String providerName = PROVIDER_NAME; + final java.security.KeyStore.LoadStoreParameter loadParameter; if (android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) { - providerName = "AndroidKeyStoreLegacy"; + loadParameter = new android.security.keystore2.AndroidKeyStoreLoadStoreParameter( + KeyProperties.legacyUidToNamespace(uid)); + } else { + loadParameter = new AndroidKeyStoreLoadStoreParameter(uid); } - java.security.KeyStore result = - java.security.KeyStore.getInstance(providerName); + java.security.KeyStore result = java.security.KeyStore.getInstance(PROVIDER_NAME); try { - result.load(new AndroidKeyStoreLoadStoreParameter(uid)); + result.load(loadParameter); } catch (NoSuchAlgorithmException | CertificateException | IOException e) { throw new KeyStoreException( "Failed to load AndroidKeyStore KeyStore for UID " + uid, e); diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java index 3694d635422f..d2678c71813c 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java @@ -215,7 +215,8 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { // 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/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java index e9aac7ddb56d..c2a7b2ee6323 100644 --- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java @@ -274,6 +274,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu 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 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu 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 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu mUserConfirmationRequired = userConfirmationRequired; mUnlockedDeviceRequired = unlockedDeviceRequired; mCriticalToDeviceEncryption = criticalToDeviceEncryption; + mMaxUsageCount = maxUsageCount; } /** @@ -782,7 +785,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu } /** - * 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 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu } /** + * 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 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu 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 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu mUserConfirmationRequired = sourceSpec.isUserConfirmationRequired(); mUnlockedDeviceRequired = sourceSpec.isUnlockedDeviceRequired(); mCriticalToDeviceEncryption = sourceSpec.isCriticalToDeviceEncryption(); + mMaxUsageCount = sourceSpec.getMaxUsageCount(); } /** @@ -1553,6 +1569,47 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu } /** + * 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 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu 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 7158d0cf248e..f50efd2c3328 100644 --- a/keystore/java/android/security/keystore/KeyInfo.java +++ b/keystore/java/android/security/keystore/KeyInfo.java @@ -85,6 +85,7 @@ public class KeyInfo implements KeySpec { private final boolean mInvalidatedByBiometricEnrollment; private final boolean mUserConfirmationRequired; private final @KeyProperties.SecurityLevelEnum int mSecurityLevel; + private final int mRemainingUsageCount; /** * @hide @@ -109,7 +110,8 @@ public class KeyInfo implements KeySpec { 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 @@ public class KeyInfo implements KeySpec { mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment; mUserConfirmationRequired = userConfirmationRequired; mSecurityLevel = securityLevel; + mRemainingUsageCount = remainingUsageCount; } /** @@ -374,4 +377,15 @@ public class KeyInfo implements KeySpec { 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 014d6882be8d..3ebca6ad302d 100644 --- a/keystore/java/android/security/keystore/KeyProperties.java +++ b/keystore/java/android/security/keystore/KeyProperties.java @@ -20,6 +20,8 @@ import android.annotation.IntDef; 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; @@ -874,9 +876,18 @@ public abstract class KeyProperties { * 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 */ @@ -884,6 +895,8 @@ public abstract class KeyProperties { 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: @@ -900,6 +913,8 @@ public abstract class KeyProperties { 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: @@ -907,4 +922,9 @@ public abstract class KeyProperties { + 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 2e793dea3e05..76ce23efd05b 100644 --- a/keystore/java/android/security/keystore/KeyProtection.java +++ b/keystore/java/android/security/keystore/KeyProtection.java @@ -235,6 +235,7 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { private final boolean mUserConfirmationRequired; private final boolean mUnlockedDeviceRequired; private final boolean mIsStrongBoxBacked; + private final int mMaxUsageCount; private KeyProtection( Date keyValidityStart, @@ -256,7 +257,8 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { 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 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { mUserConfirmationRequired = userConfirmationRequired; mUnlockedDeviceRequired = unlockedDeviceRequired; mIsStrongBoxBacked = isStrongBoxBacked; + mMaxUsageCount = maxUsageCount; } /** @@ -548,6 +551,17 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { } /** + * 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 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { 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 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { } /** + * 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 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { 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 69c15cca68bf..8163472abdfb 100644 --- a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java @@ -108,6 +108,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { 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 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { 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 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { 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/AndroidKeyStoreHmacSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java index 8475ad9fd57b..0f777495a3fe 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java @@ -164,6 +164,9 @@ public abstract class AndroidKeyStoreHmacSpi extends MacSpi implements KeyStoreC List<KeyParameter> parameters = new ArrayList<>(); parameters.add(KeyStore2ParameterUtils.makeEnum( + KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_SIGN + )); + parameters.add(KeyStore2ParameterUtils.makeEnum( KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_HMAC )); parameters.add(KeyStore2ParameterUtils.makeEnum( diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java index 32650aeda1b1..5619585d9c3c 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java @@ -21,7 +21,6 @@ import android.security.KeyStoreSecurityLevel; import android.system.keystore2.Authorization; import android.system.keystore2.Domain; import android.system.keystore2.KeyDescriptor; -import android.util.Log; import java.security.Key; @@ -127,15 +126,6 @@ public class AndroidKeyStoreKey implements Key { return false; } - // If the key ids are equal and the class matches all the other fields cannot differ - // unless we have a bug. - if (!mAlgorithm.equals(other.mAlgorithm) - || !mAuthorizations.equals(other.mAuthorizations) - || !mDescriptor.equals(other.mDescriptor)) { - Log.e("AndroidKeyStoreKey", "Bug: key ids are identical, but key metadata" - + "differs."); - return false; - } return true; } } diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java index 233f352989ab..1575bb411562 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java @@ -366,6 +366,13 @@ public abstract class AndroidKeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { )); } + 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 df0e1462a492..70e30d2de5a1 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java @@ -585,6 +585,37 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato 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 75ac61a22cab..e1011155248e 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java @@ -352,14 +352,17 @@ public class AndroidKeyStoreProvider extends Provider { 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); } } diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java index 74503e1eecfa..fe05989c3846 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java @@ -95,6 +95,7 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { 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 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { 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 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { 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 07169cedc1d9..39607aeb3852 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java @@ -535,6 +535,12 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { 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 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { 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); } @@ -854,7 +866,8 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { try { response = mKeyStore.getKeyEntry(wrappingkey); } catch (android.security.KeyStoreException e) { - throw new KeyStoreException("Failed to load wrapping key.", e); + throw new KeyStoreException("Failed to import wrapped key. Keystore error code: " + + e.getErrorCode(), e); } KeyDescriptor wrappedKey = makeKeyDescriptor(alias); diff --git a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java index 4c8ab8d6c713..dcdd7defd752 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.KeyProperties; 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 @@ public abstract class KeyStore2ParameterUtils { } /** + * 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 @@ public abstract class KeyStore2ParameterUtils { 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/tracingproxy/Android.bp b/libs/tracingproxy/Android.bp new file mode 100644 index 000000000000..67f407ff7599 --- /dev/null +++ b/libs/tracingproxy/Android.bp @@ -0,0 +1,42 @@ +// 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. + +// Provides C++ wrappers for system services. + +cc_library_shared { + name: "libtracingproxy", + + aidl: { + export_aidl_headers: true, + include_dirs: [ + "frameworks/base/core/java", + ], + }, + + srcs: [ + ":ITracingServiceProxy.aidl", + ], + + shared_libs: [ + "libbinder", + "libutils", + ], + + cflags: [ + "-Wall", + "-Werror", + "-Wunused", + "-Wunreachable-code", + ], +} diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 33c69511d273..ccb74eb39642 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -1445,6 +1445,7 @@ public class LocationManager { @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/native/android/OWNERS b/native/android/OWNERS index ac5a89527ef0..d414ed4cd5e2 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/src/android/net/CaptivePortalData.java b/packages/Connectivity/framework/src/android/net/CaptivePortalData.java index 18467fad8ec4..f4b46e9f11ed 100644 --- a/packages/Connectivity/framework/src/android/net/CaptivePortalData.java +++ b/packages/Connectivity/framework/src/android/net/CaptivePortalData.java @@ -16,12 +16,15 @@ package android.net; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; 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; /** @@ -40,10 +43,29 @@ public final class CaptivePortalData implements Parcelable { private final long mExpiryTimeMillis; private final boolean mCaptive; private final String mVenueFriendlyName; + private final int mVenueInfoUrlSource; + private final int mTermsAndConditionsSource; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"CAPTIVE_PORTAL_DATA_SOURCE_"}, value = { + CAPTIVE_PORTAL_DATA_SOURCE_OTHER, + CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT}) + public @interface CaptivePortalDataSource {} + + /** + * Source of information: Other (default) + */ + public static final int CAPTIVE_PORTAL_DATA_SOURCE_OTHER = 0; + + /** + * Source of information: Wi-Fi Passpoint + */ + public static final int CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT = 1; private CaptivePortalData(long refreshTimeMillis, Uri userPortalUrl, Uri venueInfoUrl, boolean isSessionExtendable, long byteLimit, long expiryTimeMillis, boolean captive, - String venueFriendlyName) { + String venueFriendlyName, int venueInfoUrlSource, int termsAndConditionsSource) { mRefreshTimeMillis = refreshTimeMillis; mUserPortalUrl = userPortalUrl; mVenueInfoUrl = venueInfoUrl; @@ -52,11 +74,14 @@ public final class CaptivePortalData implements Parcelable { mExpiryTimeMillis = expiryTimeMillis; mCaptive = captive; mVenueFriendlyName = venueFriendlyName; + mVenueInfoUrlSource = venueInfoUrlSource; + mTermsAndConditionsSource = termsAndConditionsSource; } private CaptivePortalData(Parcel p) { this(p.readLong(), p.readParcelable(null), p.readParcelable(null), p.readBoolean(), - p.readLong(), p.readLong(), p.readBoolean(), p.readString()); + p.readLong(), p.readLong(), p.readBoolean(), p.readString(), p.readInt(), + p.readInt()); } @Override @@ -74,6 +99,8 @@ public final class CaptivePortalData implements Parcelable { dest.writeLong(mExpiryTimeMillis); dest.writeBoolean(mCaptive); dest.writeString(mVenueFriendlyName); + dest.writeInt(mVenueInfoUrlSource); + dest.writeInt(mTermsAndConditionsSource); } /** @@ -88,6 +115,9 @@ public final class CaptivePortalData implements Parcelable { private long mExpiryTime = -1; private boolean mCaptive; private String mVenueFriendlyName; + private @CaptivePortalDataSource int mVenueInfoUrlSource = CAPTIVE_PORTAL_DATA_SOURCE_OTHER; + private @CaptivePortalDataSource int mUserPortalUrlSource = + CAPTIVE_PORTAL_DATA_SOURCE_OTHER; /** * Create an empty builder. @@ -100,8 +130,8 @@ public final class CaptivePortalData implements Parcelable { public Builder(@Nullable CaptivePortalData data) { if (data == null) return; setRefreshTime(data.mRefreshTimeMillis) - .setUserPortalUrl(data.mUserPortalUrl) - .setVenueInfoUrl(data.mVenueInfoUrl) + .setUserPortalUrl(data.mUserPortalUrl, data.mTermsAndConditionsSource) + .setVenueInfoUrl(data.mVenueInfoUrl, data.mVenueInfoUrlSource) .setSessionExtendable(data.mIsSessionExtendable) .setBytesRemaining(data.mByteLimit) .setExpiryTime(data.mExpiryTimeMillis) @@ -123,7 +153,18 @@ public final class CaptivePortalData implements Parcelable { */ @NonNull public Builder setUserPortalUrl(@Nullable Uri userPortalUrl) { + return setUserPortalUrl(userPortalUrl, CAPTIVE_PORTAL_DATA_SOURCE_OTHER); + } + + /** + * Set the URL to be used for users to login to the portal, if captive, and the source of + * the data, see {@link CaptivePortalDataSource} + */ + @NonNull + public Builder setUserPortalUrl(@Nullable Uri userPortalUrl, + @CaptivePortalDataSource int source) { mUserPortalUrl = userPortalUrl; + mUserPortalUrlSource = source; return this; } @@ -132,7 +173,18 @@ public final class CaptivePortalData implements Parcelable { */ @NonNull public Builder setVenueInfoUrl(@Nullable Uri venueInfoUrl) { + return setVenueInfoUrl(venueInfoUrl, CAPTIVE_PORTAL_DATA_SOURCE_OTHER); + } + + /** + * Set the URL that can be used by users to view information about the network venue, and + * the source of the data, see {@link CaptivePortalDataSource} + */ + @NonNull + public Builder setVenueInfoUrl(@Nullable Uri venueInfoUrl, + @CaptivePortalDataSource int source) { mVenueInfoUrl = venueInfoUrl; + mVenueInfoUrlSource = source; return this; } @@ -188,7 +240,8 @@ public final class CaptivePortalData implements Parcelable { public CaptivePortalData build() { return new CaptivePortalData(mRefreshTime, mUserPortalUrl, mVenueInfoUrl, mIsSessionExtendable, mBytesRemaining, mExpiryTime, mCaptive, - mVenueFriendlyName); + mVenueFriendlyName, mVenueInfoUrlSource, + mUserPortalUrlSource); } } @@ -249,6 +302,22 @@ public final class CaptivePortalData implements Parcelable { } /** + * Get the information source of the Venue URL + * @return The source that the Venue URL was obtained from + */ + public @CaptivePortalDataSource int getVenueInfoUrlSource() { + return mVenueInfoUrlSource; + } + + /** + * Get the information source of the user portal URL + * @return The source that the user portal URL was obtained from + */ + public @CaptivePortalDataSource int getUserPortalUrlSource() { + return mTermsAndConditionsSource; + } + + /** * Get the venue friendly name */ @Nullable @@ -272,11 +341,12 @@ public final class CaptivePortalData implements Parcelable { @Override public int hashCode() { return Objects.hash(mRefreshTimeMillis, mUserPortalUrl, mVenueInfoUrl, - mIsSessionExtendable, mByteLimit, mExpiryTimeMillis, mCaptive, mVenueFriendlyName); + mIsSessionExtendable, mByteLimit, mExpiryTimeMillis, mCaptive, mVenueFriendlyName, + mVenueInfoUrlSource, mTermsAndConditionsSource); } @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 @@ -286,7 +356,9 @@ public final class CaptivePortalData implements Parcelable { && mByteLimit == other.mByteLimit && mExpiryTimeMillis == other.mExpiryTimeMillis && mCaptive == other.mCaptive - && Objects.equals(mVenueFriendlyName, other.mVenueFriendlyName); + && Objects.equals(mVenueFriendlyName, other.mVenueFriendlyName) + && mVenueInfoUrlSource == other.mVenueInfoUrlSource + && mTermsAndConditionsSource == other.mTermsAndConditionsSource; } @Override @@ -300,6 +372,8 @@ public final class CaptivePortalData implements Parcelable { + ", expiryTime: " + mExpiryTimeMillis + ", captive: " + mCaptive + ", venueFriendlyName: " + mVenueFriendlyName + + ", venueInfoUrlSource: " + mVenueInfoUrlSource + + ", termsAndConditionsSource: " + mTermsAndConditionsSource + "}"; } } diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java index 987dcc4898b5..0976b753e674 100644 --- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java +++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java @@ -21,6 +21,7 @@ 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; +import static android.net.NetworkRequest.Type.TRACK_SYSTEM_DEFAULT; import static android.net.QosCallback.QosCallbackRegistrationException; import android.annotation.CallbackExecutor; @@ -1368,7 +1369,7 @@ public class ConnectivityManager { public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId) { try { return mService.getDefaultNetworkCapabilitiesForUser( - userId, mContext.getOpPackageName()); + userId, mContext.getOpPackageName(), getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1450,7 +1451,8 @@ public class ConnectivityManager { @Nullable public NetworkCapabilities getNetworkCapabilities(@Nullable Network network) { try { - return mService.getNetworkCapabilities(network, mContext.getOpPackageName()); + return mService.getNetworkCapabilities( + network, mContext.getOpPackageName(), getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2142,7 +2144,7 @@ public class ConnectivityManager { */ // TODO: Remove method and replace with direct call once R code is pushed to AOSP private @Nullable String getAttributionTag() { - return null; + return mContext.getAttributionTag(); } /** @@ -3231,32 +3233,6 @@ public class ConnectivityManager { } } - /** {@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 @@ -3746,7 +3722,8 @@ public class ConnectivityManager { printStackTrace(); checkCallbackNotNull(callback); Preconditions.checkArgument( - reqType == TRACK_DEFAULT || need != null, "null NetworkCapabilities"); + reqType == TRACK_DEFAULT || reqType == TRACK_SYSTEM_DEFAULT || need != null, + "null NetworkCapabilities"); final NetworkRequest request; final String callingPackageName = mContext.getOpPackageName(); try { @@ -3761,7 +3738,8 @@ public class ConnectivityManager { Binder binder = new Binder(); if (reqType == LISTEN) { request = mService.listenForNetwork( - need, messenger, binder, callingPackageName); + need, messenger, binder, callingPackageName, + getAttributionTag()); } else { request = mService.requestNetwork( need, reqType.ordinal(), messenger, timeoutMs, binder, legacyType, @@ -4206,7 +4184,8 @@ public class ConnectivityManager { checkPendingIntentNotNull(operation); try { mService.pendingListenForNetwork( - request.networkCapabilities, operation, mContext.getOpPackageName()); + request.networkCapabilities, operation, mContext.getOpPackageName(), + getAttributionTag()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { @@ -4215,8 +4194,9 @@ public class ConnectivityManager { } /** - * Registers to receive notifications about changes in the system default network. The callbacks - * will continue to be called until either the application exits or + * Registers to receive notifications about changes in the application's default network. This + * may be a physical network or a virtual network, such as a VPN that applies to the + * application. The callbacks will continue to be called until either the application exits or * {@link #unregisterNetworkCallback(NetworkCallback)} is called. * * <p>To avoid performance issues due to apps leaking callbacks, the system will limit the @@ -4229,7 +4209,7 @@ public class ConnectivityManager { * {@link #unregisterNetworkCallback(NetworkCallback)}. * * @param networkCallback The {@link NetworkCallback} that the system will call as the - * system default network changes. + * application's default network changes. * The callback is invoked on the default internal Handler. * @throws RuntimeException if the app already has too many callbacks registered. */ @@ -4239,8 +4219,9 @@ public class ConnectivityManager { } /** - * Registers to receive notifications about changes in the system default network. The callbacks - * will continue to be called until either the application exits or + * Registers to receive notifications about changes in the application's default network. This + * may be a physical network or a virtual network, such as a VPN that applies to the + * application. The callbacks will continue to be called until either the application exits or * {@link #unregisterNetworkCallback(NetworkCallback)} is called. * * <p>To avoid performance issues due to apps leaking callbacks, the system will limit the @@ -4253,26 +4234,60 @@ public class ConnectivityManager { * {@link #unregisterNetworkCallback(NetworkCallback)}. * * @param networkCallback The {@link NetworkCallback} that the system will call as the - * system default network changes. + * application's default network changes. * @param handler {@link Handler} to specify the thread upon which the callback will be invoked. * @throws RuntimeException if the app already has too many callbacks registered. */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerDefaultNetworkCallback(@NonNull NetworkCallback networkCallback, @NonNull Handler handler) { - // This works because if the NetworkCapabilities are null, - // ConnectivityService takes them from the default request. - // - // Since the capabilities are exactly the same as the default request's - // capabilities, this request is guaranteed, at all times, to be - // satisfied by the same network, if any, that satisfies the default - // request, i.e., the system default network. CallbackHandler cbHandler = new CallbackHandler(handler); sendRequestForNetwork(null /* NetworkCapabilities need */, networkCallback, 0, TRACK_DEFAULT, TYPE_NONE, cbHandler); } /** + * Registers to receive notifications about changes in the system default network. The callbacks + * will continue to be called until either the application exits or + * {@link #unregisterNetworkCallback(NetworkCallback)} is called. + * + * This method should not be used to determine networking state seen by applications, because in + * many cases, most or even all application traffic may not use the default network directly, + * and traffic from different applications may go on different networks by default. As an + * example, if a VPN is connected, traffic from all applications might be sent through the VPN + * and not onto the system default network. Applications or system components desiring to do + * determine network state as seen by applications should use other methods such as + * {@link #registerDefaultNetworkCallback(NetworkCallback, Handler)}. + * + * <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 #requestNetwork} 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 networkCallback The {@link NetworkCallback} that the system will call as the + * system default network changes. + * @param handler {@link Handler} to specify the thread upon which the callback will be invoked. + * @throws RuntimeException if the app already has too many callbacks registered. + * + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + @SuppressLint({"ExecutorRegistration", "PairedRegistration"}) + @RequiresPermission(anyOf = { + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, + android.Manifest.permission.NETWORK_SETTINGS}) + public void registerSystemDefaultNetworkCallback(@NonNull NetworkCallback networkCallback, + @NonNull Handler handler) { + CallbackHandler cbHandler = new CallbackHandler(handler); + sendRequestForNetwork(null /* NetworkCapabilities need */, networkCallback, 0, + TRACK_SYSTEM_DEFAULT, TYPE_NONE, cbHandler); + } + + /** * Requests bandwidth update for a given {@link Network} and returns whether the update request * is accepted by ConnectivityService. Once accepted, ConnectivityService will poll underlying * network connection for updated bandwidth information. The caller will be notified via @@ -4871,9 +4886,13 @@ public class ConnectivityManager { } } - 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 diff --git a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl index 1b4d2e413943..f909d1362550 100644 --- a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl +++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl @@ -29,6 +29,7 @@ import android.net.NetworkCapabilities; 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; @@ -65,7 +66,7 @@ interface IConnectivityManager Network getNetworkForType(int networkType); Network[] getAllNetworks(); NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser( - int userId, String callingPackageName); + int userId, String callingPackageName, String callingAttributionTag); boolean isNetworkSupported(int networkType); @@ -74,7 +75,8 @@ interface IConnectivityManager LinkProperties getLinkPropertiesForType(int networkType); LinkProperties getLinkProperties(in Network network); - NetworkCapabilities getNetworkCapabilities(in Network network, String callingPackageName); + NetworkCapabilities getNetworkCapabilities(in Network network, String callingPackageName, + String callingAttributionTag); @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) NetworkState[] getAllNetworkState(); @@ -156,9 +158,6 @@ interface IConnectivityManager 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); @@ -178,10 +177,12 @@ interface IConnectivityManager void releasePendingNetworkRequest(in PendingIntent operation); NetworkRequest listenForNetwork(in NetworkCapabilities networkCapabilities, - in Messenger messenger, in IBinder binder, String callingPackageName); + in Messenger messenger, in IBinder binder, String callingPackageName, + String callingAttributionTag); void pendingListenForNetwork(in NetworkCapabilities networkCapabilities, - in PendingIntent operation, String callingPackageName); + in PendingIntent operation, String callingPackageName, + String callingAttributionTag); void releaseNetworkRequest(in NetworkRequest networkRequest); @@ -243,4 +244,6 @@ interface IConnectivityManager void registerQosSocketCallback(in QosSocketInfo socketInfo, in IQosCallback callback); void unregisterQosCallback(in IQosCallback callback); + + void setOemNetworkPreference(in OemNetworkPreferences preference); } diff --git a/packages/Connectivity/framework/src/android/net/IpConfiguration.java b/packages/Connectivity/framework/src/android/net/IpConfiguration.java index 0b205642b321..d5f8b2edb6bb 100644 --- a/packages/Connectivity/framework/src/android/net/IpConfiguration.java +++ b/packages/Connectivity/framework/src/android/net/IpConfiguration.java @@ -167,7 +167,7 @@ public final class IpConfiguration implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o == this) { return true; } diff --git a/packages/Connectivity/framework/src/android/net/IpPrefix.java b/packages/Connectivity/framework/src/android/net/IpPrefix.java index e7c801475c4d..bcb65fab8571 100644 --- a/packages/Connectivity/framework/src/android/net/IpPrefix.java +++ b/packages/Connectivity/framework/src/android/net/IpPrefix.java @@ -18,6 +18,7 @@ package android.net; 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 @@ public final class IpPrefix implements Parcelable { * @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/packages/Connectivity/framework/src/android/net/LinkAddress.java b/packages/Connectivity/framework/src/android/net/LinkAddress.java index 44d25a1ab0af..d1bdaa078cdc 100644 --- a/packages/Connectivity/framework/src/android/net/LinkAddress.java +++ b/packages/Connectivity/framework/src/android/net/LinkAddress.java @@ -349,7 +349,7 @@ public class LinkAddress implements Parcelable { * @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/packages/Connectivity/framework/src/android/net/LinkProperties.java b/packages/Connectivity/framework/src/android/net/LinkProperties.java index 486e2d74dd05..e41ed72b259c 100644 --- a/packages/Connectivity/framework/src/android/net/LinkProperties.java +++ b/packages/Connectivity/framework/src/android/net/LinkProperties.java @@ -1613,7 +1613,7 @@ public final class LinkProperties implements Parcelable { * @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/packages/Connectivity/framework/src/android/net/MacAddress.java b/packages/Connectivity/framework/src/android/net/MacAddress.java index c7116b41e80a..c83c23a4b66e 100644 --- a/packages/Connectivity/framework/src/android/net/MacAddress.java +++ b/packages/Connectivity/framework/src/android/net/MacAddress.java @@ -161,7 +161,7 @@ public final class MacAddress implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { return (o instanceof MacAddress) && ((MacAddress) o).mAddr == mAddr; } diff --git a/packages/Connectivity/framework/src/android/net/Network.java b/packages/Connectivity/framework/src/android/net/Network.java index b07bd68a0f50..46141e0d0c1e 100644 --- a/packages/Connectivity/framework/src/android/net/Network.java +++ b/packages/Connectivity/framework/src/android/net/Network.java @@ -17,6 +17,7 @@ package android.net; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; @@ -510,7 +511,7 @@ public class Network implements Parcelable { }; @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/packages/Connectivity/framework/src/android/net/NetworkAgent.java b/packages/Connectivity/framework/src/android/net/NetworkAgent.java index d22d82d1f4d0..27aa15d1e1e4 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkAgent.java +++ b/packages/Connectivity/framework/src/android/net/NetworkAgent.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; 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 @@ public abstract class NetworkAgent { * @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/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java index 8bfa77acd36d..9d67f0b84367 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java +++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java @@ -762,12 +762,14 @@ public final class NetworkCapabilities implements Parcelable { final int originalSignalStrength = mSignalStrength; final int originalOwnerUid = getOwnerUid(); final int[] originalAdministratorUids = getAdministratorUids(); + final TransportInfo originalTransportInfo = getTransportInfo(); clearAll(); mTransportTypes = (originalTransportTypes & TEST_NETWORKS_ALLOWED_TRANSPORTS) | (1 << TRANSPORT_TEST); mNetworkCapabilities = originalCapabilities & TEST_NETWORKS_ALLOWED_CAPABILITIES; mNetworkSpecifier = originalSpecifier; mSignalStrength = originalSignalStrength; + mTransportInfo = originalTransportInfo; // Only retain the owner and administrator UIDs if they match the app registering the remote // caller that registered the network. @@ -1786,6 +1788,15 @@ public final class NetworkCapabilities implements Parcelable { 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); @@ -1796,7 +1807,7 @@ public final class NetworkCapabilities implements Parcelable { 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()); @@ -1819,8 +1830,7 @@ public final class NetworkCapabilities implements Parcelable { 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()); @@ -1833,6 +1843,20 @@ public final class NetworkCapabilities implements Parcelable { 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 diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java index 04011fc6816e..b4a651c0607e 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java +++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java @@ -16,6 +16,22 @@ package android.net; +import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL; +import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; +import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND; +import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; +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_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_TEMPORARILY_NOT_METERED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; + import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -30,6 +46,8 @@ import android.os.Process; import android.text.TextUtils; import android.util.proto.ProtoOutputStream; +import java.util.Arrays; +import java.util.List; import java.util.Objects; import java.util.Set; @@ -86,17 +104,14 @@ public class NetworkRequest implements Parcelable { * callbacks about the single, highest scoring current network * (if any) that matches the specified NetworkCapabilities, or * - * - TRACK_DEFAULT, a hybrid of the two designed such that the - * framework will issue callbacks for the single, highest scoring - * current network (if any) that matches the capabilities of the - * default Internet request (mDefaultRequest), but which cannot cause - * the framework to either create or retain the existence of any - * specific network. Note that from the point of view of the request - * matching code, TRACK_DEFAULT is identical to REQUEST: its special - * behaviour is not due to different semantics, but to the fact that - * the system will only ever create a TRACK_DEFAULT with capabilities - * that are identical to the default request's capabilities, thus - * causing it to share fate in every way with the default request. + * - TRACK_DEFAULT, which causes the framework to issue callbacks for + * the single, highest scoring current network (if any) that will + * be chosen for an app, but which cannot cause the framework to + * either create or retain the existence of any specific network. + * + * - TRACK_SYSTEM_DEFAULT, which causes the framework to send callbacks + * for the network (if any) that satisfies the default Internet + * request. * * - BACKGROUND_REQUEST, like REQUEST but does not cause any networks * to retain the NET_CAPABILITY_FOREGROUND capability. A network with @@ -119,6 +134,7 @@ public class NetworkRequest implements Parcelable { TRACK_DEFAULT, REQUEST, BACKGROUND_REQUEST, + TRACK_SYSTEM_DEFAULT, }; /** @@ -156,8 +172,30 @@ public class NetworkRequest implements Parcelable { * needed in terms of {@link NetworkCapabilities} features */ public static class Builder { + /** + * Capabilities that are currently compatible with VCN networks. + */ + private static final List<Integer> VCN_SUPPORTED_CAPABILITIES = Arrays.asList( + NET_CAPABILITY_CAPTIVE_PORTAL, + NET_CAPABILITY_DUN, + NET_CAPABILITY_FOREGROUND, + NET_CAPABILITY_INTERNET, + NET_CAPABILITY_NOT_CONGESTED, + NET_CAPABILITY_NOT_METERED, + NET_CAPABILITY_NOT_RESTRICTED, + NET_CAPABILITY_NOT_ROAMING, + NET_CAPABILITY_NOT_SUSPENDED, + NET_CAPABILITY_NOT_VPN, + NET_CAPABILITY_PARTIAL_CONNECTIVITY, + NET_CAPABILITY_TEMPORARILY_NOT_METERED, + NET_CAPABILITY_TRUSTED, + NET_CAPABILITY_VALIDATED); + private final NetworkCapabilities mNetworkCapabilities; + // A boolean that represents the user modified NOT_VCN_MANAGED capability. + private boolean mModifiedNotVcnManaged = false; + /** * Default constructor for Builder. */ @@ -179,6 +217,7 @@ public class NetworkRequest implements Parcelable { // maybeMarkCapabilitiesRestricted() doesn't add back. final NetworkCapabilities nc = new NetworkCapabilities(mNetworkCapabilities); nc.maybeMarkCapabilitiesRestricted(); + deduceNotVcnManagedCapability(nc); return new NetworkRequest(nc, ConnectivityManager.TYPE_NONE, ConnectivityManager.REQUEST_ID_UNSET, Type.NONE); } @@ -195,6 +234,9 @@ public class NetworkRequest implements Parcelable { */ public Builder addCapability(@NetworkCapabilities.NetCapability int capability) { mNetworkCapabilities.addCapability(capability); + if (capability == NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED) { + mModifiedNotVcnManaged = true; + } return this; } @@ -206,6 +248,9 @@ public class NetworkRequest implements Parcelable { */ public Builder removeCapability(@NetworkCapabilities.NetCapability int capability) { mNetworkCapabilities.removeCapability(capability); + if (capability == NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED) { + mModifiedNotVcnManaged = true; + } return this; } @@ -263,6 +308,9 @@ public class NetworkRequest implements Parcelable { @NonNull public Builder clearCapabilities() { mNetworkCapabilities.clearAll(); + // If the caller explicitly clear all capabilities, the NOT_VCN_MANAGED capabilities + // should not be add back later. + mModifiedNotVcnManaged = true; return this; } @@ -382,6 +430,25 @@ public class NetworkRequest implements Parcelable { mNetworkCapabilities.setSignalStrength(signalStrength); return this; } + + /** + * Deduce the NET_CAPABILITY_NOT_VCN_MANAGED capability from other capabilities + * and user intention, which includes: + * 1. For the requests that don't have anything besides + * {@link #VCN_SUPPORTED_CAPABILITIES}, add the NET_CAPABILITY_NOT_VCN_MANAGED to + * allow the callers automatically utilize VCN networks if available. + * 2. For the requests that explicitly add or remove NET_CAPABILITY_NOT_VCN_MANAGED, + * do not alter them to allow user fire request that suits their need. + * + * @hide + */ + private void deduceNotVcnManagedCapability(final NetworkCapabilities nc) { + if (mModifiedNotVcnManaged) return; + for (final int cap : nc.getCapabilities()) { + if (!VCN_SUPPORTED_CAPABILITIES.contains(cap)) return; + } + nc.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); + } } // implement the Parcelable interface @@ -435,25 +502,7 @@ public class NetworkRequest implements Parcelable { * @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; } /** @@ -550,6 +599,8 @@ public class NetworkRequest implements Parcelable { return NetworkRequestProto.TYPE_REQUEST; case BACKGROUND_REQUEST: return NetworkRequestProto.TYPE_BACKGROUND_REQUEST; + case TRACK_SYSTEM_DEFAULT: + return NetworkRequestProto.TYPE_TRACK_SYSTEM_DEFAULT; default: return NetworkRequestProto.TYPE_UNKNOWN; } @@ -567,7 +618,7 @@ public class NetworkRequest implements Parcelable { 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/packages/Connectivity/framework/src/android/net/Proxy.java b/packages/Connectivity/framework/src/android/net/Proxy.java index 03b07e080add..9cd7ab2c3e40 100644 --- a/packages/Connectivity/framework/src/android/net/Proxy.java +++ b/packages/Connectivity/framework/src/android/net/Proxy.java @@ -30,8 +30,6 @@ import java.net.InetSocketAddress; 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 @@ public final class Proxy { @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 @@ public final class Proxy { 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/packages/Connectivity/framework/src/android/net/ProxyInfo.java b/packages/Connectivity/framework/src/android/net/ProxyInfo.java index c9bca2876b0a..229db0d717cd 100644 --- a/packages/Connectivity/framework/src/android/net/ProxyInfo.java +++ b/packages/Connectivity/framework/src/android/net/ProxyInfo.java @@ -23,6 +23,8 @@ import android.os.Parcel; 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 class ProxyInfo implements Parcelable { */ 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 @@ public class ProxyInfo implements Parcelable { } @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. diff --git a/packages/Connectivity/framework/src/android/net/RouteInfo.java b/packages/Connectivity/framework/src/android/net/RouteInfo.java index 94f849f006f3..5b6684ace052 100644 --- a/packages/Connectivity/framework/src/android/net/RouteInfo.java +++ b/packages/Connectivity/framework/src/android/net/RouteInfo.java @@ -534,7 +534,7 @@ public final class RouteInfo implements Parcelable { * 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 @@ public final class RouteInfo implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (!(o instanceof RouteKey)) { return false; } diff --git a/packages/Connectivity/framework/src/android/net/VpnManager.java b/packages/Connectivity/framework/src/android/net/VpnManager.java index c87b8279c4d6..1e30283a9e6c 100644 --- a/packages/Connectivity/framework/src/android/net/VpnManager.java +++ b/packages/Connectivity/framework/src/android/net/VpnManager.java @@ -21,6 +21,7 @@ import static com.android.internal.util.Preconditions.checkNotNull; 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.Intent; 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; @@ -52,13 +55,29 @@ import java.security.GeneralSecurityException; public class VpnManager { /** Type representing a lack of VPN @hide */ public static final int TYPE_VPN_NONE = -1; - /** VPN service type code @hide */ + + /** + * A VPN created by an app using the {@link VpnService} API. + * @hide + */ public static final int TYPE_VPN_SERVICE = 1; - /** Platform VPN type code @hide */ + + /** + * A VPN created using a {@link VpnManager} API such as {@link #startProvisionedVpnProfile}. + * @hide + */ public static final int TYPE_VPN_PLATFORM = 2; + /** + * An IPsec VPN created by the built-in LegacyVpnRunner. + * @deprecated new Android devices should use VPN_TYPE_PLATFORM instead. + * @hide + */ + @Deprecated + public static final int TYPE_VPN_LEGACY = 3; + /** @hide */ - @IntDef(value = {TYPE_VPN_NONE, TYPE_VPN_SERVICE, TYPE_VPN_PLATFORM}) + @IntDef(value = {TYPE_VPN_NONE, TYPE_VPN_SERVICE, TYPE_VPN_PLATFORM, TYPE_VPN_LEGACY}) @Retention(RetentionPolicy.SOURCE) public @interface VpnType {} @@ -161,4 +180,104 @@ public class VpnManager { 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/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java index c5d4fa9f1b40..cb610fc61142 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 @@ public class SystemSettingsValidators { 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/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 135356c864ec..c3fc019c6787 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -87,6 +87,7 @@ <!-- TODO(b/152310230): remove once APIs are confirmed to be sufficient --> <uses-permission android:name="com.android.permission.USE_INSTALLER_V2" /> <uses-permission android:name="android.permission.MOVE_PACKAGE" /> + <uses-permission android:name="android.permission.KEEP_UNINSTALLED_PACKAGES" /> <uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" /> <uses-permission android:name="android.permission.CLEAR_APP_CACHE" /> <uses-permission android:name="android.permission.ACCESS_INSTANT_APPS" /> @@ -121,6 +122,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"/> @@ -355,6 +358,7 @@ <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" /> + <uses-permission android:name="android.permission.BIND_RESUME_ON_REBOOT_SERVICE" /> <application android:label="@string/app_label" android:theme="@android:style/Theme.DeviceDefault.DayNight" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java index c5a35eaf3e6c..d18902a7796b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java @@ -27,12 +27,11 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.UserInfo; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; -import android.net.IConnectivityManager; import android.net.Network; import android.net.NetworkRequest; +import android.net.VpnManager; import android.os.Handler; import android.os.RemoteException; -import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; import android.security.KeyChain; @@ -75,7 +74,7 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi private final Context mContext; private final ConnectivityManager mConnectivityManager; - private final IConnectivityManager mConnectivityManagerService; + private final VpnManager mVpnManager; private final DevicePolicyManager mDevicePolicyManager; private final PackageManager mPackageManager; private final UserManager mUserManager; @@ -107,8 +106,7 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi context.getSystemService(Context.DEVICE_POLICY_SERVICE); mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); - mConnectivityManagerService = IConnectivityManager.Stub.asInterface( - ServiceManager.getService(Context.CONNECTIVITY_SERVICE)); + mVpnManager = context.getSystemService(VpnManager.class); mPackageManager = context.getPackageManager(); mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); mBgExecutor = bgExecutor; @@ -346,25 +344,19 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi private void updateState() { // Find all users with an active VPN SparseArray<VpnConfig> vpns = new SparseArray<>(); - try { - for (UserInfo user : mUserManager.getUsers()) { - VpnConfig cfg = mConnectivityManagerService.getVpnConfig(user.id); - if (cfg == null) { + for (UserInfo user : mUserManager.getUsers()) { + VpnConfig cfg = mVpnManager.getVpnConfig(user.id); + if (cfg == null) { + continue; + } else if (cfg.legacy) { + // Legacy VPNs should do nothing if the network is disconnected. Third-party + // VPN warnings need to continue as traffic can still go to the app. + LegacyVpnInfo legacyVpn = mVpnManager.getLegacyVpnInfo(user.id); + if (legacyVpn == null || legacyVpn.state != LegacyVpnInfo.STATE_CONNECTED) { continue; - } else if (cfg.legacy) { - // Legacy VPNs should do nothing if the network is disconnected. Third-party - // VPN warnings need to continue as traffic can still go to the app. - LegacyVpnInfo legacyVpn = mConnectivityManagerService.getLegacyVpnInfo(user.id); - if (legacyVpn == null || legacyVpn.state != LegacyVpnInfo.STATE_CONNECTED) { - continue; - } } - vpns.put(user.id, cfg); } - } catch (RemoteException rme) { - // Roll back to previous state - Log.e(TAG, "Unable to list active VPNs", rme); - return; + vpns.put(user.id, cfg); } mCurrentVpns = vpns; } diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java index 4d95ef13a2a8..6dcad255eee4 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.FLAG_ALT_FOCUSABLE_IM; 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 @@ public class AlwaysOnDisconnectedDialog extends AlertActivity private static final String TAG = "VpnDisconnected"; - private IConnectivityManager mService; + private ConnectivityManager mService; private int mUserId; private String mVpnPackage; @@ -53,10 +50,9 @@ public class AlwaysOnDisconnectedDialog extends AlertActivity 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 @@ public class AlwaysOnDisconnectedDialog extends AlertActivity } } - 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 e66f2cc17a7f..aab01d03b96d 100644 --- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java +++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java @@ -18,15 +18,12 @@ package com.android.vpndialogs; 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 @@ public class ConfirmDialog extends AlertActivity 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 @@ public class ConfirmDialog extends AlertActivity 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 @@ public class ConfirmDialog extends AlertActivity 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 @@ public class ConfirmDialog extends AlertActivity 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 @@ public class ConfirmDialog extends AlertActivity @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 01dca7e30e64..1fc74f704f62 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 @@ public class ManageDialog extends AlertActivity implements private VpnConfig mConfig; - private IConnectivityManager mService; + private VpnManager mVm; private TextView mDuration; private TextView mDataTransmitted; @@ -55,11 +53,9 @@ public class ManageDialog extends AlertActivity implements 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 @@ public class ManageDialog extends AlertActivity implements } 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/backup/OWNERS b/services/backup/OWNERS index 3c5268c5a2a9..ba2a63abb62d 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 4bebe399b8bc..b4038bff07f1 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -95,10 +95,9 @@ java_library_static { libs: [ "services.net", "android.hardware.light-V2.0-java", - "android.hardware.power-java", + "android.hardware.power-V1-java", "android.hardware.power-V1.0-java", - "android.hardware.vibrator-java", - "android.net.ipsec.ike.stubs.module_lib", + "android.hardware.vibrator-V1-java", "app-compat-annotations", "framework-tethering.stubs.module_lib", "service-permission.stubs.system_server", @@ -120,7 +119,7 @@ java_library_static { "android.hardware.health-V1.0-java", "android.hardware.health-V2.0-java", "android.hardware.health-V2.1-java", - "android.hardware.light-java", + "android.hardware.light-V1-java", "android.hardware.tv.cec-V1.0-java", "android.hardware.weaver-V1.0-java", "android.hardware.biometrics.face-V1.0-java", @@ -128,11 +127,11 @@ java_library_static { "android.hardware.oemlock-V1.0-java", "android.hardware.configstore-V1.0-java", "android.hardware.contexthub-V1.0-java", - "android.hardware.rebootescrow-java", + "android.hardware.rebootescrow-V1-java", "android.hardware.soundtrigger-V2.3-java", "android.hidl.manager-V1.2-java", "capture_state_listener-aidl-java", - "dnsresolver_aidl_interface-java", + "dnsresolver_aidl_interface-V7-java", "icu4j_calendar_astronomer", "netd-client", "overlayable_policy_aidl-java", diff --git a/services/core/java/android/content/pm/OWNERS b/services/core/java/android/content/pm/OWNERS new file mode 100644 index 000000000000..5eed0b509688 --- /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 000000000000..d0a2daf0905c --- /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 c091dfa384ca..277152d82c34 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -45,6 +45,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; 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_VALIDATED; @@ -120,6 +121,7 @@ import android.net.NetworkState; 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; @@ -130,12 +132,14 @@ import android.net.RouteInfo; import android.net.RouteInfoParcel; import android.net.SocketKeepalive; import android.net.TetheringManager; +import android.net.TransportInfo; import android.net.UidRange; import android.net.UidRangeParcel; import android.net.UnderlyingNetworkInfo; import android.net.Uri; import android.net.VpnManager; import android.net.VpnService; +import android.net.VpnTransportInfo; import android.net.metrics.INetdEventListener; import android.net.metrics.IpConnectivityLog; import android.net.metrics.NetworkEvent; @@ -222,6 +226,7 @@ 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; @@ -280,15 +285,18 @@ public class ConnectivityService extends IConnectivityManager.Stub // 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 @@ -990,6 +998,15 @@ public class ConnectivityService extends IConnectivityManager.Stub } /** + * 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( @@ -1021,11 +1038,14 @@ public class ConnectivityService extends IConnectivityManager.Stub 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(), + null /* attributionTag */); + mNetworkRequests.put(defaultInternetRequest, mDefaultRequest); + mDefaultNetworkRequests.add(mDefaultRequest); + mNetworkRequestInfoLogs.log("REGISTER " + mDefaultRequest); mDefaultMobileDataRequest = createDefaultInternetRequestForTransport( NetworkCapabilities.TRANSPORT_CELLULAR, NetworkRequest.Type.BACKGROUND_REQUEST); @@ -1051,6 +1071,8 @@ public class ConnectivityService extends IConnectivityManager.Stub 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"); @@ -1215,11 +1237,20 @@ public class ConnectivityService extends IConnectivityManager.Stub 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) { final NetworkCapabilities netCap = new NetworkCapabilities(); netCap.addCapability(NET_CAPABILITY_INTERNET); + netCap.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); netCap.removeCapability(NET_CAPABILITY_NOT_VPN); netCap.setSingleUid(uid); return netCap; @@ -1229,6 +1260,7 @@ public class ConnectivityService extends IConnectivityManager.Stub int transportType, NetworkRequest.Type type) { final NetworkCapabilities netCap = new NetworkCapabilities(); netCap.addCapability(NET_CAPABILITY_INTERNET); + netCap.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); netCap.setRequestorUidAndPackageName(Process.myUid(), mContext.getPackageName()); if (transportType > -1) { netCap.addTransportType(transportType); @@ -1282,7 +1314,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (enable) { handleRegisterNetworkRequest(new NetworkRequestInfo( - null, networkRequest, new Binder())); + null, networkRequest, new Binder(), null /* attributionTag */)); } else { handleReleaseNetworkRequest(networkRequest, Process.SYSTEM_UID, /* callOnUnavailable */ false); @@ -1364,7 +1396,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } private NetworkState getUnfilteredActiveNetworkState(int uid) { - NetworkAgentInfo nai = getDefaultNetwork(); + NetworkAgentInfo nai = getDefaultNetworkForUid(uid); final Network[] networks = getVpnUnderlyingNetworks(uid); if (networks != null) { @@ -1497,7 +1529,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - NetworkAgentInfo nai = getDefaultNetwork(); + NetworkAgentInfo nai = getDefaultNetworkForUid(uid); if (nai == null || isNetworkWithCapabilitiesBlocked(nai.networkCapabilities, uid, ignoreBlocked)) { return null; @@ -1617,7 +1649,7 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser( - int userId, String callingPackageName) { + int userId, String callingPackageName, @Nullable String callingAttributionTag) { // The basic principle is: if an app's traffic could possibly go over a // network, without the app doing anything multinetwork-specific, // (hence, by "default"), then include that network's capabilities in @@ -1636,25 +1668,34 @@ public class ConnectivityService extends IConnectivityManager.Stub 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, + callingAttributionTag)); + } } // 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( - nc, mDeps.getCallingUid(), callingPackageName)); + nc, mDeps.getCallingUid(), callingPackageName, + callingAttributionTag)); } } } @@ -1672,9 +1713,7 @@ public class ConnectivityService extends IConnectivityManager.Stub /** * 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 */ @@ -1731,12 +1770,13 @@ public class ConnectivityService extends IConnectivityManager.Stub } @Override - public NetworkCapabilities getNetworkCapabilities(Network network, String callingPackageName) { + public NetworkCapabilities getNetworkCapabilities(Network network, String callingPackageName, + @Nullable String callingAttributionTag) { mAppOpsManager.checkPackage(mDeps.getCallingUid(), callingPackageName); enforceAccessPermission(); return createWithLocationInfoSanitizedIfNecessaryWhenParceled( getNetworkCapabilitiesInternal(network), - mDeps.getCallingUid(), callingPackageName); + mDeps.getCallingUid(), callingPackageName, callingAttributionTag); } @VisibleForTesting @@ -1755,11 +1795,12 @@ public class ConnectivityService extends IConnectivityManager.Stub return newNc; } - private boolean hasLocationPermission(int callerUid, @NonNull String callerPkgName) { + private boolean hasLocationPermission(int callerUid, @NonNull String callerPkgName, + @Nullable String callingAttributionTag) { final long token = Binder.clearCallingIdentity(); try { return mLocationPermissionChecker.checkLocationPermission( - callerPkgName, null /* featureId */, callerUid, null /* message */); + callerPkgName, callingAttributionTag, callerUid, null /* message */); } finally { Binder.restoreCallingIdentity(token); } @@ -1768,7 +1809,8 @@ public class ConnectivityService extends IConnectivityManager.Stub @VisibleForTesting @Nullable NetworkCapabilities createWithLocationInfoSanitizedIfNecessaryWhenParceled( - @Nullable NetworkCapabilities nc, int callerUid, @NonNull String callerPkgName) { + @Nullable NetworkCapabilities nc, int callerUid, @NonNull String callerPkgName, + @Nullable String callingAttributionTag) { if (nc == null) { return null; } @@ -1777,7 +1819,8 @@ public class ConnectivityService extends IConnectivityManager.Stub // Avoid doing location permission check if the transport info has no location sensitive // data. if (nc.getTransportInfo() != null && nc.getTransportInfo().hasLocationSensitiveFields()) { - hasLocationPermission = hasLocationPermission(callerUid, callerPkgName); + hasLocationPermission = + hasLocationPermission(callerUid, callerPkgName, callingAttributionTag); newNc = new NetworkCapabilities(nc, hasLocationPermission); } else { newNc = new NetworkCapabilities(nc, false /* parcelLocationSensitiveFields */); @@ -1794,7 +1837,8 @@ public class ConnectivityService extends IConnectivityManager.Stub } if (hasLocationPermission == null) { // Location permission not checked yet, check now for masking owner UID. - hasLocationPermission = hasLocationPermission(callerUid, callerPkgName); + hasLocationPermission = + hasLocationPermission(callerUid, callerPkgName, callingAttributionTag); } // Reset owner uid if the app has no location permission. if (!hasLocationPermission) { @@ -1852,7 +1896,8 @@ public class ConnectivityService extends IConnectivityManager.Stub final ArrayList<NetworkState> result = new ArrayList<>(); for (Network network : getAllNetworks()) { final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network); - if (nai != null) { + // TODO: Consider include SUSPENDED networks. + if (nai != null && nai.networkInfo.isConnected()) { // TODO (b/73321673) : NetworkState contains a copy of the // NetworkCapabilities, which may contain UIDs of apps to which the // network applies. Should the UIDs be cleared so as not to leak or @@ -2005,7 +2050,7 @@ public class ConnectivityService extends IConnectivityManager.Stub 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"); @@ -2023,7 +2068,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // 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); } } @@ -2117,8 +2162,8 @@ public class ConnectivityService extends IConnectivityManager.Stub 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); } /** @@ -2701,9 +2746,9 @@ public class ConnectivityService extends IConnectivityManager.Stub 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(); } @@ -3298,27 +3343,27 @@ public class ConnectivityService extends IConnectivityManager.Stub } /** - * 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; } @@ -3332,7 +3377,6 @@ public class ConnectivityService extends IConnectivityManager.Stub 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"); @@ -3434,25 +3478,33 @@ public class ConnectivityService extends IConnectivityManager.Stub 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.getSatisfier(); if (currentNetwork != null && currentNetwork.network.getNetId() == nai.network.getNetId()) { + // uid rules for this network will be removed in destroyNativeNetwork(nai). nri.setSatisfier(null, null); - sendUpdatedScoreToFactories(request, 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); @@ -3461,10 +3513,9 @@ public class ConnectivityService extends IConnectivityManager.Stub // (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); } @@ -3549,18 +3600,15 @@ public class ConnectivityService extends IConnectivityManager.Stub } } rematchAllNetworksAndRequests(); - // If an active request exists, return as its score has already been sent if needed. - if (null != nri.getActiveRequest()) { + // 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()) { - continue; - } - sendUpdatedScoreToFactories(req, null); + if (req.isRequest()) sendUpdatedScoreToFactories(req, null); } } @@ -3600,7 +3648,7 @@ public class ConnectivityService extends IConnectivityManager.Stub 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()) { @@ -3693,7 +3741,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (mNetworkRequests.get(nri.mRequests.get(0)) == null) { return; } - if (nri.getSatisfier() != null) { + if (nri.isBeingSatisfied()) { return; } if (VDBG || (DBG && nri.mRequests.get(0).isRequest())) { @@ -3737,7 +3785,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mNetworkRequestInfoLogs.log("RELEASE " + nri); if (null != nri.getActiveRequest()) { - if (nri.getActiveRequest().isRequest()) { + if (!nri.getActiveRequest().isListen()) { removeSatisfiedNetworkRequestFromNetwork(nri); } else { nri.setSatisfier(null, null); @@ -3792,7 +3840,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // 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)) { + if (updateInactivityState(nai, now)) { notifyNetworkLosing(nai, now); } if (unneeded(nai, UnneededFor.TEARDOWN)) { @@ -4253,7 +4301,7 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public NetworkRequest getDefaultRequest() { - return mDefaultRequest; + return mDefaultRequest.mRequests.get(0); } private class InternalHandler extends Handler { @@ -4831,7 +4879,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } synchronized (mVpns) { throwIfLockdownEnabled(); - mVpns.get(user).startLegacyVpn(profile, mKeyStore, egress); + mVpns.get(user).startLegacyVpn(profile, mKeyStore, null /* underlying */, egress); } } @@ -4884,7 +4932,8 @@ public class ConnectivityService extends IConnectivityManager.Stub // 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 }; } @@ -4935,8 +4984,10 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - 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 }; @@ -4949,7 +5000,8 @@ public class ConnectivityService extends IConnectivityManager.Stub // 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); } @@ -5407,27 +5459,21 @@ public class ConnectivityService extends IConnectivityManager.Stub 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)); @@ -5438,38 +5484,19 @@ public class ConnectivityService extends IConnectivityManager.Stub } 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(); } } } @@ -5493,6 +5520,8 @@ public class ConnectivityService extends IConnectivityManager.Stub */ @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; // mSatisfier and mActiveRequest rely on one another therefore set them together. @@ -5503,9 +5532,8 @@ public class ConnectivityService extends IConnectivityManager.Stub mActiveRequest = activeRequest; } - // 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. + // 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 private NetworkAgentInfo mSatisfier; NetworkAgentInfo getSatisfier() { @@ -5527,8 +5555,23 @@ public class ConnectivityService extends IConnectivityManager.Stub final int mPid; final int mUid; final Messenger messenger; + @Nullable + final String mCallingAttributionTag; + + /** + * 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) { + NetworkRequestInfo(NetworkRequest r, PendingIntent pi, + @Nullable String callingAttributionTag) { mRequests = initializeRequests(r); ensureAllNetworkRequestsHaveType(mRequests); mPendingIntent = pi; @@ -5537,9 +5580,11 @@ public class ConnectivityService extends IConnectivityManager.Stub mPid = getCallingPid(); mUid = mDeps.getCallingUid(); mNetworkRequestCounter.incrementCountOrThrow(mUid); + mCallingAttributionTag = callingAttributionTag; } - NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder) { + NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder, + @Nullable String callingAttributionTag) { super(); messenger = m; mRequests = initializeRequests(r); @@ -5549,6 +5594,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mUid = mDeps.getCallingUid(); mPendingIntent = null; mNetworkRequestCounter.incrementCountOrThrow(mUid); + mCallingAttributionTag = callingAttributionTag; try { mBinder.linkToDeath(this, 0); @@ -5558,7 +5604,14 @@ public class ConnectivityService extends IConnectivityManager.Stub } NetworkRequestInfo(NetworkRequest r) { - this(r, null); + this(r, null /* pi */, null /* callingAttributionTag */); + } + + // 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() { @@ -5586,7 +5639,9 @@ public class ConnectivityService extends IConnectivityManager.Stub @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); } } @@ -5694,6 +5749,7 @@ public class ConnectivityService extends IConnectivityManager.Stub throw new SecurityException("Insufficient permissions to specify legacy type"); } } + final NetworkCapabilities defaultNc = mDefaultRequest.mRequests.get(0).networkCapabilities; final int callingUid = mDeps.getCallingUid(); final NetworkRequest.Type reqType; try { @@ -5704,11 +5760,15 @@ public class ConnectivityService extends IConnectivityManager.Stub switch (reqType) { case TRACK_DEFAULT: // If the request type is TRACK_DEFAULT, the passed {@code networkCapabilities} - // is unused and will be replaced by the one from the default network request. - // This allows callers to keep track of the system default network. + // is unused and will be replaced by ones appropriate for the caller. + // This allows callers to keep track of the default network for their app. networkCapabilities = createDefaultNetworkCapabilitiesForUid(callingUid); enforceAccessPermission(); break; + case TRACK_SYSTEM_DEFAULT: + enforceSettingsPermission(); + networkCapabilities = new NetworkCapabilities(defaultNc); + break; case BACKGROUND_REQUEST: enforceNetworkStackOrSettingsPermission(); // Fall-through since other checks are the same with normal requests. @@ -5727,6 +5787,7 @@ public class ConnectivityService extends IConnectivityManager.Stub ensureRequestableCapabilities(networkCapabilities); ensureSufficientPermissionsForRequest(networkCapabilities, Binder.getCallingPid(), callingUid, callingPackageName); + // Set the UID range for this request to the single UID of the requester, or to an empty // set of UIDs if the caller has the appropriate permission and UIDs have not been set. // This will overwrite any allowed UIDs in the requested capabilities. Though there @@ -5742,9 +5803,20 @@ public class ConnectivityService extends IConnectivityManager.Stub NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType, nextNetworkRequestId(), reqType); - NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder); + NetworkRequestInfo nri = + new NetworkRequestInfo(messenger, networkRequest, binder, callingAttributionTag); if (DBG) log("requestNetwork for " + nri); + // For TRACK_SYSTEM_DEFAULT callbacks, the capabilities have been modified since they were + // copied from the default request above. (This is necessary to ensure, for example, that + // the callback does not leak sensitive information to unprivileged apps.) Check that the + // changes don't alter request matching. + if (reqType == NetworkRequest.Type.TRACK_SYSTEM_DEFAULT && + (!networkCapabilities.equalRequestableCapabilities(defaultNc))) { + Log.wtf(TAG, "TRACK_SYSTEM_DEFAULT capabilities don't match default request: " + + networkCapabilities + " vs. " + defaultNc); + } + mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST, nri)); if (timeoutMs > 0) { mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_TIMEOUT_NETWORK_REQUEST, @@ -5831,7 +5903,8 @@ public class ConnectivityService extends IConnectivityManager.Stub NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE, nextNetworkRequestId(), NetworkRequest.Type.REQUEST); - NetworkRequestInfo nri = new NetworkRequestInfo(networkRequest, operation); + NetworkRequestInfo nri = + new NetworkRequestInfo(networkRequest, operation, callingAttributionTag); if (DBG) log("pendingRequest for " + nri); mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST_WITH_INTENT, nri)); @@ -5875,7 +5948,8 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public NetworkRequest listenForNetwork(NetworkCapabilities networkCapabilities, - Messenger messenger, IBinder binder, @NonNull String callingPackageName) { + Messenger messenger, IBinder binder, @NonNull String callingPackageName, + @Nullable String callingAttributionTag) { final int callingUid = mDeps.getCallingUid(); if (!hasWifiNetworkListenPermission(networkCapabilities)) { enforceAccessPermission(); @@ -5895,7 +5969,8 @@ public class ConnectivityService extends IConnectivityManager.Stub NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(), NetworkRequest.Type.LISTEN); - NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder); + NetworkRequestInfo nri = + new NetworkRequestInfo(messenger, networkRequest, binder, callingAttributionTag); if (VDBG) log("listenForNetwork for " + nri); mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri)); @@ -5904,7 +5979,8 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public void pendingListenForNetwork(NetworkCapabilities networkCapabilities, - PendingIntent operation, @NonNull String callingPackageName) { + PendingIntent operation, @NonNull String callingPackageName, + @Nullable String callingAttributionTag) { Objects.requireNonNull(operation, "PendingIntent cannot be null."); final int callingUid = mDeps.getCallingUid(); if (!hasWifiNetworkListenPermission(networkCapabilities)) { @@ -5918,7 +5994,8 @@ public class ConnectivityService extends IConnectivityManager.Stub NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(), NetworkRequest.Type.LISTEN); - NetworkRequestInfo nri = new NetworkRequestInfo(networkRequest, operation); + NetworkRequestInfo nri = + new NetworkRequestInfo(networkRequest, operation, callingAttributionTag); if (VDBG) log("pendingListenForNetwork for " + nri); mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri)); @@ -5942,15 +6019,6 @@ public class ConnectivityService extends IConnectivityManager.Stub 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. @@ -5964,10 +6032,7 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ -5986,11 +6051,6 @@ public class ConnectivityService extends IConnectivityManager.Stub 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) { @@ -6038,11 +6098,33 @@ public class ConnectivityService extends IConnectivityManager.Stub @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. @@ -6055,8 +6137,37 @@ public class ConnectivityService extends IConnectivityManager.Stub // 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 @@ -6143,8 +6254,6 @@ public class ConnectivityService extends IConnectivityManager.Stub 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, @@ -6298,20 +6407,18 @@ public class ConnectivityService extends IConnectivityManager.Stub Math.max(naData.getRefreshTimeMillis(), apiData.getRefreshTimeMillis())); } - // Prioritize the user portal URL from the network agent. - if (apiData.getUserPortalUrl() != null && (naData.getUserPortalUrl() == null - || TextUtils.isEmpty(naData.getUserPortalUrl().toSafeString()))) { - captivePortalBuilder.setUserPortalUrl(apiData.getUserPortalUrl()); + // Prioritize the user portal URL from the network agent if the source is authenticated. + if (apiData.getUserPortalUrl() != null && naData.getUserPortalUrlSource() + != CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) { + captivePortalBuilder.setUserPortalUrl(apiData.getUserPortalUrl(), + apiData.getUserPortalUrlSource()); } - // Prioritize the venue information URL from the network agent. - if (apiData.getVenueInfoUrl() != null && (naData.getVenueInfoUrl() == null - || TextUtils.isEmpty(naData.getVenueInfoUrl().toSafeString()))) { - captivePortalBuilder.setVenueInfoUrl(apiData.getVenueInfoUrl()); - - // Note that venue friendly name can only come from the network agent because it is not - // in use in RFC8908. However, if using the Capport venue URL, make sure that the - // friendly name is not set from the network agent. - captivePortalBuilder.setVenueFriendlyName(null); + // Prioritize the venue information URL from the network agent if the source is + // authenticated. + if (apiData.getVenueInfoUrl() != null && naData.getVenueInfoUrlSource() + != CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) { + captivePortalBuilder.setVenueInfoUrl(apiData.getVenueInfoUrl(), + apiData.getVenueInfoUrlSource()); } return captivePortalBuilder.build(); } @@ -6567,7 +6674,8 @@ public class ConnectivityService extends IConnectivityManager.Stub @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; @@ -6939,8 +7047,8 @@ public class ConnectivityService extends IConnectivityManager.Stub 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); } } @@ -7002,10 +7110,10 @@ public class ConnectivityService extends IConnectivityManager.Stub ensureRunningOnConnectivityServiceThread(); for (final NetworkRequestInfo nri : getNrisFromGlobalRequests()) { for (final NetworkRequest req : nri.mRequests) { - if (req.isListen() && nri.getActiveRequest() == req) { + if (!req.isRequest() && nri.getActiveRequest() == req) { break; } - if (req.isListen()) { + if (!req.isRequest()) { continue; } // Only set the nai for the request it is satisfying. @@ -7100,7 +7208,8 @@ public class ConnectivityService extends IConnectivityManager.Stub putParcelable( bundle, createWithLocationInfoSanitizedIfNecessaryWhenParceled( - nc, nri.mUid, nrForCallback.getRequestorPackageName())); + nc, nri.mUid, nrForCallback.getRequestorPackageName(), + nri.mCallingAttributionTag)); putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions( networkAgent.linkProperties, nri.mPid, nri.mUid)); // For this notification, arg1 contains the blocked status. @@ -7119,7 +7228,8 @@ public class ConnectivityService extends IConnectivityManager.Stub putParcelable( bundle, createWithLocationInfoSanitizedIfNecessaryWhenParceled( - netCap, nri.mUid, nrForCallback.getRequestorPackageName())); + netCap, nri.mUid, nrForCallback.getRequestorPackageName(), + nri.mCallingAttributionTag)); break; } case ConnectivityManager.CALLBACK_IP_CHANGED: { @@ -7155,8 +7265,8 @@ public class ConnectivityService extends IConnectivityManager.Stub 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; } @@ -7173,42 +7283,132 @@ public class ConnectivityService extends IConnectivityManager.Stub // 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); + } + + 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); + } - mDefaultNetworkNai = newNetwork; + // 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) { @@ -7332,9 +7532,9 @@ public class ConnectivityService extends IConnectivityManager.Stub @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()); } @@ -7343,12 +7543,20 @@ public class ConnectivityService extends IConnectivityManager.Stub } else { if (VDBG || DDBG) log(" accepting network in place of null"); } + + // 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); + } + newSatisfier.unlingerRequest(newRequest.requestId); if (!newSatisfier.addRequest(newRequest)) { Log.wtf(TAG, "BUG: " + newSatisfier.toShortString() + " already has " + newRequest); } - } else { + } else if (null != previousSatisfier) { if (DBG) { log("Network " + previousSatisfier.toShortString() + " stopped satisfying" + " request " + previousRequest.requestId); @@ -7399,7 +7607,11 @@ public class ConnectivityService extends IConnectivityManager.Stub break; } } - if (bestNetwork != nri.mSatisfier) { + 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.mActiveRequest, bestRequest, nri.getSatisfier(), bestNetwork)); @@ -7460,46 +7672,8 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ -7519,19 +7693,19 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - // 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); } } @@ -7548,16 +7722,20 @@ public class ConnectivityService extends IConnectivityManager.Stub 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. // @@ -7565,7 +7743,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // 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 { @@ -7595,9 +7773,15 @@ public class ConnectivityService extends IConnectivityManager.Stub } 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 @@ -7611,7 +7795,8 @@ public class ConnectivityService extends IConnectivityManager.Stub // 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 @@ -7663,7 +7848,7 @@ public class ConnectivityService extends IConnectivityManager.Stub 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; @@ -7782,6 +7967,15 @@ public class ConnectivityService extends IConnectivityManager.Stub // 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(); @@ -7835,7 +8029,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // 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); } @@ -7933,8 +8127,8 @@ public class ConnectivityService extends IConnectivityManager.Stub 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); @@ -7981,10 +8175,15 @@ public class ConnectivityService extends IConnectivityManager.Stub */ 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); } } @@ -8034,7 +8233,6 @@ public class ConnectivityService extends IConnectivityManager.Stub int user = UserHandle.getUserId(mDeps.getCallingUid()); final boolean success; synchronized (mVpns) { - throwIfLockdownEnabled(); success = mVpns.get(user).setUnderlyingNetworks(networks); } return success; @@ -8280,22 +8478,11 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - /** - * Caller either needs to be an active VPN, or hold the NETWORK_STACK permission - * for testing. - */ - private Vpn enforceActiveVpnOrNetworkStackPermission() { - if (checkNetworkStackPermission()) { - return null; - } - synchronized (mVpns) { - Vpn vpn = getVpnIfOwner(); - if (vpn != null) { - return vpn; - } - } - throw new SecurityException("App must either be an active VPN or have the NETWORK_STACK " - + "permission"); + private @VpnManager.VpnType int getVpnType(@Nullable NetworkAgentInfo vpn) { + if (vpn == null) return VpnManager.TYPE_VPN_NONE; + final TransportInfo ti = vpn.networkCapabilities.getTransportInfo(); + if (!(ti instanceof VpnTransportInfo)) return VpnManager.TYPE_VPN_NONE; + return ((VpnTransportInfo) ti).type; } /** @@ -8305,23 +8492,22 @@ public class ConnectivityService extends IConnectivityManager.Stub * connection is not found. */ public int getConnectionOwnerUid(ConnectionInfo connectionInfo) { - final Vpn vpn = enforceActiveVpnOrNetworkStackPermission(); - - // Only VpnService based VPNs should be able to get this information. - if (vpn != null && vpn.getActiveAppVpnType() != VpnManager.TYPE_VPN_SERVICE) { - throw new SecurityException( - "getConnectionOwnerUid() not allowed for non-VpnService VPNs"); - } - if (connectionInfo.protocol != IPPROTO_TCP && connectionInfo.protocol != IPPROTO_UDP) { 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. */ - if (vpn != null && !vpn.appliesToUid(uid)) { + if (uid == INVALID_UID) return uid; // Not found. + + // Connection owner UIDs are visible only to the network stack and to the VpnService-based + // VPN, if any, that applies to the UID that owns the connection. + if (checkNetworkStackPermission()) return uid; + + final NetworkAgentInfo vpn = getVpnForUid(uid); + if (vpn == null || getVpnType(vpn) != VpnManager.TYPE_VPN_SERVICE + || vpn.networkCapabilities.getOwnerUid() != Binder.getCallingUid()) { return INVALID_UID; } @@ -8968,6 +9154,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } } } + /** * Registers {@link QosSocketFilter} with {@link IQosCallback}. * @@ -9017,4 +9204,10 @@ public class ConnectivityService extends IConnectivityManager.Stub 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/EntropyMixer.java b/services/core/java/com/android/server/EntropyMixer.java index c56cef2d58dc..a83c981235df 100644 --- a/services/core/java/com/android/server/EntropyMixer.java +++ b/services/core/java/com/android/server/EntropyMixer.java @@ -16,12 +16,6 @@ package com.android.server; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintWriter; - import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -33,10 +27,15 @@ import android.os.Message; import android.os.SystemProperties; import android.util.Slog; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; + /** * A service designed to load and periodically save "randomness" - * for the Linux kernel RNG and to mix in data from Hardware RNG (if present) - * into the Linux RNG. + * for the Linux kernel RNG. * * <p>When a Linux system starts up, the entropy pool associated with * {@code /dev/random} may be in a fairly predictable state. Applications which @@ -45,15 +44,8 @@ import android.util.Slog; * this effect, it's helpful to carry the entropy pool information across * shutdowns and startups. * - * <p>On systems with Hardware RNG (/dev/hw_random), a block of output from HW - * RNG is mixed into the Linux RNG on EntropyMixer's startup and whenever - * EntropyMixer periodically runs to save a block of output from Linux RNG on - * disk. This mixing is done in a way that does not increase the Linux RNG's - * entropy estimate is not increased. This is to avoid having to trust/verify - * the quality and authenticity of the "randomness" of the HW RNG. - * * <p>This class was modeled after the script in the - * <a href="http://www.kernel.org/doc/man-pages/online/pages/man4/random.4.html"> + * <a href="https://man7.org/linux/man-pages/man4/random.4.html"> * random(4) manual page</a>. */ public class EntropyMixer extends Binder { @@ -64,7 +56,6 @@ public class EntropyMixer extends Binder { private static final long START_NANOTIME = System.nanoTime(); private final String randomDevice; - private final String hwRandomDevice; private final String entropyFile; /** @@ -80,7 +71,6 @@ public class EntropyMixer extends Binder { Slog.e(TAG, "Will not process invalid message"); return; } - addHwRandomEntropy(); writeEntropy(); scheduleEntropyWriter(); } @@ -94,25 +84,21 @@ public class EntropyMixer extends Binder { }; public EntropyMixer(Context context) { - this(context, getSystemDir() + "/entropy.dat", "/dev/urandom", "/dev/hw_random"); + this(context, getSystemDir() + "/entropy.dat", "/dev/urandom"); } /** Test only interface, not for public use */ public EntropyMixer( Context context, String entropyFile, - String randomDevice, - String hwRandomDevice) { + String randomDevice) { if (randomDevice == null) { throw new NullPointerException("randomDevice"); } - if (hwRandomDevice == null) { throw new NullPointerException("hwRandomDevice"); } if (entropyFile == null) { throw new NullPointerException("entropyFile"); } this.randomDevice = randomDevice; - this.hwRandomDevice = hwRandomDevice; this.entropyFile = entropyFile; loadInitialEntropy(); addDeviceSpecificEntropy(); - addHwRandomEntropy(); writeEntropy(); scheduleEntropyWriter(); IntentFilter broadcastFilter = new IntentFilter(Intent.ACTION_SHUTDOWN); @@ -192,23 +178,6 @@ public class EntropyMixer extends Binder { } } - /** - * Mixes in the output from HW RNG (if present) into the Linux RNG. - */ - private void addHwRandomEntropy() { - if (!new File(hwRandomDevice).exists()) { - // HW RNG not present/exposed -- ignore - return; - } - - try { - RandomBlock.fromFile(hwRandomDevice).toFile(randomDevice, false); - Slog.i(TAG, "Added HW RNG output to entropy pool"); - } catch (IOException e) { - Slog.w(TAG, "Failed to add HW RNG output to entropy pool", e); - } - } - private static String getSystemDir() { File dataDir = Environment.getDataDirectory(); File systemDir = new File(dataDir, "system"); diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java index e96fd390f15a..96f832d26816 100644 --- a/services/core/java/com/android/server/TestNetworkService.java +++ b/services/core/java/com/android/server/TestNetworkService.java @@ -50,6 +50,7 @@ import android.util.SparseArray; 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; @@ -280,10 +281,12 @@ class TestNetworkService extends ITestNetworkManager.Stub { // 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, diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index 6a72010738db..27210daac241 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -66,6 +66,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; import java.util.concurrent.TimeUnit; /** @@ -290,8 +291,10 @@ public class VcnManagementService extends IVcnManagementService.Stub { 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}. */ @@ -382,6 +385,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { // delay) for (Entry<ParcelUuid, Vcn> entry : mVcns.entrySet()) { final VcnConfig config = mConfigs.get(entry.getKey()); + if (config == null || !snapshot.packageHasPermissionsForSubscriptionGroup( entry.getKey(), config.getProvisioningPackageName())) { @@ -395,10 +399,13 @@ public class VcnManagementService extends IVcnManagementService.Stub { // 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); } } } @@ -406,14 +413,44 @@ public class VcnManagementService extends IVcnManagementService.Stub { } @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") @@ -476,9 +513,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { synchronized (mLock) { mConfigs.remove(subscriptionGroup); - if (mVcns.containsKey(subscriptionGroup)) { - mVcns.remove(subscriptionGroup).teardownAsynchronously(); - } + stopVcnLocked(subscriptionGroup); writeConfigsToDiskLocked(); } @@ -508,7 +543,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { } } - /** Get current configuration list for testing purposes */ + /** Get current VCNs for testing purposes */ @VisibleForTesting(visibility = Visibility.PRIVATE) public Map<ParcelUuid, Vcn> getAllVcns() { synchronized (mLock) { @@ -606,21 +641,61 @@ public class VcnManagementService extends IVcnManagementService.Stub { } boolean isVcnManagedNetwork = false; + boolean isRestrictedCarrierWifi = false; if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { synchronized (mLock) { ParcelUuid subGroup = mLastSnapshot.getGroupForSubId(subId); - // TODO(b/178140910): only mark the Network as VCN-managed if not in safe mode - if (mVcns.containsKey(subGroup)) { - isVcnManagedNetwork = true; + Vcn vcn = mVcns.get(subGroup); + if (vcn != null) { + if (vcn.isActive()) { + isVcnManagedNetwork = true; + } + + if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { + // Carrier WiFi always restricted if VCN exists (even in safe mode). + isRestrictedCarrierWifi = true; + } } } } + if (isVcnManagedNetwork) { networkCapabilities.removeCapability( NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED); } + if (isRestrictedCarrierWifi) { + networkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); + } + 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/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 686adbb7b793..f5df87eefafd 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 @@ public class ActivityManagerService extends IActivityManager.Stub "getContentProviderImpl: after checkContentProviderPermission"); final long origId = Binder.clearCallingIdentity(); + try { + checkTime(startTime, "getContentProviderImpl: incProviderCountLocked"); + + // 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: 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"); - } - } - - 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; + } + 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; } - // 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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; @@ -13729,13 +13720,13 @@ public class ActivityManagerService extends IActivityManager.Stub long kernelUsed = memInfo.getKernelUsedSizeKb(); final long ionHeap = Debug.getIonHeapsSizeKb(); final long ionPool = Debug.getIonPoolsSizeKb(); + final long dmabufMapped = Debug.getDmabufMappedSizeKb(); if (ionHeap >= 0 && ionPool >= 0) { - final long ionMapped = Debug.getIonMappedSizeKb(); - final long ionUnmapped = ionHeap - ionMapped; + final long ionUnmapped = ionHeap - dmabufMapped; pw.print(" ION: "); pw.print(stringifyKBSize(ionHeap + ionPool)); pw.print(" ("); - pw.print(stringifyKBSize(ionMapped)); + pw.print(stringifyKBSize(dmabufMapped)); pw.print(" mapped + "); pw.print(stringifyKBSize(ionUnmapped)); pw.print(" unmapped + "); @@ -13744,11 +13735,34 @@ public class ActivityManagerService extends IActivityManager.Stub // Note: mapped ION memory is not accounted in PSS due to VM_PFNMAP flag being // set on ION VMAs, therefore consider the entire ION heap as used kernel memory kernelUsed += ionHeap; + } else { + final long totalExportedDmabuf = Debug.getDmabufTotalExportedKb(); + if (totalExportedDmabuf >= 0) { + final long dmabufUnmapped = totalExportedDmabuf - dmabufMapped; + pw.print("DMA-BUF: "); + pw.print(stringifyKBSize(totalExportedDmabuf)); + pw.print(" ("); + pw.print(stringifyKBSize(dmabufMapped)); + pw.print(" mapped + "); + pw.print(stringifyKBSize(dmabufUnmapped)); + pw.println(" unmapped)"); + kernelUsed += totalExportedDmabuf; + } + final long totalDmabufHeapPool = Debug.getDmabufHeapPoolsSizeKb(); + if (totalDmabufHeapPool >= 0) { + pw.print("DMA-BUF Heaps pool: "); + pw.println(stringifyKBSize(totalDmabufHeapPool)); + } } final long gpuUsage = Debug.getGpuTotalUsageKb(); if (gpuUsage >= 0) { pw.print(" GPU: "); pw.println(stringifyKBSize(gpuUsage)); } + + /* + * Note: ION/DMA-BUF heap pools are reclaimable and hence, they are included as part of + * memInfo.getCachedSizeKb(). + */ final long lostRAM = memInfo.getTotalSizeKb() - (totalPss - totalSwapPss) - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb() - kernelUsed - memInfo.getZramTotalSizeKb(); @@ -14548,15 +14562,28 @@ public class ActivityManagerService extends IActivityManager.Stub final long ionHeap = Debug.getIonHeapsSizeKb(); final long ionPool = Debug.getIonPoolsSizeKb(); if (ionHeap >= 0 && ionPool >= 0) { - final long ionMapped = Debug.getIonMappedSizeKb(); - final long ionUnmapped = ionHeap - ionMapped; memInfoBuilder.append(" ION: "); memInfoBuilder.append(stringifyKBSize(ionHeap + ionPool)); memInfoBuilder.append("\n"); // Note: mapped ION memory is not accounted in PSS due to VM_PFNMAP flag being // set on ION VMAs, therefore consider the entire ION heap as used kernel memory kernelUsed += ionHeap; + } else { + final long totalExportedDmabuf = Debug.getDmabufTotalExportedKb(); + if (totalExportedDmabuf >= 0) { + memInfoBuilder.append("DMA-BUF: "); + memInfoBuilder.append(stringifyKBSize(totalExportedDmabuf)); + memInfoBuilder.append("\n"); + kernelUsed += totalExportedDmabuf; + } + final long totalDmabufHeapPool = Debug.getDmabufHeapPoolsSizeKb(); + if (totalDmabufHeapPool >= 0) { + memInfoBuilder.append("DMA-BUF Heaps pool: "); + memInfoBuilder.append(stringifyKBSize(totalDmabufHeapPool)); + memInfoBuilder.append("\n"); + } } + final long gpuUsage = Debug.getGpuTotalUsageKb(); if (gpuUsage >= 0) { memInfoBuilder.append(" GPU: "); @@ -14567,6 +14594,11 @@ public class ActivityManagerService extends IActivityManager.Stub memInfoBuilder.append(stringifyKBSize( totalPss - cachedPss + kernelUsed)); memInfoBuilder.append("\n"); + + /* + * Note: ION/DMA-BUF heap pools are reclaimable and hence, they are included as part of + * memInfo.getCachedSizeKb(). + */ memInfoBuilder.append(" Lost RAM: "); memInfoBuilder.append(stringifyKBSize(memInfo.getTotalSizeKb() - (totalPss - totalSwapPss) - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb() @@ -16943,11 +16975,12 @@ public class ActivityManagerService extends IActivityManager.Stub || (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 +16998,7 @@ public class ActivityManagerService extends IActivityManager.Stub } 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/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index b6e632d42d8e..e2c020af1b02 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 @@ public final class ProcessList { */ @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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 28afcbbb2a86..c20a01d2e3b8 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 @@ public class SettingsToPropertiesMapper { 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 fded85cd9126..e97f0b47380a 100644 --- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java +++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java @@ -18,11 +18,9 @@ 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.ACTION_USER_ADDED; -import static android.content.Intent.ACTION_USER_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_ALL; +import static android.content.pm.PackageManager.MATCH_ANY_USER; import static android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION; import android.annotation.NonNull; @@ -36,8 +34,9 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; -import android.content.pm.UserInfo; +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; @@ -48,16 +47,21 @@ 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 @@ -66,6 +70,11 @@ import java.util.Set; */ 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 @@ -76,9 +85,13 @@ public final class AppHibernationService extends SystemService { private final IActivityManager mIActivityManager; private final UserManager mUserManager; @GuardedBy("mLock") - private final SparseArray<Map<String, UserPackageState>> mUserStates = new SparseArray<>(); + private final SparseArray<Map<String, UserLevelState>> mUserStates = new SparseArray<>(); + private final SparseArray<HibernationStateDiskStore<UserLevelState>> mUserDiskStores = + new SparseArray<>(); @GuardedBy("mLock") - private final Set<String> mGloballyHibernatedPackages = new ArraySet<>(); + private final Map<String, GlobalLevelState> mGlobalHibernationStates = new ArrayMap<>(); + private final HibernationStateDiskStore<GlobalLevelState> mGlobalLevelHibernationDiskStore; + private final Injector mInjector; /** * Initializes the system service. @@ -90,28 +103,22 @@ public final class AppHibernationService extends SystemService { * @param context The system server context. */ public AppHibernationService(@NonNull Context context) { - this(context, IPackageManager.Stub.asInterface(ServiceManager.getService("package")), - ActivityManager.getService(), - context.getSystemService(UserManager.class)); + this(new InjectorImpl(context)); } @VisibleForTesting - AppHibernationService(@NonNull Context context, IPackageManager packageManager, - IActivityManager activityManager, UserManager userManager) { - super(context); - mContext = context; - mIPackageManager = packageManager; - mIActivityManager = activityManager; - mUserManager = userManager; + 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_USER_ADDED); - intentFilter.addAction(ACTION_USER_REMOVED); - userAllContext.registerReceiver(mBroadcastReceiver, intentFilter); - - intentFilter = new IntentFilter(); intentFilter.addAction(ACTION_PACKAGE_ADDED); intentFilter.addAction(ACTION_PACKAGE_REMOVED); intentFilter.addDataScheme("package"); @@ -126,12 +133,10 @@ public final class AppHibernationService extends SystemService { @Override public void onBootPhase(int phase) { if (phase == PHASE_BOOT_COMPLETED) { + List<GlobalLevelState> states = + mGlobalLevelHibernationDiskStore.readHibernationStates(); synchronized (mLock) { - final List<UserInfo> users = mUserManager.getUsers(); - // TODO: Pull from persistent disk storage. For now, just make from scratch. - for (UserInfo user : users) { - addUserPackageStatesL(user.id); - } + initializeGlobalHibernationStates(states); } } } @@ -145,12 +150,14 @@ public final class AppHibernationService extends SystemService { */ 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, UserPackageState> packageStates = mUserStates.get(userId); - if (packageStates == null) { - throw new IllegalArgumentException("No user associated with user id " + userId); - } - final UserPackageState pkgState = packageStates.get(packageName); + 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", @@ -168,7 +175,12 @@ public final class AppHibernationService extends SystemService { */ boolean isHibernatingGlobally(String packageName) { synchronized (mLock) { - return mGloballyHibernatedPackages.contains(packageName); + GlobalLevelState state = mGlobalHibernationStates.get(packageName); + if (state == null) { + throw new IllegalArgumentException( + String.format("Package %s is not installed", packageName)); + } + return state.hibernated; } } @@ -181,12 +193,14 @@ public final class AppHibernationService extends SystemService { */ 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) { - if (!mUserStates.contains(userId)) { - throw new IllegalArgumentException("No user associated with user id " + userId); - } - Map<String, UserPackageState> packageStates = mUserStates.get(userId); - UserPackageState pkgState = packageStates.get(packageName); + 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", @@ -198,10 +212,12 @@ public final class AppHibernationService extends SystemService { } if (isHibernating) { - hibernatePackageForUserL(packageName, userId, pkgState); + hibernatePackageForUser(packageName, userId, pkgState); } else { - unhibernatePackageForUserL(packageName, userId, pkgState); + unhibernatePackageForUser(packageName, userId, pkgState); } + List<UserLevelState> states = new ArrayList<>(mUserStates.get(userId).values()); + mUserDiskStores.get(userId).scheduleWriteHibernationStates(states); } } @@ -213,25 +229,32 @@ public final class AppHibernationService extends SystemService { * @param isHibernating new hibernation state */ void setHibernatingGlobally(String packageName, boolean isHibernating) { - if (isHibernating != mGloballyHibernatedPackages.contains(packageName)) { - synchronized (mLock) { + 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) { - hibernatePackageGloballyL(packageName); + hibernatePackageGlobally(packageName, state); } else { - unhibernatePackageGloballyL(packageName); + 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. - * The caller should hold {@link #mLock} * * @param pkgState package hibernation state */ - private void hibernatePackageForUserL(@NonNull String packageName, int userId, - @NonNull UserPackageState pkgState) { + @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 { @@ -249,12 +272,13 @@ public final class AppHibernationService extends SystemService { } /** - * Remove a package from hibernation for a given user. The caller should hold {@link #mLock}. + * Remove a package from hibernation for a given user. * * @param pkgState package hibernation state */ - private void unhibernatePackageForUserL(@NonNull String packageName, int userId, - UserPackageState pkgState) { + @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 { @@ -271,60 +295,140 @@ public final class AppHibernationService extends SystemService { /** * Put a package into global hibernation, optimizing its storage at a package / APK level. - * The caller should hold {@link #mLock}. */ - private void hibernatePackageGloballyL(@NonNull String packageName) { + @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 - mGloballyHibernatedPackages.add(packageName); + state.hibernated = true; Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); } /** - * Unhibernate a package from global hibernation. The caller should hold {@link #mLock}. + * Unhibernate a package from global hibernation. */ - private void unhibernatePackageGloballyL(@NonNull String packageName) { + @GuardedBy("mLock") + private void unhibernatePackageGlobally(@NonNull String packageName, GlobalLevelState state) { Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "unhibernatePackageGlobally"); - mGloballyHibernatedPackages.remove(packageName); + state.hibernated = false; Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); } /** - * Populates {@link #mUserStates} with the users installed packages. The caller should hold - * {@link #mLock}. + * 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 */ - private void addUserPackageStatesL(int userId) { - Map<String, UserPackageState> packages = new ArrayMap<>(); - List<PackageInfo> packageList; + @GuardedBy("mLock") + private void initializeUserHibernationStates(int userId, + @Nullable List<UserLevelState> diskStates) { + List<PackageInfo> packages; try { - packageList = mIPackageManager.getInstalledPackages(MATCH_ALL, userId).getList(); + packages = mIPackageManager.getInstalledPackages(PACKAGE_MATCH_FLAGS, userId).getList(); } catch (RemoteException e) { - throw new IllegalStateException("Package manager not available.", e); + throw new IllegalStateException("Package manager not available", e); } - for (int i = 0, size = packageList.size(); i < size; i++) { - packages.put(packageList.get(i).packageName, new UserPackageState()); + 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); } - mUserStates.put(userId, packages); + + 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); } - private void onUserAdded(int userId) { + /** + * 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) { - addUserPackageStatesL(userId); + initializeUserHibernationStates(userId, storedStates); } } - private void onUserRemoved(int userId) { + @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) { - mUserStates.get(userId).put(packageName, new UserPackageState()); + 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); + } } } @@ -336,7 +440,7 @@ public final class AppHibernationService extends SystemService { private void onPackageRemovedForAllUsers(@NonNull String packageName) { synchronized (mLock) { - mGloballyHibernatedPackages.remove(packageName); + mGlobalHibernationStates.remove(packageName); } } @@ -395,7 +499,7 @@ public final class AppHibernationService extends SystemService { } } - // Broadcast receiver for user and package add/removal events + // Broadcast receiver for package add/removal events private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -405,12 +509,6 @@ public final class AppHibernationService extends SystemService { } final String action = intent.getAction(); - if (ACTION_USER_ADDED.equals(action)) { - onUserAdded(userId); - } - if (ACTION_USER_REMOVED.equals(action)) { - onUserRemoved(userId); - } if (ACTION_PACKAGE_ADDED.equals(action) || ACTION_PACKAGE_REMOVED.equals(action)) { final String packageName = intent.getData().getSchemeSpecificPart(); if (intent.getBooleanExtra(EXTRA_REPLACING, false)) { @@ -443,10 +541,66 @@ public final class AppHibernationService extends SystemService { } /** - * Data class that contains hibernation state info of a package for a user. + * Dependency injector for {@link #AppHibernationService)}. */ - private static final class UserPackageState { - public boolean hibernated; - // TODO: Track whether hibernation is exempted by the user + 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/GlobalLevelHibernationProto.java b/services/core/java/com/android/server/apphibernation/GlobalLevelHibernationProto.java new file mode 100644 index 000000000000..79e995b038fa --- /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/tools/hiddenapi/Android.bp b/services/core/java/com/android/server/apphibernation/GlobalLevelState.java index e0eb06cbea7f..4f756756c2ab 100644 --- a/tools/hiddenapi/Android.bp +++ 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,17 +14,12 @@ * limitations under the License. */ -python_binary_host { - name: "merge_csv", - main: "merge_csv.py", - srcs: ["merge_csv.py"], - version: { - py2: { - enabled: false, - }, - py3: { - enabled: true, - embedded_launcher: true - }, - }, +package com.android.server.apphibernation; + +/** + * Data class that contains global hibernation state for a package. + */ +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 000000000000..c83659d2ff56 --- /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 000000000000..0cbc09a7a99d --- /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 000000000000..a24c4c575975 --- /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/services/core/java/com/android/server/apphibernation/UserLevelState.java b/services/core/java/com/android/server/apphibernation/UserLevelState.java new file mode 100644 index 000000000000..c66dad87c891 --- /dev/null +++ b/services/core/java/com/android/server/apphibernation/UserLevelState.java @@ -0,0 +1,25 @@ +/* + * 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; + +/** + * Data class that contains hibernation state info of a package for a user. + */ +final class UserLevelState { + public String packageName; + public boolean hibernated; +} diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java index c70bb080b0b1..43d9ade67a11 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.ContentResolver; 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 @@ public class DnsManager { 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 952193b77681..46c49e7fc28c 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 android.util.Log; 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 @@ public class Nat464Xlat extends BaseNetworkObserver { // 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 1a4f20c7101e..c05e25367d03 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -122,6 +122,13 @@ import java.util.TreeSet; // // 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. // @@ -210,23 +217,23 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { // 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); @@ -269,30 +276,32 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { */ 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; @@ -708,8 +717,9 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { mNumBackgroundNetworkRequests += delta; break; - case TRACK_DEFAULT: case LISTEN: + case TRACK_DEFAULT: + case TRACK_SYSTEM_DEFAULT: break; case NONE: @@ -895,20 +905,25 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { /** * 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); } /** @@ -916,23 +931,25 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { * 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 @@ -940,49 +957,65 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { // 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 setInactive() { + mInactive = true; } - public void linger() { - mLingering = true; + public void unsetInactive() { + mInactive = false; } - public void unlinger() { - mLingering = 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); + } } /** @@ -1016,7 +1049,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { + "network{" + network + "} handle{" + network.getNetworkHandle() + "} ni{" + networkInfo.toShortString() + "} " + " Score{" + getCurrentScore() + "} " - + (isLingering() ? " lingering" : "") + + (isNascent() ? " nascent" : (isLingering() ? " lingering" : "")) + (everValidated ? " everValidated" : "") + (lastValidated ? " lastValidated" : "") + (partialConnectivity ? " partialConnectivity" : "") diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java index 3d71b0a269c9..6f112d73ba14 100644 --- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java +++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java @@ -161,13 +161,20 @@ public class NetworkNotificationManager { if (nai != null) { transportType = approximateTransportType(nai); final String extraInfo = nai.networkInfo.getExtraInfo(); - name = TextUtils.isEmpty(extraInfo) ? nai.networkCapabilities.getSsid() : extraInfo; + if (nai.linkProperties != null && nai.linkProperties.getCaptivePortalData() != null + && !TextUtils.isEmpty(nai.linkProperties.getCaptivePortalData() + .getVenueFriendlyName())) { + name = nai.linkProperties.getCaptivePortalData().getVenueFriendlyName(); + } else { + name = TextUtils.isEmpty(extraInfo) + ? WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid()) : extraInfo; + } // Only notify for Internet-capable networks. if (!nai.networkCapabilities.hasCapability(NET_CAPABILITY_INTERNET)) return; } else { // Legacy notifications. transportType = TRANSPORT_CELLULAR; - name = null; + name = ""; } // Clear any previous notification with lower priority, otherwise return. http://b/63676954. @@ -193,35 +200,30 @@ public class NetworkNotificationManager { final CharSequence details; int icon = getIcon(transportType); if (notifyType == NotificationType.NO_INTERNET && transportType == TRANSPORT_WIFI) { - title = r.getString(R.string.wifi_no_internet, - WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid())); + title = r.getString(R.string.wifi_no_internet, name); details = r.getString(R.string.wifi_no_internet_detailed); } else if (notifyType == NotificationType.PRIVATE_DNS_BROKEN) { if (transportType == TRANSPORT_CELLULAR) { title = r.getString(R.string.mobile_no_internet); } else if (transportType == TRANSPORT_WIFI) { - title = r.getString(R.string.wifi_no_internet, - WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid())); + title = r.getString(R.string.wifi_no_internet, name); } else { title = r.getString(R.string.other_networks_no_internet); } details = r.getString(R.string.private_dns_broken_detailed); } else if (notifyType == NotificationType.PARTIAL_CONNECTIVITY && transportType == TRANSPORT_WIFI) { - title = r.getString(R.string.network_partial_connectivity, - WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid())); + title = r.getString(R.string.network_partial_connectivity, name); details = r.getString(R.string.network_partial_connectivity_detailed); } else if (notifyType == NotificationType.LOST_INTERNET && transportType == TRANSPORT_WIFI) { - title = r.getString(R.string.wifi_no_internet, - WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid())); + title = r.getString(R.string.wifi_no_internet, name); details = r.getString(R.string.wifi_no_internet_detailed); } else if (notifyType == NotificationType.SIGN_IN) { switch (transportType) { case TRANSPORT_WIFI: title = r.getString(R.string.wifi_available_sign_in, 0); - details = r.getString(R.string.network_available_sign_in_detailed, - WifiInfo.sanitizeSsid(nai.networkCapabilities.getSsid())); + details = r.getString(R.string.network_available_sign_in_detailed, name); break; case TRANSPORT_CELLULAR: title = r.getString(R.string.network_available_sign_in, 0); diff --git a/services/core/java/com/android/server/connectivity/PacProxyInstaller.java b/services/core/java/com/android/server/connectivity/PacProxyInstaller.java index 5dc8c1a00eaf..aadaf4d9584f 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 @@ public class PacProxyInstaller { 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 @@ public class PacProxyInstaller { private volatile boolean mHasSentBroadcast; private volatile boolean mHasDownloaded; - private final Handler mConnectivityHandler; + private Handler mConnectivityHandler; private final int mProxyMessage; /** @@ -99,13 +102,6 @@ public class PacProxyInstaller { 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 class PacProxyInstaller { } } - 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 @@ public class PacProxyInstaller { * 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 @@ public class PacProxyInstaller { 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 class PacProxyInstaller { 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 @@ public class PacProxyInstaller { 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 d507b5f82bd0..8d21f6f0f59f 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 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse 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 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse 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 b618d2b99a63..d83ff837d9be 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 @@ public class ProxyTracker { 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 @@ public class ProxyTracker { } } - 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 b455a3f4169f..33c19b1ca2b2 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.DnsResolver; 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; @@ -73,6 +74,7 @@ import android.net.UidRangeParcel; import android.net.UnderlyingNetworkInfo; import android.net.VpnManager; import android.net.VpnService; +import android.net.VpnTransportInfo; import android.net.ipsec.ike.ChildSessionCallback; import android.net.ipsec.ike.ChildSessionConfiguration; import android.net.ipsec.ike.ChildSessionParams; @@ -111,6 +113,7 @@ 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.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 +206,7 @@ public class Vpn { 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 +281,10 @@ public class Vpn { 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 +335,7 @@ public class Vpn { 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 +413,7 @@ public class Vpn { mLooper = looper; mSystemServices = systemServices; mIkev2SessionCreator = ikev2SessionCreator; + mUserManager = mContext.getSystemService(UserManager.class); mPackage = VpnConfig.LEGACY_VPN; mOwnerUID = getAppUid(mPackage, mUserId); @@ -427,6 +436,7 @@ public class Vpn { mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN); mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN); mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED); + mNetworkCapabilities.setTransportInfo(new VpnTransportInfo(VpnManager.TYPE_VPN_NONE)); loadAlwaysOnPackage(keyStore); } @@ -921,6 +931,7 @@ public class Vpn { jniReset(mInterface); mInterface = null; mNetworkCapabilities.setUids(null); + mNetworkCapabilities.setTransportInfo(null); } // Revoke the connection or stop the VpnRunner. @@ -991,6 +1002,8 @@ public class Vpn { case VpnManager.TYPE_VPN_SERVICE: toChange = new String[] {AppOpsManager.OPSTR_ACTIVATE_VPN}; break; + case VpnManager.TYPE_VPN_LEGACY: + return false; default: Log.wtf(TAG, "Unrecognized VPN type while granting authorization"); return false; @@ -1021,6 +1034,8 @@ public class Vpn { return isVpnServicePreConsented(context, packageName); case VpnManager.TYPE_VPN_PLATFORM: return isVpnProfilePreConsented(context, packageName); + case VpnManager.TYPE_VPN_LEGACY: + return VpnConfig.LEGACY_VPN.equals(packageName); default: return false; } @@ -1119,7 +1134,7 @@ public class Vpn { 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; @@ -1129,10 +1144,12 @@ public class Vpn { 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. @@ -1201,6 +1218,8 @@ public class Vpn { mNetworkCapabilities.setUids(createUserAndRestrictedProfilesRanges(mUserId, mConfig.allowedApplications, mConfig.disallowedApplications)); + mNetworkCapabilities.setTransportInfo(new VpnTransportInfo(getActiveVpnType())); + // Only apps targeting Q and above can explicitly declare themselves as metered. // These VPNs are assumed metered unless they state otherwise. if (mIsPackageTargetingAtLeastQ && mConfig.isMetered) { @@ -1431,7 +1450,7 @@ public class Vpn { final long token = Binder.clearCallingIdentity(); List<UserInfo> users; try { - users = UserManager.get(mContext).getAliveUsers(); + users = mUserManager.getAliveUsers(); } finally { Binder.restoreCallingIdentity(token); } @@ -1515,7 +1534,7 @@ public class Vpn { */ 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(); @@ -1543,7 +1562,7 @@ public class Vpn { */ 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(); @@ -1726,6 +1745,7 @@ public class Vpn { private void cleanupVpnStateLocked() { mStatusIntent = null; mNetworkCapabilities.setUids(null); + mNetworkCapabilities.setTransportInfo(null); mConfig = null; mInterface = null; @@ -1768,7 +1788,7 @@ public class Vpn { private void prepareStatusIntent() { final long token = Binder.clearCallingIdentity(); try { - mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext); + mStatusIntent = mDeps.getIntentForStatusPanel(mContext); } finally { Binder.restoreCallingIdentity(token); } @@ -1836,22 +1856,18 @@ public class Vpn { } /** - * Gets the currently running App-based VPN type + * Gets the currently running VPN type * - * @return the {@link VpnManager.VpnType}. {@link VpnManager.TYPE_VPN_NONE} if not running an - * app-based VPN. While VpnService-based VPNs are always app VPNs and LegacyVpn is always + * @return the {@link VpnManager.VpnType}. {@link VpnManager.TYPE_VPN_NONE} if not running a + * VPN. While VpnService-based VPNs are always app VPNs and LegacyVpn is always * Settings-based, the Platform VPNs can be initiated by both apps and Settings. */ - public synchronized int getActiveAppVpnType() { - if (VpnConfig.LEGACY_VPN.equals(mPackage)) { - return VpnManager.TYPE_VPN_NONE; - } - - if (mVpnRunner != null && mVpnRunner instanceof IkeV2VpnRunner) { - return VpnManager.TYPE_VPN_PLATFORM; - } else { - return VpnManager.TYPE_VPN_SERVICE; - } + public synchronized int getActiveVpnType() { + if (!mNetworkInfo.isConnectedOrConnecting()) return VpnManager.TYPE_VPN_NONE; + if (mVpnRunner == null) return VpnManager.TYPE_VPN_SERVICE; + return mVpnRunner instanceof IkeV2VpnRunner + ? VpnManager.TYPE_VPN_PLATFORM + : VpnManager.TYPE_VPN_LEGACY; } private void updateAlwaysOnNotification(DetailedState networkState) { @@ -1968,8 +1984,7 @@ public class Vpn { 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"); @@ -1982,30 +1997,30 @@ public class Vpn { * 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"); } @@ -2128,6 +2143,9 @@ public class Vpn { 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 5d1c4e6715f1..16d4f94277cb 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -555,7 +555,7 @@ public class HdmiControlService extends SystemService { private void bootCompleted() { // on boot, if device is interactive, set HDMI CEC state as powered on as well if (mPowerManager.isInteractive() && isPowerStandbyOrTransient()) { - onWakeUp(); + mPowerStatus = HdmiControlManager.POWER_STATUS_ON; } } diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index c005af4e9696..2044f40eb978 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -2381,10 +2381,17 @@ public class LockSettingsService extends ILockSettings.Stub { public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver) { enforceShell(); + final int origPid = Binder.getCallingPid(); + final int origUid = Binder.getCallingUid(); + + // The original identity is an opaque integer. final long origId = Binder.clearCallingIdentity(); + Slog.e(TAG, "Caller pid " + origPid + " Caller uid " + origUid); try { - (new LockSettingsShellCommand(new LockPatternUtils(mContext))).exec( - this, in, out, err, args, callback, resultReceiver); + final LockSettingsShellCommand command = + new LockSettingsShellCommand(new LockPatternUtils(mContext), mContext, origPid, + origUid); + command.exec(this, in, out, err, args, callback, resultReceiver); } finally { Binder.restoreCallingIdentity(origId); } diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java index 7b767b86f0d4..440ab04e5698 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java @@ -23,8 +23,11 @@ import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTE import android.app.ActivityManager; import android.app.admin.PasswordMetrics; +import android.content.Context; import android.os.ShellCommand; +import android.os.SystemProperties; import android.text.TextUtils; +import android.util.Slog; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockPatternUtils.RequestThrottledException; @@ -45,15 +48,25 @@ class LockSettingsShellCommand extends ShellCommand { private static final String COMMAND_VERIFY = "verify"; private static final String COMMAND_GET_DISABLED = "get-disabled"; private static final String COMMAND_REMOVE_CACHE = "remove-cache"; + private static final String COMMAND_SET_ROR_PROVIDER_PACKAGE = + "set-resume-on-reboot-provider-package"; private static final String COMMAND_HELP = "help"; private int mCurrentUserId; private final LockPatternUtils mLockPatternUtils; + private final Context mContext; + private final int mCallingPid; + private final int mCallingUid; + private String mOld = ""; private String mNew = ""; - LockSettingsShellCommand(LockPatternUtils lockPatternUtils) { + LockSettingsShellCommand(LockPatternUtils lockPatternUtils, Context context, int callingPid, + int callingUid) { mLockPatternUtils = lockPatternUtils; + mCallingPid = callingPid; + mCallingUid = callingUid; + mContext = context; } @Override @@ -70,6 +83,7 @@ class LockSettingsShellCommand extends ShellCommand { case COMMAND_HELP: case COMMAND_GET_DISABLED: case COMMAND_SET_DISABLED: + case COMMAND_SET_ROR_PROVIDER_PACKAGE: break; default: getErrPrintWriter().println( @@ -82,6 +96,9 @@ class LockSettingsShellCommand extends ShellCommand { case COMMAND_REMOVE_CACHE: runRemoveCache(); return 0; + case COMMAND_SET_ROR_PROVIDER_PACKAGE: + runSetResumeOnRebootProviderPackage(); + return 0; case COMMAND_HELP: onHelp(); return 0; @@ -172,6 +189,9 @@ class LockSettingsShellCommand extends ShellCommand { pw.println(""); pw.println(" remove-cache [--user USER_ID]"); pw.println(" Removes cached unified challenge for the managed profile."); + pw.println(" set-resume-on-reboot-provider-package <package_name>"); + pw.println(" Sets the package name for server based resume on reboot service " + + "provider."); pw.println(""); } } @@ -258,6 +278,17 @@ class LockSettingsShellCommand extends ShellCommand { return true; } + private boolean runSetResumeOnRebootProviderPackage() { + final String packageName = mNew; + String name = ResumeOnRebootServiceProvider.PROP_ROR_PROVIDER_PACKAGE; + Slog.i(TAG, "Setting " + name + " to " + packageName); + + mContext.enforcePermission(android.Manifest.permission.BIND_RESUME_ON_REBOOT_SERVICE, + mCallingPid, mCallingUid, TAG); + SystemProperties.set(name, packageName); + return true; + } + private boolean runClear() { LockscreenCredential none = LockscreenCredential.createNone(); if (!isNewCredentialSufficient(none)) { diff --git a/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java b/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java index a1e18bd5a6bd..9c471b85eb76 100644 --- a/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java +++ b/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java @@ -31,6 +31,7 @@ import android.os.IBinder; import android.os.ParcelableException; import android.os.RemoteCallback; import android.os.RemoteException; +import android.os.SystemProperties; import android.os.UserHandle; import android.provider.DeviceConfig; import android.service.resumeonreboot.IResumeOnRebootService; @@ -55,6 +56,10 @@ public class ResumeOnRebootServiceProvider { Manifest.permission.BIND_RESUME_ON_REBOOT_SERVICE; private static final String TAG = "ResumeOnRebootServiceProvider"; + // The system property name that overrides the default service provider package name. + static final String PROP_ROR_PROVIDER_PACKAGE = + "persist.sys.resume_on_reboot_provider_package"; + private final Context mContext; private final PackageManager mPackageManager; @@ -72,12 +77,19 @@ public class ResumeOnRebootServiceProvider { private ServiceInfo resolveService() { Intent intent = new Intent(); intent.setAction(ResumeOnRebootService.SERVICE_INTERFACE); - if (PROVIDER_PACKAGE != null && !PROVIDER_PACKAGE.equals("")) { - intent.setPackage(PROVIDER_PACKAGE); + int queryFlag = PackageManager.GET_SERVICES; + String testAppName = SystemProperties.get(PROP_ROR_PROVIDER_PACKAGE, ""); + if (!testAppName.isEmpty()) { + Slog.i(TAG, "Using test app: " + testAppName); + intent.setPackage(testAppName); + } else { + queryFlag |= PackageManager.MATCH_SYSTEM_ONLY; + if (PROVIDER_PACKAGE != null && !PROVIDER_PACKAGE.equals("")) { + intent.setPackage(PROVIDER_PACKAGE); + } } - List<ResolveInfo> resolvedIntents = - mPackageManager.queryIntentServices(intent, PackageManager.MATCH_SYSTEM_ONLY); + List<ResolveInfo> resolvedIntents = mPackageManager.queryIntentServices(intent, queryFlag); for (ResolveInfo resolvedInfo : resolvedIntents) { if (resolvedInfo.serviceInfo != null && PROVIDER_REQUIRED_PERMISSION.equals(resolvedInfo.serviceInfo.permission)) { @@ -120,6 +132,7 @@ public class ResumeOnRebootServiceProvider { if (mServiceConnection != null) { mContext.unbindService(mServiceConnection); } + mBinder = null; } /** Bind to the service */ diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java index ea2788c0c3d8..6d1c68039ee5 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 @@ public class LockdownVpnTracker { 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 f92f3dcd77ef..39ed7e8b1e1a 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 class NetworkPolicyManagerInternal { 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 01d4faf5c594..b5c0f28d8ba2 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_ALL; 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.GuardedBy; 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.FastXmlSerializer; 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // 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, @@ -1959,14 +1962,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (state.network != null) { mNetIdToSubId.put(state.network.netId, parseSubId(state)); } - if (state.networkInfo != null && state.networkInfo.isConnected()) { - // Policies matched by NPMS only match by subscriber ID or by ssid. Thus subtype - // in the object created here is never used and its value doesn't matter, so use - // NETWORK_TYPE_UNKNOWN. - final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state, - true, TelephonyManager.NETWORK_TYPE_UNKNOWN /* subType */); - identified.put(state, ident); - } + + // Policies matched by NPMS only match by subscriber ID or by ssid. Thus subtype + // in the object created here is never used and its value doesn't matter, so use + // NETWORK_TYPE_UNKNOWN. + final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state, + true, TelephonyManager.NETWORK_TYPE_UNKNOWN /* subType */); + identified.put(state, ident); } final ArraySet<String> newMeteredIfaces = new ArraySet<>(); @@ -2041,8 +2043,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // One final pass to catch any metered ifaces that don't have explicitly // defined policies; typically Wi-Fi networks. for (NetworkState state : states) { - if (state.networkInfo != null && state.networkInfo.isConnected() - && !state.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)) { + if (!state.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)) { matchingIfaces.clear(); collectIfaces(matchingIfaces, state); for (int j = matchingIfaces.size() - 1; j >= 0; j--) { @@ -2162,13 +2163,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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 +3488,27 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @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 +3520,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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 +3617,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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 +4797,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } 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 +4932,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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 +5402,17 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } @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 +5421,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index 0ab35a911025..9706bcece924 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -96,7 +96,6 @@ import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkIdentity; -import android.net.NetworkInfo; import android.net.NetworkStack; import android.net.NetworkState; import android.net.NetworkStats; @@ -1264,7 +1263,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { /** * Inspect all current {@link NetworkState} to derive mapping from {@code iface} to {@link - * NetworkStatsHistory}. When multiple {@link NetworkInfo} are active on a single {@code iface}, + * NetworkStatsHistory}. When multiple networks are active on a single {@code iface}, * they are combined under a single {@link NetworkIdentitySet}. */ @GuardedBy("mStatsLock") @@ -1294,84 +1293,82 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final boolean combineSubtypeEnabled = mSettings.getCombineSubtypeEnabled(); final ArraySet<String> mobileIfaces = new ArraySet<>(); for (NetworkState state : states) { - if (state.networkInfo.isConnected()) { - final boolean isMobile = isNetworkTypeMobile(state.networkInfo.getType()); - final boolean isDefault = ArrayUtils.contains(mDefaultNetworks, state.network); - final int subType = combineSubtypeEnabled ? SUBTYPE_COMBINED - : getSubTypeForState(state); - final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state, - isDefault, subType); - - // Traffic occurring on the base interface is always counted for - // both total usage and UID details. - final String baseIface = state.linkProperties.getInterfaceName(); - if (baseIface != null) { - findOrCreateNetworkIdentitySet(mActiveIfaces, baseIface).add(ident); - findOrCreateNetworkIdentitySet(mActiveUidIfaces, baseIface).add(ident); - - // Build a separate virtual interface for VT (Video Telephony) data usage. - // Only do this when IMS is not metered, but VT is metered. - // If IMS is metered, then the IMS network usage has already included VT usage. - // VT is considered always metered in framework's layer. If VT is not metered - // per carrier's policy, modem will report 0 usage for VT calls. - if (state.networkCapabilities.hasCapability( - NetworkCapabilities.NET_CAPABILITY_IMS) && !ident.getMetered()) { - - // Copy the identify from IMS one but mark it as metered. - NetworkIdentity vtIdent = new NetworkIdentity(ident.getType(), - ident.getSubType(), ident.getSubscriberId(), ident.getNetworkId(), - ident.getRoaming(), true /* metered */, - true /* onDefaultNetwork */); - findOrCreateNetworkIdentitySet(mActiveIfaces, IFACE_VT).add(vtIdent); - findOrCreateNetworkIdentitySet(mActiveUidIfaces, IFACE_VT).add(vtIdent); - } + final boolean isMobile = isNetworkTypeMobile(state.legacyNetworkType); + final boolean isDefault = ArrayUtils.contains(mDefaultNetworks, state.network); + final int subType = combineSubtypeEnabled ? SUBTYPE_COMBINED + : getSubTypeForState(state); + final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state, + isDefault, subType); + + // Traffic occurring on the base interface is always counted for + // both total usage and UID details. + final String baseIface = state.linkProperties.getInterfaceName(); + if (baseIface != null) { + findOrCreateNetworkIdentitySet(mActiveIfaces, baseIface).add(ident); + findOrCreateNetworkIdentitySet(mActiveUidIfaces, baseIface).add(ident); + + // Build a separate virtual interface for VT (Video Telephony) data usage. + // Only do this when IMS is not metered, but VT is metered. + // If IMS is metered, then the IMS network usage has already included VT usage. + // VT is considered always metered in framework's layer. If VT is not metered + // per carrier's policy, modem will report 0 usage for VT calls. + if (state.networkCapabilities.hasCapability( + NetworkCapabilities.NET_CAPABILITY_IMS) && !ident.getMetered()) { + + // Copy the identify from IMS one but mark it as metered. + NetworkIdentity vtIdent = new NetworkIdentity(ident.getType(), + ident.getSubType(), ident.getSubscriberId(), ident.getNetworkId(), + ident.getRoaming(), true /* metered */, + true /* onDefaultNetwork */); + findOrCreateNetworkIdentitySet(mActiveIfaces, IFACE_VT).add(vtIdent); + findOrCreateNetworkIdentitySet(mActiveUidIfaces, IFACE_VT).add(vtIdent); + } - if (isMobile) { - mobileIfaces.add(baseIface); - } + if (isMobile) { + mobileIfaces.add(baseIface); } + } - // Traffic occurring on stacked interfaces is usually clatd. - // - // UID stats are always counted on the stacked interface and never on the base - // interface, because the packets on the base interface do not actually match - // application sockets (they're not IPv4) and thus the app uid is not known. - // For receive this is obvious: packets must be translated from IPv6 to IPv4 - // before the application socket can be found. - // For transmit: either they go through the clat daemon which by virtue of going - // through userspace strips the original socket association during the IPv4 to - // IPv6 translation process, or they are offloaded by eBPF, which doesn't: - // However, on an ebpf device the accounting is done in cgroup ebpf hooks, - // which don't trigger again post ebpf translation. - // (as such stats accounted to the clat uid are ignored) - // - // Interface stats are more complicated. - // - // eBPF offloaded 464xlat'ed packets never hit base interface ip6tables, and thus - // *all* statistics are collected by iptables on the stacked v4-* interface. - // - // Additionally for ingress all packets bound for the clat IPv6 address are dropped - // in ip6tables raw prerouting and thus even non-offloaded packets are only - // accounted for on the stacked interface. - // - // For egress, packets subject to eBPF offload never appear on the base interface - // and only appear on the stacked interface. Thus to ensure packets increment - // interface stats, we must collate data from stacked interfaces. For xt_qtaguid - // (or non eBPF offloaded) TX they would appear on both, however egress interface - // accounting is explicitly bypassed for traffic from the clat uid. - // - final List<LinkProperties> stackedLinks = state.linkProperties.getStackedLinks(); - for (LinkProperties stackedLink : stackedLinks) { - final String stackedIface = stackedLink.getInterfaceName(); - if (stackedIface != null) { - findOrCreateNetworkIdentitySet(mActiveIfaces, stackedIface).add(ident); - findOrCreateNetworkIdentitySet(mActiveUidIfaces, stackedIface).add(ident); - if (isMobile) { - mobileIfaces.add(stackedIface); - } - - mStatsFactory.noteStackedIface(stackedIface, baseIface); + // Traffic occurring on stacked interfaces is usually clatd. + // + // UID stats are always counted on the stacked interface and never on the base + // interface, because the packets on the base interface do not actually match + // application sockets (they're not IPv4) and thus the app uid is not known. + // For receive this is obvious: packets must be translated from IPv6 to IPv4 + // before the application socket can be found. + // For transmit: either they go through the clat daemon which by virtue of going + // through userspace strips the original socket association during the IPv4 to + // IPv6 translation process, or they are offloaded by eBPF, which doesn't: + // However, on an ebpf device the accounting is done in cgroup ebpf hooks, + // which don't trigger again post ebpf translation. + // (as such stats accounted to the clat uid are ignored) + // + // Interface stats are more complicated. + // + // eBPF offloaded 464xlat'ed packets never hit base interface ip6tables, and thus + // *all* statistics are collected by iptables on the stacked v4-* interface. + // + // Additionally for ingress all packets bound for the clat IPv6 address are dropped + // in ip6tables raw prerouting and thus even non-offloaded packets are only + // accounted for on the stacked interface. + // + // For egress, packets subject to eBPF offload never appear on the base interface + // and only appear on the stacked interface. Thus to ensure packets increment + // interface stats, we must collate data from stacked interfaces. For xt_qtaguid + // (or non eBPF offloaded) TX they would appear on both, however egress interface + // accounting is explicitly bypassed for traffic from the clat uid. + // + final List<LinkProperties> stackedLinks = state.linkProperties.getStackedLinks(); + for (LinkProperties stackedLink : stackedLinks) { + final String stackedIface = stackedLink.getInterfaceName(); + if (stackedIface != null) { + findOrCreateNetworkIdentitySet(mActiveIfaces, stackedIface).add(ident); + findOrCreateNetworkIdentitySet(mActiveUidIfaces, stackedIface).add(ident); + if (isMobile) { + mobileIfaces.add(stackedIface); } + + mStatsFactory.noteStackedIface(stackedIface, baseIface); } } } diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index c3cb42f95cc6..45419fe3bf76 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_ALL; 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 5417275bc8f1..2067fd081b4a 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_ENABLED; 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 000000000000..a83edb75badb --- /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 000000000000..cb3c7ff0c07d --- /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/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java index 48ec9b4b502d..acec93cac34d 100644 --- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java +++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java @@ -241,7 +241,7 @@ public class BackgroundDexOptService extends JobService { // trade-off worth doing to save boot time work. int result = pm.performDexOptWithStatus(new DexoptOptions( pkg, - PackageManagerService.REASON_BOOT, + PackageManagerService.REASON_POST_BOOT, DexoptOptions.DEXOPT_BOOT_COMPLETE)); if (result == PackageDexOptimizer.DEX_OPT_PERFORMED) { updatedPackages.add(pkg); diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index 79607351b18a..4a2fb5da5e70 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -687,7 +687,8 @@ public class PackageDexOptimizer { boolean generateCompactDex = true; switch (compilationReason) { case PackageManagerService.REASON_FIRST_BOOT: - case PackageManagerService.REASON_BOOT: + case PackageManagerService.REASON_BOOT_AFTER_OTA: + case PackageManagerService.REASON_POST_BOOT: case PackageManagerService.REASON_INSTALL: generateCompactDex = false; } diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 330f99523507..9f0efa5fad83 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 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements 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 6594a90d2478..ae2e58ff8f55 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index a7b9622ab3c0..febbfbce9e6c 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -676,17 +676,18 @@ public class PackageManagerService extends IPackageManager.Stub // Compilation reasons. public static final int REASON_UNKNOWN = -1; public static final int REASON_FIRST_BOOT = 0; - public static final int REASON_BOOT = 1; - public static final int REASON_INSTALL = 2; - public static final int REASON_INSTALL_FAST = 3; - public static final int REASON_INSTALL_BULK = 4; - public static final int REASON_INSTALL_BULK_SECONDARY = 5; - public static final int REASON_INSTALL_BULK_DOWNGRADED = 6; - public static final int REASON_INSTALL_BULK_SECONDARY_DOWNGRADED = 7; - public static final int REASON_BACKGROUND_DEXOPT = 8; - public static final int REASON_AB_OTA = 9; - public static final int REASON_INACTIVE_PACKAGE_DOWNGRADE = 10; - public static final int REASON_SHARED = 11; + public static final int REASON_BOOT_AFTER_OTA = 1; + public static final int REASON_POST_BOOT = 2; + public static final int REASON_INSTALL = 3; + public static final int REASON_INSTALL_FAST = 4; + public static final int REASON_INSTALL_BULK = 5; + public static final int REASON_INSTALL_BULK_SECONDARY = 6; + public static final int REASON_INSTALL_BULK_DOWNGRADED = 7; + public static final int REASON_INSTALL_BULK_SECONDARY_DOWNGRADED = 8; + public static final int REASON_BACKGROUND_DEXOPT = 9; + public static final int REASON_AB_OTA = 10; + public static final int REASON_INACTIVE_PACKAGE_DOWNGRADE = 11; + public static final int REASON_SHARED = 12; public static final int REASON_LAST = REASON_SHARED; @@ -9664,10 +9665,7 @@ public class PackageManagerService extends IPackageManager.Stub // first boot, as they do not have profile data. boolean causeFirstBoot = isFirstBoot() || mIsPreNUpgrade; - // We need to re-extract after a pruned cache, as AoT-ed files will be out of date. - boolean causePrunedCache = VMRuntime.didPruneDalvikCache(); - - if (!causeUpgrade && !causeFirstBoot && !causePrunedCache) { + if (!causeUpgrade && !causeFirstBoot) { return; } @@ -9684,7 +9682,7 @@ public class PackageManagerService extends IPackageManager.Stub final long startTime = System.nanoTime(); final int[] stats = performDexOptUpgrade(pkgs, mIsPreNUpgrade /* showDialog */, - causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT, + causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT_AFTER_OTA, false /* bootComplete */); final int elapsedTimeSeconds = diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java index 9cd55a6bb07e..636db111be88 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java @@ -29,7 +29,8 @@ public class PackageManagerServiceCompilerMapping { // Names for compilation reasons. public static final String REASON_STRINGS[] = { "first-boot", - "boot", + "boot-after-ota", + "post-boot", "install", "install-fast", "install-bulk", diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java index 6e145b5ecbe4..7de5c9467930 100644 --- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java +++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java @@ -587,7 +587,7 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { private static final int TRON_COMPILATION_REASON_ERROR = 0; private static final int TRON_COMPILATION_REASON_UNKNOWN = 1; private static final int TRON_COMPILATION_REASON_FIRST_BOOT = 2; - private static final int TRON_COMPILATION_REASON_BOOT = 3; + private static final int TRON_COMPILATION_REASON_BOOT_DEPRECATED_SINCE_S = 3; private static final int TRON_COMPILATION_REASON_INSTALL = 4; private static final int TRON_COMPILATION_REASON_BG_DEXOPT = 5; private static final int TRON_COMPILATION_REASON_AB_OTA = 6; @@ -605,6 +605,8 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { private static final int TRON_COMPILATION_REASON_INSTALL_BULK_DOWNGRADED_WITH_DM = 18; private static final int TRON_COMPILATION_REASON_INSTALL_BULK_SECONDARY_DOWNGRADED_WITH_DM = 19; + private static final int TRON_COMPILATION_REASON_BOOT_AFTER_OTA = 20; + private static final int TRON_COMPILATION_REASON_POST_BOOT = 21; // The annotation to add as a suffix to the compilation reason when dexopt was // performed with dex metadata. @@ -618,7 +620,8 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { case "unknown" : return TRON_COMPILATION_REASON_UNKNOWN; case "error" : return TRON_COMPILATION_REASON_ERROR; case "first-boot" : return TRON_COMPILATION_REASON_FIRST_BOOT; - case "boot" : return TRON_COMPILATION_REASON_BOOT; + case "boot-after-ota": return TRON_COMPILATION_REASON_BOOT_AFTER_OTA; + case "post-boot" : return TRON_COMPILATION_REASON_POST_BOOT; case "install" : return TRON_COMPILATION_REASON_INSTALL; case "bg-dexopt" : return TRON_COMPILATION_REASON_BG_DEXOPT; case "ab-ota" : return TRON_COMPILATION_REASON_AB_OTA; diff --git a/services/core/java/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdater.java b/services/core/java/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdater.java new file mode 100644 index 000000000000..6cdd4df824a8 --- /dev/null +++ b/services/core/java/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdater.java @@ -0,0 +1,35 @@ +/* + * 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.pm.parsing.library; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.pm.parsing.pkg.ParsedPackage; + +/** + * Updates a package to remove dependency on android.net.ipsec.ike library. + * + * @hide + */ +@VisibleForTesting +public class AndroidNetIpSecIkeUpdater extends PackageSharedLibraryUpdater { + + private static final String LIBRARY_NAME = "android.net.ipsec.ike"; + + @Override + public void updatePackage(ParsedPackage parsedPackage, boolean isUpdatedSystemApp) { + removeLibrary(parsedPackage, LIBRARY_NAME); + } +} diff --git a/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java b/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java index 1405a7d613f1..8a8a302734b1 100644 --- a/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java +++ b/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java @@ -45,6 +45,9 @@ public class PackageBackwardCompatibility extends PackageSharedLibraryUpdater { static { final List<PackageSharedLibraryUpdater> packageUpdaters = new ArrayList<>(); + // Remove android.net.ipsec.ike library, it is added to boot classpath since Android S. + packageUpdaters.add(new AndroidNetIpSecIkeUpdater()); + // Remove com.google.android.maps library. packageUpdaters.add(new ComGoogleAndroidMapsUpdater()); diff --git a/services/core/java/com/android/server/tracing/OWNERS b/services/core/java/com/android/server/tracing/OWNERS new file mode 100644 index 000000000000..f5de4eb05c54 --- /dev/null +++ b/services/core/java/com/android/server/tracing/OWNERS @@ -0,0 +1,2 @@ +cfijalkovich@google.com +carmenjackson@google.com diff --git a/services/core/java/com/android/server/tracing/TracingServiceProxy.java b/services/core/java/com/android/server/tracing/TracingServiceProxy.java new file mode 100644 index 000000000000..8f227489740f --- /dev/null +++ b/services/core/java/com/android/server/tracing/TracingServiceProxy.java @@ -0,0 +1,99 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.tracing; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.UserHandle; +import android.tracing.ITracingServiceProxy; +import android.util.Log; + +import com.android.server.SystemService; + +/** + * TracingServiceProxy is the system_server intermediary between the Perfetto tracing daemon and the + * system tracing app Traceur. + * + * @hide + */ +public class TracingServiceProxy extends SystemService { + private static final String TAG = "TracingServiceProxy"; + + public static final String TRACING_SERVICE_PROXY_BINDER_NAME = "tracing.proxy"; + + private static final String TRACING_APP_PACKAGE_NAME = "com.android.traceur"; + private static final String TRACING_APP_ACTIVITY = "com.android.traceur.StopTraceService"; + + // Keep this in sync with the definitions in TraceService + private static final String INTENT_ACTION_NOTIFY_SESSION_STOPPED = + "com.android.traceur.NOTIFY_SESSION_STOPPED"; + private static final String INTENT_ACTION_NOTIFY_SESSION_STOLEN = + "com.android.traceur.NOTIFY_SESSION_STOLEN"; + + private final Context mContext; + private final PackageManager mPackageManager; + + private final ITracingServiceProxy.Stub mTracingServiceProxy = new ITracingServiceProxy.Stub() { + /** + * Notifies system tracing app that a tracing session has ended. If a session is repurposed + * for use in a bugreport, sessionStolen can be set to indicate that tracing has ended but + * there is no buffer available to dump. + */ + @Override + public void notifyTraceSessionEnded(boolean sessionStolen) { + notifyTraceur(sessionStolen); + } + }; + + public TracingServiceProxy(Context context) { + super(context); + mContext = context; + mPackageManager = context.getPackageManager(); + } + + @Override + public void onStart() { + publishBinderService(TRACING_SERVICE_PROXY_BINDER_NAME, mTracingServiceProxy); + } + + private void notifyTraceur(boolean sessionStolen) { + final Intent intent = new Intent(); + + try { + // Validate that Traceur is a system app. + PackageInfo info = mPackageManager.getPackageInfo(TRACING_APP_PACKAGE_NAME, + PackageManager.MATCH_SYSTEM_ONLY); + + intent.setClassName(info.packageName, TRACING_APP_ACTIVITY); + if (sessionStolen) { + intent.setAction(INTENT_ACTION_NOTIFY_SESSION_STOLEN); + } else { + intent.setAction(INTENT_ACTION_NOTIFY_SESSION_STOPPED); + } + + try { + mContext.startForegroundServiceAsUser(intent, UserHandle.SYSTEM); + } catch (RuntimeException e) { + Log.e(TAG, "Failed to notifyTraceSessionEnded", e); + } + } catch (NameNotFoundException e) { + Log.e(TAG, "Failed to locate Traceur", e); + } + } +} diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java index 3dfb99e5c0fc..bba5dcb0d1b9 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 @@ class TvInputHardwareManager implements TvInputHal.Callback { 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 fd12c2d2ebb8..b6ddd93af3b8 100644 --- a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java +++ b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java @@ -28,16 +28,15 @@ import android.net.NetworkRequest; import android.net.TelephonyNetworkSpecifier; import android.os.Handler; import android.os.ParcelUuid; -import android.telephony.SubscriptionInfo; -import android.telephony.SubscriptionManager; +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 com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; -import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; @@ -55,19 +54,18 @@ public class UnderlyingNetworkTracker { @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 SubscriptionManager mSubscriptionManager; - @NonNull private final SparseArray<NetworkCallback> mCellBringupCallbacks = new SparseArray<>(); + @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 final Set<Integer> mSubIds = new ArraySet<>(); - - @NonNull private final Set<Integer> mRequiredUnderlyingNetworkCapabilities; + @NonNull private TelephonySubscriptionSnapshot mLastSnapshot; + private boolean mIsRunning = true; @Nullable private UnderlyingNetworkRecord mCurrentRecord; @Nullable private UnderlyingNetworkRecord.Builder mRecordInProgress; @@ -75,11 +73,13 @@ public class UnderlyingNetworkTracker { public UnderlyingNetworkTracker( @NonNull VcnContext vcnContext, @NonNull ParcelUuid subscriptionGroup, + @NonNull TelephonySubscriptionSnapshot snapshot, @NonNull Set<Integer> requiredUnderlyingNetworkCapabilities, @NonNull UnderlyingNetworkTrackerCallback cb) { this( vcnContext, subscriptionGroup, + snapshot, requiredUnderlyingNetworkCapabilities, cb, new Dependencies()); @@ -88,11 +88,13 @@ public class UnderlyingNetworkTracker { private UnderlyingNetworkTracker( @NonNull VcnContext vcnContext, @NonNull ParcelUuid subscriptionGroup, + @NonNull TelephonySubscriptionSnapshot snapshot, @NonNull Set<Integer> requiredUnderlyingNetworkCapabilities, @NonNull UnderlyingNetworkTrackerCallback cb, @NonNull Dependencies deps) { mVcnContext = Objects.requireNonNull(vcnContext, "Missing vcnContext"); mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup"); + mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot"); mRequiredUnderlyingNetworkCapabilities = Objects.requireNonNull( requiredUnderlyingNetworkCapabilities, @@ -103,7 +105,6 @@ public class UnderlyingNetworkTracker { mHandler = new Handler(mVcnContext.getLooper()); mConnectivityManager = mVcnContext.getContext().getSystemService(ConnectivityManager.class); - mSubscriptionManager = mVcnContext.getContext().getSystemService(SubscriptionManager.class); registerNetworkRequests(); } @@ -149,36 +150,49 @@ public class UnderlyingNetworkTracker { private void updateSubIdsAndCellularRequests() { mVcnContext.ensureRunningOnLooperThread(); - Set<Integer> prevSubIds = new ArraySet<>(mSubIds); - mSubIds.clear(); + // Don't bother re-filing NetworkRequests if this Tracker has been torn down. + if (!mIsRunning) { + return; + } - // Ensure NetworkRequests filed for all current subIds in mSubscriptionGroup - // STOPSHIP: b/177364490 use TelephonySubscriptionSnapshot to avoid querying Telephony - List<SubscriptionInfo> subInfos = - mSubscriptionManager.getSubscriptionsInGroup(mSubscriptionGroup); + final Set<Integer> subIdsInSubGroup = mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup); - for (SubscriptionInfo subInfo : subInfos) { - final int subId = subInfo.getSubscriptionId(); - mSubIds.add(subId); + // new subIds to track = (updated list of subIds) - (currently tracked subIds) + final Set<Integer> subIdsToRegister = new ArraySet<>(subIdsInSubGroup); + subIdsToRegister.removeAll(mCellBringupCallbacks.keySet()); - if (!mCellBringupCallbacks.contains(subId)) { - final NetworkBringupCallback cb = new NetworkBringupCallback(); - mCellBringupCallbacks.put(subId, cb); + // subIds to stop tracking = (currently tracked subIds) - (updated list of subIds) + final Set<Integer> subIdsToUnregister = new ArraySet<>(mCellBringupCallbacks.keySet()); + subIdsToUnregister.removeAll(subIdsInSubGroup); - mConnectivityManager.requestBackgroundNetwork( - getCellNetworkRequestForSubId(subId), mHandler, cb); - } + for (final int subId : subIdsToRegister) { + final NetworkBringupCallback cb = new NetworkBringupCallback(); + mCellBringupCallbacks.put(subId, cb); + + mConnectivityManager.requestBackgroundNetwork( + getCellNetworkRequestForSubId(subId), mHandler, cb); } - // unregister all NetworkCallbacks for outdated subIds - for (final int subId : prevSubIds) { - if (!mSubIds.contains(subId)) { - final NetworkCallback cb = mCellBringupCallbacks.removeReturnOld(subId); - mConnectivityManager.unregisterNetworkCallback(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() { mVcnContext.ensureRunningOnLooperThread(); @@ -186,11 +200,12 @@ public class UnderlyingNetworkTracker { mConnectivityManager.unregisterNetworkCallback(mWifiBringupCallback); mConnectivityManager.unregisterNetworkCallback(mRouteSelectionCallback); - for (final int subId : mSubIds) { - final NetworkCallback cb = mCellBringupCallbacks.removeReturnOld(subId); + for (final NetworkCallback cb : mCellBringupCallbacks.values()) { mConnectivityManager.unregisterNetworkCallback(cb); } - mSubIds.clear(); + mCellBringupCallbacks.clear(); + + mIsRunning = false; } /** Returns whether the currently selected Network matches the given network. */ diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java index 132883e4a041..3726407211d5 100644 --- a/services/core/java/com/android/server/vcn/Vcn.java +++ b/services/core/java/com/android/server/vcn/Vcn.java @@ -16,6 +16,7 @@ package com.android.server.vcn; +import static com.android.server.VcnManagementService.VDBG; import android.annotation.NonNull; import android.net.NetworkCapabilities; @@ -27,9 +28,18 @@ import android.os.Message; 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 +73,86 @@ public class Vcn extends Handler { */ 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 +165,29 @@ public class Vcn extends Handler { 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 +199,7 @@ public class Vcn extends Handler { @Override public void handleMessage(@NonNull Message msg) { - if (!mIsRunning) { + if (!isActive()) { return; } @@ -137,9 +210,15 @@ public class Vcn extends Handler { 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); } @@ -147,7 +226,7 @@ public class Vcn extends Handler { private void handleConfigUpdated(@NonNull VcnConfig config) { // TODO: Add a dump function in VcnConfig that omits PII. Until then, use hashCode() - Slog.v(getLogTag(), String.format("Config updated: config = %s", config.hashCode())); + Slog.v(getLogTag(), "Config updated: config = " + config.hashCode()); mConfig = config; @@ -161,23 +240,41 @@ public class Vcn extends Handler { 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 already satisfied by higher-scoring (" + score + ") network from " - + "provider " + providerId + ": " + request); + if (VDBG) { + Slog.v( + getLogTag(), + "Request already satisfied by higher-scoring (" + + score + + ") network from " + + "provider " + + providerId + + ": " + + request); + } return; } // If preexisting VcnGatewayConnection(s) satisfy request, return for (VcnGatewayConnectionConfig gatewayConnectionConfig : mVcnGatewayConnections.keySet()) { if (requestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) { - Slog.v(getLogTag(), - "Request already satisfied by existing VcnGatewayConnection: " + request); + if (VDBG) { + Slog.v( + getLogTag(), + "Request already satisfied by existing VcnGatewayConnection: " + + request); + } return; } } @@ -192,13 +289,27 @@ public class Vcn extends Handler { "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.Builder builder = new NetworkCapabilities.Builder(); @@ -210,15 +321,47 @@ public class Vcn extends Handler { } private String getLogTag() { - return String.format("%s [%d]", TAG, mSubscriptionGroup.hashCode()); + return TAG + " [" + mSubscriptionGroup.hashCode() + "]"; } /** 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/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java index 39c96069f9c6..2503e812f9e1 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.LinkAddress; 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,23 +52,30 @@ import android.net.ipsec.ike.IkeSessionParams; 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.List; import java.util.Objects; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -113,6 +125,9 @@ import java.util.concurrent.TimeUnit; 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; @@ -283,9 +298,9 @@ public class VcnGatewayConnection extends StateMachine { private static final int EVENT_SETUP_COMPLETED = 6; private static class EventSetupCompletedInfo implements EventInfo { - @NonNull public final ChildSessionConfiguration childSessionConfig; + @NonNull public final VcnChildSessionConfiguration childSessionConfig; - EventSetupCompletedInfo(@NonNull ChildSessionConfiguration childSessionConfig) { + EventSetupCompletedInfo(@NonNull VcnChildSessionConfiguration childSessionConfig) { this.childSessionConfig = Objects.requireNonNull(childSessionConfig); } @@ -359,6 +374,16 @@ public class VcnGatewayConnection extends StateMachine { */ 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(); @@ -379,10 +404,13 @@ public class VcnGatewayConnection extends StateMachine { @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; @@ -444,7 +472,7 @@ public class VcnGatewayConnection extends StateMachine { * <p>Set in Connected and Migrating states, always @NonNull in Connected, Migrating * states, @Nullable otherwise. */ - private ChildSessionConfiguration mChildConfig; + private VcnChildSessionConfiguration mChildConfig; /** * The active network agent. @@ -457,28 +485,43 @@ public class VcnGatewayConnection extends StateMachine { 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, + mLastSnapshot, mConnectionConfig.getAllUnderlyingCapabilities(), mUnderlyingNetworkTrackerCallback); mIpSecManager = mVcnContext.getContext().getSystemService(IpSecManager.class); @@ -533,10 +576,28 @@ public class VcnGatewayConnection extends StateMachine { 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) { @@ -599,7 +660,7 @@ public class VcnGatewayConnection extends StateMachine { new EventTransformCreatedInfo(direction, transform)); } - private void childOpened(int token, @NonNull ChildSessionConfiguration childConfig) { + private void childOpened(int token, @NonNull VcnChildSessionConfiguration childConfig) { sendMessage(EVENT_SETUP_COMPLETED, token, new EventSetupCompletedInfo(childConfig)); } @@ -668,7 +729,8 @@ public class VcnGatewayConnection extends StateMachine { 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: @@ -921,6 +983,8 @@ public class VcnGatewayConnection extends StateMachine { 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); @@ -941,7 +1005,108 @@ public class VcnGatewayConnection extends StateMachine { } } - private abstract class ConnectedStateBase extends ActiveBaseState {} + private abstract class ConnectedStateBase extends ActiveBaseState { + protected void updateNetworkAgent( + @NonNull IpSecTunnelInterface tunnelIface, + @NonNull NetworkAgent agent, + @NonNull VcnChildSessionConfiguration 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 VcnChildSessionConfiguration 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 VcnChildSessionConfiguration childConfig) { + setupInterface(token, tunnelIface, childConfig, null); + } + + protected void setupInterface( + int token, + @NonNull IpSecTunnelInterface tunnelIface, + @NonNull VcnChildSessionConfiguration childConfig, + @Nullable VcnChildSessionConfiguration 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. @@ -951,7 +1116,89 @@ public class VcnGatewayConnection extends StateMachine { */ 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 VcnChildSessionConfiguration childConfig) { + setupInterface(token, tunnelIface, childConfig); + + if (mNetworkAgent == null) { + mNetworkAgent = buildNetworkAgent(tunnelIface, childConfig); + } else { + updateNetworkAgent(tunnelIface, mNetworkAgent, childConfig); + } + } } /** @@ -961,12 +1208,70 @@ public class VcnGatewayConnection extends StateMachine { */ class RetryTimeoutState extends ActiveBaseState { @Override - protected void processStateMsg(Message msg) {} + protected void enterState() throws Exception { + // Reset upon entry to ConnectedState + mFailedAttempts++; + + if (mUnderlying == null) { + Slog.wtf(TAG, "Underlying network was null in retry state"); + transitionTo(mDisconnectedState); + } else { + sendMessageDelayed( + EVENT_RETRY_TIMEOUT_EXPIRED, mCurrentToken, getNextRetryIntervalsMs()); + } + } + + @Override + protected void processStateMsg(Message msg) { + switch (msg.what) { + case EVENT_UNDERLYING_NETWORK_CHANGED: + final UnderlyingNetworkRecord oldUnderlying = mUnderlying; + mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying; + + // If new underlying is null, all networks were lost; go back to disconnected. + if (mUnderlying == null) { + removeMessages(EVENT_RETRY_TIMEOUT_EXPIRED); + + transitionTo(mDisconnectedState); + return; + } else if (oldUnderlying != null + && mUnderlying.network.equals(oldUnderlying.network)) { + // If the network has not changed, do nothing. + return; + } + + // Fallthrough + case EVENT_RETRY_TIMEOUT_EXPIRED: + removeMessages(EVENT_RETRY_TIMEOUT_EXPIRED); + + transitionTo(mConnectingState); + break; + case EVENT_DISCONNECT_REQUESTED: + handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason); + break; + default: + logUnhandledMessage(msg); + break; + } + } + + private long getNextRetryIntervalsMs() { + final int retryDelayIndex = mFailedAttempts - 1; + final long[] retryIntervalsMs = mConnectionConfig.getRetryIntervalsMs(); + + // Repeatedly use last item in retry timeout list. + if (retryDelayIndex >= retryIntervalsMs.length) { + return retryIntervalsMs[retryIntervalsMs.length - 1]; + } + + return retryIntervalsMs[retryDelayIndex]; + } } @VisibleForTesting(visibility = Visibility.PRIVATE) static NetworkCapabilities buildNetworkCapabilities( - @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig) { + @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig, + @Nullable UnderlyingNetworkRecord underlying) { final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder(); builder.addTransportType(TRANSPORT_CELLULAR); @@ -978,13 +1283,59 @@ public class VcnGatewayConnection extends StateMachine { builder.addCapability(cap); } + 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( @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig, @NonNull IpSecTunnelInterface tunnelIface, - @NonNull ChildSessionConfiguration childConfig) { + @NonNull VcnChildSessionConfiguration childConfig) { final LinkProperties lp = new LinkProperties(); lp.setInterfaceName(tunnelIface.getInterfaceName()); @@ -1035,20 +1386,28 @@ public class VcnGatewayConnection extends StateMachine { } } - private class ChildSessionCallbackImpl implements ChildSessionCallback { + /** Implementation of ChildSessionCallback, exposed for testing. */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + public class VcnChildSessionCallback implements ChildSessionCallback { private final int mToken; - ChildSessionCallbackImpl(int token) { + VcnChildSessionCallback(int token) { mToken = token; } - @Override - public void onOpened(@NonNull ChildSessionConfiguration childConfig) { + /** Internal proxy method for injecting of mocked ChildSessionConfiguration */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + void onOpened(@NonNull VcnChildSessionConfiguration childConfig) { Slog.v(TAG, "ChildOpened for token " + mToken); childOpened(mToken, childConfig); } @Override + public void onOpened(@NonNull ChildSessionConfiguration childConfig) { + onOpened(new VcnChildSessionConfiguration(childConfig)); + } + + @Override public void onClosed() { Slog.v(TAG, "ChildClosed for token " + mToken); sessionLost(mToken, null); @@ -1128,7 +1487,7 @@ public class VcnGatewayConnection extends StateMachine { buildIkeParams(), buildChildParams(), new IkeSessionCallbackImpl(token), - new ChildSessionCallbackImpl(token)); + new VcnChildSessionCallback(token)); } /** External dependencies used by VcnGatewayConnection, for injection in tests */ @@ -1138,10 +1497,15 @@ public class VcnGatewayConnection extends StateMachine { public UnderlyingNetworkTracker newUnderlyingNetworkTracker( VcnContext vcnContext, ParcelUuid subscriptionGroup, + TelephonySubscriptionSnapshot snapshot, Set<Integer> requiredUnderlyingNetworkCapabilities, UnderlyingNetworkTrackerCallback callback) { return new UnderlyingNetworkTracker( - vcnContext, subscriptionGroup, requiredUnderlyingNetworkCapabilities, callback); + vcnContext, + subscriptionGroup, + snapshot, + requiredUnderlyingNetworkCapabilities, + callback); } /** Builds a new IkeSession. */ @@ -1160,6 +1524,35 @@ public class VcnGatewayConnection extends StateMachine { } } + /** + * Proxy implementation of Child Session Configuration, used for testing. + * + * <p>This wrapper allows mocking of the final, parcelable ChildSessionConfiguration object for + * testing purposes. This is the unfortunate result of mockito-inline (for mocking final + * classes) not working properly with system services & associated classes. + * + * <p>This class MUST EXCLUSIVELY be a passthrough, proxying calls directly to the actual + * ChildSessionConfiguration. + */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + public static class VcnChildSessionConfiguration { + private final ChildSessionConfiguration mChildConfig; + + public VcnChildSessionConfiguration(ChildSessionConfiguration childConfig) { + mChildConfig = childConfig; + } + + /** Retrieves the addresses to be used inside the tunnel. */ + public List<LinkAddress> getInternalAddresses() { + return mChildConfig.getInternalAddresses(); + } + + /** Retrieves the DNS servers to be used inside the tunnel. */ + public List<InetAddress> getInternalDnsServers() { + return mChildConfig.getInternalDnsServers(); + } + } + /** Proxy implementation of IKE session, used for testing. */ @VisibleForTesting(visibility = Visibility.PRIVATE) public static class VcnIkeSession { diff --git a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java index b9babae4c6b7..bfeec011a2c9 100644 --- a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java +++ b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java @@ -16,6 +16,8 @@ package com.android.server.vcn; +import static com.android.server.VcnManagementService.VDBG; + import android.annotation.NonNull; import android.content.Context; import android.net.NetworkProvider; @@ -25,6 +27,9 @@ 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 java.util.Objects; import java.util.Set; @@ -52,8 +57,13 @@ public class VcnNetworkProvider extends NetworkProvider { 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 @@ -62,8 +72,9 @@ public class VcnNetworkProvider extends NetworkProvider { } } - // 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); } @@ -74,11 +85,16 @@ public class VcnNetworkProvider extends NetworkProvider { @Override public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) { - Slog.v( - TAG, - String.format( - "Network requested: Request = %s, score = %d, providerId = %d", - request, score, providerId)); + if (VDBG) { + Slog.v( + TAG, + "Network requested: Request = " + + request + + ", score = " + + score + + ", providerId = " + + providerId); + } final NetworkRequestEntry entry = new NetworkRequestEntry(request, score, providerId); diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 7af237b80cfa..fd8fa82ef0a5 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 @@ class ActivityStarter { 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/Android.bp b/services/core/jni/Android.bp index e0db93a01efd..87c766d961a2 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -147,11 +147,11 @@ cc_defaults { "android.hardware.light@2.0", "android.hardware.power@1.0", "android.hardware.power@1.1", - "android.hardware.power-cpp", + "android.hardware.power-V1-cpp", "android.hardware.power.stats@1.0", "android.hardware.thermal@1.0", "android.hardware.tv.input@1.0", - "android.hardware.vibrator-cpp", + "android.hardware.vibrator-V1-cpp", "android.hardware.vibrator@1.0", "android.hardware.vibrator@1.1", "android.hardware.vibrator@1.2", @@ -160,7 +160,7 @@ cc_defaults { "android.frameworks.schedulerservice@1.0", "android.frameworks.sensorservice@1.0", "android.frameworks.stats@1.0", - "android.system.suspend.control-cpp", + "android.system.suspend.control-V1-cpp", "android.system.suspend.control.internal-cpp", "android.system.suspend@1.0", "service.incremental", diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS index 389d07ae34b5..bbcc2c1e581b 100644 --- a/services/core/jni/OWNERS +++ b/services/core/jni/OWNERS @@ -11,6 +11,9 @@ per-file com_android_server_input_InputManagerService.cpp = michaelwr@google.com 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/incremental/Android.bp b/services/incremental/Android.bp index e978ed4000e0..7534c7c40a3d 100644 --- a/services/incremental/Android.bp +++ b/services/incremental/Android.bp @@ -51,9 +51,9 @@ cc_defaults { static_libs: [ "libbase", "libext2_uuid", - "libdataloader_aidl-unstable-cpp", - "libincremental_aidl-unstable-cpp", - "libincremental_manager_aidl-unstable-cpp", + "libdataloader_aidl-cpp", + "libincremental_aidl-cpp", + "libincremental_manager_aidl-cpp", "libprotobuf-cpp-lite", "service.incremental.proto", "libutils", diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp index 0ae10b6dc3b5..a3cadf3cc0b2 100644 --- a/services/incremental/BinderIncrementalService.cpp +++ b/services/incremental/BinderIncrementalService.cpp @@ -88,7 +88,6 @@ BinderIncrementalService* BinderIncrementalService::start(JNIEnv* env) { } 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 203de9dbcc07..62c3f43d2676 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.oemlock.OemLockService; 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; @@ -174,6 +175,7 @@ import com.android.server.telecom.TelecomLoaderService; import com.android.server.testharness.TestHarnessModeService; import com.android.server.textclassifier.TextClassificationManagerService; import com.android.server.textservices.TextServicesManagerService; +import com.android.server.tracing.TracingServiceProxy; import com.android.server.trust.TrustManagerService; import com.android.server.tv.TvInputManagerService; import com.android.server.tv.TvRemoteService; @@ -1072,6 +1074,11 @@ public final class SystemServer { 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); @@ -2201,6 +2208,11 @@ public final class SystemServer { mSystemServiceManager.startService(AppBindingService.Lifecycle.class); t.traceEnd(); + // Perfetto TracingServiceProxy + t.traceBegin("startTracingServiceProxy"); + mSystemServiceManager.startService(TracingServiceProxy.class); + t.traceEnd(); + // It is now time to start up the app processes... t.traceBegin("MakeVibratorServiceReady"); diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index 979f4e179e95..0aec701a0f9a 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -82,7 +82,7 @@ android_test { "libui", "libunwindstack", "libutils", - "netd_aidl_interface-cpp", + "netd_aidl_interface-V5-cpp", ], dxflags: ["--multi-dex"], diff --git a/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java b/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java index 50e7a0395a2a..58d6dae1637a 100644 --- a/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java +++ b/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java @@ -34,7 +34,7 @@ public class EntropyMixerTest extends AndroidTestCase { assertEquals(0, FileUtils.readTextFile(file, 0, null).length()); // The constructor has the side effect of writing to file - new EntropyMixer(getContext(), "/dev/null", file.getCanonicalPath(), "/dev/null"); + new EntropyMixer(getContext(), "/dev/null", file.getCanonicalPath()); assertTrue(FileUtils.readTextFile(file, 0, null).length() > 0); } diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java index 45bca6829553..1328b91d03f9 100644 --- a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java @@ -16,16 +16,18 @@ 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 static org.mockito.internal.verification.VerificationModeFactory.times; import android.app.IActivityManager; import android.content.BroadcastReceiver; @@ -48,6 +50,7 @@ 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; @@ -76,18 +79,21 @@ public final class AppHibernationServiceTest { 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(mContext, mIPackageManager, - mIActivityManager, mUserManager); + mAppHibernationService = new AppHibernationService(new MockInjector(mContext)); - verify(mContext, times(2)).registerReceiver(mReceiverCaptor.capture(), any()); + verify(mContext).registerReceiver(mReceiverCaptor.capture(), any()); mBroadcastReceiver = mReceiverCaptor.getValue(); doReturn(mUserInfos).when(mUserManager).getUsers(); @@ -95,12 +101,19 @@ public final class AppHibernationServiceTest { doAnswer(returnsArgAt(2)).when(mIActivityManager).handleIncomingUser(anyInt(), anyInt(), anyInt(), anyBoolean(), anyBoolean(), any(), any()); - addUser(USER_ID_1); + 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() throws RemoteException { + public void testSetHibernatingForUser_packageIsHibernating() { // WHEN we hibernate a package for a user mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true); @@ -109,8 +122,7 @@ public final class AppHibernationServiceTest { } @Test - public void testSetHibernatingForUser_newPackageAdded_packageIsHibernating() - throws RemoteException { + 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 */)); @@ -124,17 +136,12 @@ public final class AppHibernationServiceTest { } @Test - public void testSetHibernatingForUser_newUserAdded_packageIsHibernating() + public void testSetHibernatingForUser_newUserUnlocked_packageIsHibernating() throws RemoteException { // WHEN a new user is added and a package from the user is hibernated - List<PackageInfo> userPackages = new ArrayList<>(); - userPackages.add(makePackageInfo(PACKAGE_NAME_1)); - doReturn(new ParceledListSlice<>(userPackages)).when(mIPackageManager) - .getInstalledPackages(anyInt(), eq(USER_ID_2)); - Intent intent = new Intent(Intent.ACTION_USER_ADDED); - intent.putExtra(Intent.EXTRA_USER_HANDLE, USER_ID_2); - mBroadcastReceiver.onReceive(mContext, intent); - + 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 @@ -142,8 +149,7 @@ public final class AppHibernationServiceTest { } @Test - public void testIsHibernatingForUser_packageReplaced_stillReturnsHibernating() - throws RemoteException { + public void testIsHibernatingForUser_packageReplaced_stillReturnsHibernating() { // GIVEN a package is currently hibernated mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true); @@ -168,25 +174,25 @@ public final class AppHibernationServiceTest { } /** - * Add a mock user with one package. Must be called before - * {@link AppHibernationService#onBootPhase(int)} to work properly. + * Add a mock user with one package. */ - private void addUser(int userId) throws RemoteException { - addUser(userId, new String[]{PACKAGE_NAME_1}); + private UserInfo addUser(int userId) throws RemoteException { + return addUser(userId, new String[]{PACKAGE_NAME_1}); } /** - * Add a mock user with the packages specified. Must be called before - * {@link AppHibernationService#onBootPhase(int)} to work properly + * Add a mock user with the packages specified. */ - private void addUser(int userId, String[] packageNames) throws RemoteException { - mUserInfos.add(new UserInfo(userId, "user_" + userId, 0 /* flags */)); + 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(anyInt(), eq(userId)); + .getInstalledPackages(intThat(arg -> (arg & MATCH_ANY_USER) == 0), eq(userId)); + return userInfo; } private static PackageInfo makePackageInfo(String packageName) { @@ -194,4 +200,42 @@ public final class AppHibernationServiceTest { 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 000000000000..59f3c35f2137 --- /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/locksettings/LockSettingsShellCommandTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java index 220569449ffb..336bbaeae2d9 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java @@ -42,6 +42,7 @@ import android.content.Context; import android.os.Binder; import android.os.Handler; import android.os.Looper; +import android.os.Process; import android.os.ResultReceiver; import android.os.ShellCallback; import android.platform.test.annotations.Presubmit; @@ -86,7 +87,8 @@ public class LockSettingsShellCommandTest { MockitoAnnotations.initMocks(this); final Context context = InstrumentationRegistry.getTargetContext(); mUserId = ActivityManager.getCurrentUser(); - mCommand = new LockSettingsShellCommand(mLockPatternUtils); + mCommand = new LockSettingsShellCommand(mLockPatternUtils, context, 0, + Process.SHELL_UID); when(mLockPatternUtils.hasSecureLockScreen()).thenReturn(true); } 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 df19aeb13707..3ebe4efee013 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -19,11 +19,13 @@ package com.android.server.net; import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS; import static android.Manifest.permission.NETWORK_STACK; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; +import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.INetd.FIREWALL_CHAIN_RESTRICTED; import static android.net.INetd.FIREWALL_RULE_ALLOW; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.net.NetworkPolicy.LIMIT_DISABLED; import static android.net.NetworkPolicy.SNOOZE_NEVER; import static android.net.NetworkPolicy.WARNING_DISABLED; @@ -112,8 +114,6 @@ import android.net.INetworkPolicyListener; import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; -import android.net.NetworkInfo; -import android.net.NetworkInfo.DetailedState; import android.net.NetworkPolicy; import android.net.NetworkPolicyManager; import android.net.NetworkState; @@ -1829,11 +1829,11 @@ public class NetworkPolicyManagerServiceTest { } /** - * 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 @@ public class NetworkPolicyManagerServiceTest { 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)); } } @@ -1986,13 +1985,6 @@ public class NetworkPolicyManagerServiceTest { return users; } - private NetworkInfo buildNetworkInfo() { - final NetworkInfo ni = new NetworkInfo(ConnectivityManager.TYPE_MOBILE, - TelephonyManager.NETWORK_TYPE_LTE, null, null); - ni.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null); - return ni; - } - private LinkProperties buildLinkProperties(String iface) { final LinkProperties lp = new LinkProperties(); lp.setInterfaceName(iface); @@ -2046,13 +2038,12 @@ public class NetworkPolicyManagerServiceTest { } private static NetworkState buildWifi() { - final NetworkInfo info = new NetworkInfo(TYPE_WIFI, 0, null, null); - info.setDetailedState(DetailedState.CONNECTED, null, null); final LinkProperties prop = new LinkProperties(); prop.setInterfaceName(TEST_IFACE); final NetworkCapabilities networkCapabilities = new NetworkCapabilities(); + networkCapabilities.addTransportType(TRANSPORT_WIFI); networkCapabilities.setSSID(TEST_SSID); - return new NetworkState(info, prop, networkCapabilities, null, null, TEST_SSID); + return new NetworkState(TYPE_WIFI, prop, networkCapabilities, null, null, TEST_SSID); } private void expectHasInternetPermission(int uid, boolean hasIt) throws Exception { @@ -2073,7 +2064,7 @@ public class NetworkPolicyManagerServiceTest { when(mCarrierConfigManager.getConfigForSubId(eq(TEST_SUB_ID))) .thenReturn(mCarrierConfig); when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[] { - new NetworkState(buildNetworkInfo(), + new NetworkState(TYPE_MOBILE, buildLinkProperties(TEST_IFACE), buildNetworkCapabilities(TEST_SUB_ID, roaming), new Network(TEST_NET_ID), TEST_IMSI, null) diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java index 22020ad45666..bc84e350a329 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java @@ -96,7 +96,7 @@ public class DexoptOptionsTests { int[] reasons = new int[] { PackageManagerService.REASON_FIRST_BOOT, - PackageManagerService.REASON_BOOT, + PackageManagerService.REASON_POST_BOOT, PackageManagerService.REASON_INSTALL, PackageManagerService.REASON_BACKGROUND_DEXOPT, PackageManagerService.REASON_AB_OTA, diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java new file mode 100644 index 000000000000..70d85b6e0411 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java @@ -0,0 +1,90 @@ +/* + * 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.pm.parsing.library; + +import android.os.Build; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import com.android.server.pm.parsing.pkg.AndroidPackage; +import com.android.server.pm.parsing.pkg.PackageImpl; +import com.android.server.pm.parsing.pkg.ParsedPackage; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Test for {@link AndroidNetIpSecIkeUpdater} + */ +@Presubmit +@SmallTest +@RunWith(JUnit4.class) +public class AndroidNetIpSecIkeUpdaterTest extends PackageSharedLibraryUpdaterTest { + + @Test + public void otherUsesLibraries() { + ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.O) + .addUsesLibrary("other") + .addUsesOptionalLibrary("optional") + .addUsesLibrary("android.net.ipsec.ike") + .hideAsParsed()); + AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.O) + .addUsesLibrary("other") + .addUsesOptionalLibrary("optional") + .hideAsParsed()) + .hideAsFinal(); + checkBackwardsCompatibility(before, after); + } + + @Test + public void in_usesLibraries() { + ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT) + .addUsesLibrary("android.net.ipsec.ike") + .hideAsParsed()); + + AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT) + .hideAsParsed()) + .hideAsFinal(); + + checkBackwardsCompatibility(before, after); + } + + @Test + public void in_usesOptionalLibraries() { + ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT) + .addUsesOptionalLibrary("android.net.ipsec.ike") + .hideAsParsed()); + + AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT) + .hideAsParsed()) + .hideAsFinal(); + + checkBackwardsCompatibility(before, after); + } + + private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) { + checkBackwardsCompatibility(before, after, AndroidNetIpSecIkeUpdater::new); + } +} diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java index 09c8142105cc..9768f176ea85 100644 --- a/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java @@ -165,6 +165,23 @@ public class PackageBackwardCompatibilityTest extends PackageSharedLibraryUpdate checkBackwardsCompatibility(before, ((ParsedPackage) after.hideAsParsed()).hideAsFinal()); } + /** + * Ensures that the {@link PackageBackwardCompatibility} uses a + * {@link AndroidNetIpSecIkeUpdater}. + */ + @Test + public void android_net_ipsec_ike_in_usesLibraries() { + ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT) + .addUsesLibrary("android.net.ipsec.ike") + .hideAsParsed()); + + ParsingPackage after = PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT); + + checkBackwardsCompatibility(before, ((ParsedPackage) after.hideAsParsed()).hideAsFinal()); + } + private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) { checkBackwardsCompatibility(before, after, PackageBackwardCompatibility::getInstance); } diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp index 4439f998a527..4a1a6ada9eeb 100644 --- a/services/tests/uiservicestests/Android.bp +++ b/services/tests/uiservicestests/Android.bp @@ -57,6 +57,6 @@ android_test { "libui", "libunwindstack", "libutils", - "netd_aidl_interface-cpp", + "netd_aidl_interface-V5-cpp", ], } 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 a118e0df1338..bbb25cd20149 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_LOW; 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 3c7206fee9d1..69e4190a02ae 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_FULL_SCRE 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/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp index 7cc233b2439e..cac7b82adc2c 100644 --- a/startop/view_compiler/Android.bp +++ b/startop/view_compiler/Android.bp @@ -84,7 +84,6 @@ cc_test_host { 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 5f7d3f99ae81..791e47105ff9 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/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java index b73ef9b794e4..be5fae488d5e 100644 --- a/telecomm/java/android/telecom/ConnectionRequest.java +++ b/telecomm/java/android/telecom/ConnectionRequest.java @@ -18,6 +18,7 @@ package android.telecom; 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 @@ public final class ConnectionRequest implements Parcelable { * 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/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index a42e3642c0d0..b81c4f2c71c8 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -3937,6 +3937,20 @@ public class CarrierConfigManager { 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 @@ public class CarrierConfigManager { 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; } } diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java index 8507d8512a5c..706e3cb93a0f 100644 --- a/telephony/java/android/telephony/NetworkRegistrationInfo.java +++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java @@ -344,6 +344,7 @@ public final class NetworkRegistrationInfo implements Parcelable { // 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/SignalThresholdInfo.java b/telephony/java/android/telephony/SignalThresholdInfo.java index 0059ad6c2426..ae7d20929d58 100644 --- a/telephony/java/android/telephony/SignalThresholdInfo.java +++ b/telephony/java/android/telephony/SignalThresholdInfo.java @@ -402,29 +402,27 @@ public final class SignalThresholdInfo implements Parcelable { * @see #getThresholds() for more details on signal strength thresholds */ public @NonNull Builder setThresholds(@NonNull int[] thresholds) { - Objects.requireNonNull(thresholds, "thresholds must not be null"); - if (thresholds.length < MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED - || thresholds.length > MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED) { - throw new IllegalArgumentException( - "thresholds length must between " + MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED - + " and " + MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED); - } - mThresholds = thresholds.clone(); - Arrays.sort(mThresholds); - return this; + return setThresholds(thresholds, false /*isSystem*/); } /** - * Set the signal strength thresholds for the corresponding signal measurement type without - * the length limitation. + * Set the signal strength thresholds for the corresponding signal measurement type. * * @param thresholds array of integer as the signal threshold values + * @param isSystem true is the caller is system which does not have restrictions on + * the length of thresholds array. * @return the builder to facilitate the chaining * * @hide */ - public @NonNull Builder setThresholdsUnlimited(@NonNull int[] thresholds) { + public @NonNull Builder setThresholds(@NonNull int[] thresholds, boolean isSystem) { Objects.requireNonNull(thresholds, "thresholds must not be null"); + if (!isSystem && (thresholds.length < MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED + || thresholds.length > MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED)) { + throw new IllegalArgumentException( + "thresholds length must between " + MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED + + " and " + MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED); + } mThresholds = thresholds.clone(); Arrays.sort(mThresholds); return this; diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 904232b54b8f..4e481b3ad837 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -2589,14 +2589,45 @@ public class SubscriptionManager { * 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 @@ public class SubscriptionManager { * 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/data/ApnThrottleStatus.java b/telephony/java/android/telephony/data/ApnThrottleStatus.java index 51461d17690a..eec140869466 100644 --- a/telephony/java/android/telephony/data/ApnThrottleStatus.java +++ b/telephony/java/android/telephony/data/ApnThrottleStatus.java @@ -261,6 +261,10 @@ public final class ApnThrottleStatus implements Parcelable { private long mThrottleExpiryTimeMillis; private @RetryType int mRetryType; private @ThrottleType int mThrottleType; + + /** + * @hide + */ public static final long NO_THROTTLE_EXPIRY_TIME = DataCallResponse.RETRY_DURATION_UNDEFINED; diff --git a/telephony/java/android/telephony/ims/DelegateStateCallback.java b/telephony/java/android/telephony/ims/DelegateStateCallback.java index fb659490d546..6bf992e64480 100644 --- a/telephony/java/android/telephony/ims/DelegateStateCallback.java +++ b/telephony/java/android/telephony/ims/DelegateStateCallback.java @@ -18,6 +18,7 @@ package android.telephony.ims; 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 @@ public interface DelegateStateCallback { * 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/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java index bd623e055743..5f4e1e6f3148 100644 --- a/telephony/java/android/telephony/ims/ImsRcsManager.java +++ b/telephony/java/android/telephony/ims/ImsRcsManager.java @@ -21,6 +21,7 @@ import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; +import android.annotation.SystemApi; import android.content.Context; import android.content.Intent; import android.os.Binder; @@ -39,6 +40,8 @@ import android.util.Log; import com.android.internal.telephony.IIntegerConsumer; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -77,31 +80,49 @@ public class ImsRcsManager { "android.telephony.ims.action.SHOW_CAPABILITY_DISCOVERY_OPT_IN"; /** - * Receives RCS Feature availability status updates from the ImsService. - * - * @see #isAvailable(int) - * @see #registerRcsAvailabilityCallback(Executor, AvailabilityCallback) - * @see #unregisterRcsAvailabilityCallback(AvailabilityCallback) + * An application can use {@link #addOnAvailabilityChangedListener} to register a + * {@link OnAvailabilityChangedListener}, which will notify the user when the RCS feature + * availability status updates from the ImsService. * @hide */ - public static class AvailabilityCallback { + @SystemApi + public interface OnAvailabilityChangedListener { + /** + * The availability of the feature's capabilities has changed to either available or + * unavailable. + * <p> + * If unavailable, the feature does not support the capability at the current time. This may + * be due to network or subscription provisioning changes, such as the IMS registration + * being lost, network type changing, or OMA-DM provisioning updates. + * + * @param capabilities The new availability of the capabilities. + */ + void onAvailabilityChanged(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities); + } - private static class CapabilityBinder extends IImsCapabilityCallback.Stub { + /** + * Receive the availability status changed from the ImsService and pass the status change to + * the associated {@link OnAvailabilityChangedListener} + */ + private static class AvailabilityCallbackAdapter { - private final AvailabilityCallback mLocalCallback; - private Executor mExecutor; + private static class CapabilityBinder extends IImsCapabilityCallback.Stub { + private final OnAvailabilityChangedListener mOnAvailabilityChangedListener; + private final Executor mExecutor; - CapabilityBinder(AvailabilityCallback c) { - mLocalCallback = c; + CapabilityBinder(OnAvailabilityChangedListener listener, Executor executor) { + mExecutor = executor; + mOnAvailabilityChangedListener = listener; } @Override public void onCapabilitiesStatusChanged(int config) { - if (mLocalCallback == null) return; + if (mOnAvailabilityChangedListener == null) return; long callingIdentity = Binder.clearCallingIdentity(); try { - mExecutor.execute(() -> mLocalCallback.onAvailabilityChanged(config)); + mExecutor.execute(() -> + mOnAvailabilityChangedListener.onAvailabilityChanged(config)); } finally { restoreCallingIdentity(callingIdentity); } @@ -110,48 +131,34 @@ public class ImsRcsManager { @Override public void onQueryCapabilityConfiguration(int capability, int radioTech, boolean isEnabled) { - // This is not used for public interfaces. + // This is not used. } @Override public void onChangeCapabilityConfigurationError(int capability, int radioTech, @ImsFeature.ImsCapabilityError int reason) { - // This is not used for public interfaces - } - - private void setExecutor(Executor executor) { - mExecutor = executor; + // This is not used. } } - private final CapabilityBinder mBinder = new CapabilityBinder(this); + private final CapabilityBinder mBinder; - /** - * The availability of the feature's capabilities has changed to either available or - * unavailable. - * <p> - * If unavailable, the feature does not support the capability at the current time. This may - * be due to network or subscription provisioning changes, such as the IMS registration - * being lost, network type changing, or OMA-DM provisioning updates. - * - * @param capabilities The new availability of the capabilities. - */ - public void onAvailabilityChanged(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) { + AvailabilityCallbackAdapter(@NonNull Executor executor, + @NonNull OnAvailabilityChangedListener listener) { + mBinder = new CapabilityBinder(listener, executor); } /**@hide*/ public final IImsCapabilityCallback getBinder() { return mBinder; } - - private void setExecutor(Executor executor) { - mBinder.setExecutor(executor); - } } private final int mSubId; private final Context mContext; private final BinderCacheManager<IImsRcsController> mBinderCache; + private final Map<OnAvailabilityChangedListener, AvailabilityCallbackAdapter> + mAvailabilityChangedCallbacks; /** * Use {@link ImsManager#getImsRcsManager(int)} to create an instance of this class. @@ -162,6 +169,7 @@ public class ImsRcsManager { mSubId = subId; mContext = context; mBinderCache = binderCache; + mAvailabilityChangedCallbacks = new HashMap<>(); } /** @@ -174,10 +182,23 @@ public class ImsRcsManager { } /** - * @hide + * Registers a {@link RegistrationManager.RegistrationCallback} with the system. When the + * callback is registered, it will initiate the callback c to be called with the current + * registration state. + * + * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE + * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges + * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). + * + * @param executor The executor the callback events should be run on. + * @param c The {@link RegistrationManager.RegistrationCallback} to be added. + * @see #unregisterImsRegistrationCallback(RegistrationManager.RegistrationCallback) + * @throws ImsException if the subscription associated with this callback is valid, but + * the {@link ImsService} associated with the subscription is not available. This can happen if + * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed + * reason. */ - // @Override add back to RegistrationManager interface once public. - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) public void registerImsRegistrationCallback( @NonNull @CallbackExecutor Executor executor, @NonNull RegistrationManager.RegistrationCallback c) @@ -191,7 +212,7 @@ public class ImsRcsManager { IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { - Log.e(TAG, "Register registration callback: IImsRcsController is null"); + Log.w(TAG, "Register registration callback: IImsRcsController is null"); throw new ImsException("Cannot find remote IMS service", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } @@ -207,10 +228,21 @@ public class ImsRcsManager { } /** - * @hide + * Removes an existing {@link RegistrationManager.RegistrationCallback}. + * + * When the subscription associated with this callback is removed (SIM removed, ESIM swap, + * etc...), this callback will automatically be removed. If this method is called for an + * inactive subscription, it will result in a no-op. + * + * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE + * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges + * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). + * + * @param c The {@link RegistrationManager.RegistrationCallback} to be removed. + * @see android.telephony.SubscriptionManager.OnSubscriptionsChangedListener + * @see #registerImsRegistrationCallback(Executor, RegistrationCallback) */ - // @Override add back to RegistrationManager interface once public. - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) public void unregisterImsRegistrationCallback( @NonNull RegistrationManager.RegistrationCallback c) { if (c == null) { @@ -219,7 +251,7 @@ public class ImsRcsManager { IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { - Log.e(TAG, "Unregister registration callback: IImsRcsController is null"); + Log.w(TAG, "Unregister registration callback: IImsRcsController is null"); throw new IllegalStateException("Cannot find remote IMS service"); } @@ -231,10 +263,21 @@ public class ImsRcsManager { } /** - * @hide + * Gets the registration state of the IMS service. + * + * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE + * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges + * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). + * + * @param executor The {@link Executor} that will be used to call the IMS registration state + * callback. + * @param stateCallback A callback called on the supplied {@link Executor} that will contain the + * registration state of the IMS service, which will be one of the + * following: {@link RegistrationManager#REGISTRATION_STATE_NOT_REGISTERED}, + * {@link RegistrationManager#REGISTRATION_STATE_REGISTERING}, or + * {@link RegistrationManager#REGISTRATION_STATE_REGISTERED}. */ - // @Override add back to RegistrationManager interface once public. - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) public void getRegistrationState(@NonNull @CallbackExecutor Executor executor, @NonNull @RegistrationManager.ImsRegistrationState Consumer<Integer> stateCallback) { if (stateCallback == null) { @@ -246,7 +289,7 @@ public class ImsRcsManager { IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { - Log.e(TAG, "Get registration state error: IImsRcsController is null"); + Log.w(TAG, "Get registration state error: IImsRcsController is null"); throw new IllegalStateException("Cannot find remote IMS service"); } @@ -263,9 +306,20 @@ public class ImsRcsManager { } /** - * @hide + * Gets the Transport Type associated with the current IMS registration. + * + * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE + * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges + * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). + * + * @param executor The {@link Executor} that will be used to call the transportTypeCallback. + * @param transportTypeCallback The transport type associated with the current IMS registration, + * which will be one of following: + * {@see AccessNetworkConstants#TRANSPORT_TYPE_WWAN}, + * {@see AccessNetworkConstants#TRANSPORT_TYPE_WLAN}, or + * {@see AccessNetworkConstants#TRANSPORT_TYPE_INVALID}. */ - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) public void getRegistrationTransportType(@NonNull @CallbackExecutor Executor executor, @NonNull @AccessNetworkConstants.TransportType Consumer<Integer> transportTypeCallback) { @@ -278,7 +332,7 @@ public class ImsRcsManager { IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { - Log.e(TAG, "Get registration transport type error: IImsRcsController is null"); + Log.w(TAG, "Get registration transport type error: IImsRcsController is null"); throw new IllegalStateException("Cannot find remote IMS service"); } @@ -296,31 +350,33 @@ public class ImsRcsManager { } /** - * Registers an {@link AvailabilityCallback} with the system, which will provide RCS + * Add an {@link OnAvailabilityChangedListener} with the system, which will provide RCS * availability updates for the subscription specified. * * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to * subscription changed events and call - * {@link #unregisterRcsAvailabilityCallback(AvailabilityCallback)} to clean up after a - * subscription is removed. + * {@link #removeOnAvailabilityChangedListener(OnAvailabilityChangedListener)} to clean up + * after a subscription is removed. * <p> - * When the callback is registered, it will initiate the callback c to be called with the - * current capabilities. + * When the listener is registered, it will initiate the callback listener to be called with + * the current capabilities. * * @param executor The executor the callback events should be run on. - * @param c The RCS {@link AvailabilityCallback} to be registered. - * @see #unregisterRcsAvailabilityCallback(AvailabilityCallback) + * @param listener The RCS {@link OnAvailabilityChangedListener} to be registered. + * @see #removeOnAvailabilityChangedListener(OnAvailabilityChangedListener) * @throws ImsException if the subscription associated with this instance of * {@link ImsRcsManager} is valid, but the ImsService associated with the subscription is not * available. This can happen if the ImsService has crashed, for example, or if the subscription * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public void registerRcsAvailabilityCallback(@NonNull @CallbackExecutor Executor executor, - @NonNull AvailabilityCallback c) throws ImsException { - if (c == null) { - throw new IllegalArgumentException("Must include a non-null AvailabilityCallback."); + public void addOnAvailabilityChangedListener(@NonNull @CallbackExecutor Executor executor, + @NonNull OnAvailabilityChangedListener listener) throws ImsException { + if (listener == null) { + throw new IllegalArgumentException("Must include a non-null" + + "OnAvailabilityChangedListener."); } if (executor == null) { throw new IllegalArgumentException("Must include a non-null Executor."); @@ -328,56 +384,61 @@ public class ImsRcsManager { IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { - Log.e(TAG, "Register availability callback: IImsRcsController is null"); + Log.w(TAG, "Add availability changed listener: IImsRcsController is null"); throw new ImsException("Cannot find remote IMS service", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } - c.setExecutor(executor); + AvailabilityCallbackAdapter adapter = + addAvailabilityChangedListenerToCollection(executor, listener); try { - imsRcsController.registerRcsAvailabilityCallback(mSubId, c.getBinder()); - + imsRcsController.registerRcsAvailabilityCallback(mSubId, adapter.getBinder()); } catch (ServiceSpecificException e) { throw new ImsException(e.toString(), e.errorCode); } catch (RemoteException e) { - Log.e(TAG, "Error calling IImsRcsController#registerRcsAvailabilityCallback", e); + Log.w(TAG, "Error calling IImsRcsController#registerRcsAvailabilityCallback", e); throw new ImsException("Remote IMS Service is not available", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } } - /** - * Removes an existing RCS {@link AvailabilityCallback}. + /** + * Removes an existing RCS {@link OnAvailabilityChangedListener}. * <p> * When the subscription associated with this callback is removed (SIM removed, ESIM swap, * etc...), this callback will automatically be unregistered. If this method is called for an * inactive subscription, it will result in a no-op. - * @param c The RCS {@link AvailabilityCallback} to be removed. - * @see #registerRcsAvailabilityCallback(Executor, AvailabilityCallback) + * @param listener The RCS {@link OnAvailabilityChangedListener} to be removed. + * @see #addOnAvailabilityChangedListener(Executor, OnAvailabilityChangedListener) * @throws ImsException if the IMS service is not available when calling this method. * See {@link ImsException#getCode()} for more information on the error codes. * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public void unregisterRcsAvailabilityCallback(@NonNull AvailabilityCallback c) - throws ImsException { - if (c == null) { - throw new IllegalArgumentException("Must include a non-null AvailabilityCallback."); + public void removeOnAvailabilityChangedListener( + @NonNull OnAvailabilityChangedListener listener) { + if (listener == null) { + throw new IllegalArgumentException("Must include a non-null" + + "OnAvailabilityChangedListener."); } IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { - Log.e(TAG, "Unregister availability callback: IImsRcsController is null"); - throw new ImsException("Cannot find remote IMS service", - ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + Log.w(TAG, "Remove availability changed listener: IImsRcsController is null"); + return; + } + + AvailabilityCallbackAdapter callback = + removeAvailabilityChangedListenerFromCollection(listener); + if (callback == null) { + return; } try { - imsRcsController.unregisterRcsAvailabilityCallback(mSubId, c.getBinder()); + imsRcsController.unregisterRcsAvailabilityCallback(mSubId, callback.getBinder()); } catch (RemoteException e) { - Log.e(TAG, "Error calling IImsRcsController#unregisterRcsAvailabilityCallback", e); - throw new ImsException("Remote IMS Service is not available", - ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + Log.w(TAG, "Error calling IImsRcsController#unregisterRcsAvailabilityCallback", e); } } @@ -388,25 +449,24 @@ public class ImsRcsManager { * RCS capabilities provided over-the-top by applications. * * @param capability The RCS capability to query. - * @param radioTech The radio tech that this capability failed for, defined as - * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or - * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}. + * @param radioTech The radio technology type that we are querying. * @return true if the RCS capability is capable for this subscription, false otherwise. This * does not necessarily mean that we are registered for IMS and the capability is available, but * rather the subscription is capable of this service over IMS. - * @see #isAvailable(int) + * @see #isAvailable(int, int) * @see android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL * @see android.telephony.CarrierConfigManager.Ims#KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL * @throws ImsException if the IMS service is not available when calling this method. * See {@link ImsException#getCode()} for more information on the error codes. * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isCapable(@RcsUceAdapter.RcsImsCapabilityFlag int capability, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) throws ImsException { IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { - Log.e(TAG, "isCapable: IImsRcsController is null"); + Log.w(TAG, "isCapable: IImsRcsController is null"); throw new ImsException("Cannot find remote IMS service", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } @@ -414,7 +474,7 @@ public class ImsRcsManager { try { return imsRcsController.isCapable(mSubId, capability, radioTech); } catch (RemoteException e) { - Log.e(TAG, "Error calling IImsRcsController#isCapable", e); + Log.w(TAG, "Error calling IImsRcsController#isCapable", e); throw new ImsException("Remote IMS Service is not available", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } @@ -427,6 +487,7 @@ public class ImsRcsManager { * RCS capabilities provided by over-the-top by applications. * * @param capability the RCS capability to query. + * @param radioTech The radio technology type that we are querying. * @return true if the RCS capability is currently available for the associated subscription, * false otherwise. If the capability is available, IMS is registered and the service is * currently available over IMS. @@ -435,25 +496,57 @@ public class ImsRcsManager { * See {@link ImsException#getCode()} for more information on the error codes. * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public boolean isAvailable(@RcsUceAdapter.RcsImsCapabilityFlag int capability) + public boolean isAvailable(@RcsUceAdapter.RcsImsCapabilityFlag int capability, + @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) throws ImsException { IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { - Log.e(TAG, "isAvailable: IImsRcsController is null"); + Log.w(TAG, "isAvailable: IImsRcsController is null"); throw new ImsException("Cannot find remote IMS service", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } try { - return imsRcsController.isAvailable(mSubId, capability); + return imsRcsController.isAvailable(mSubId, capability, radioTech); } catch (RemoteException e) { - Log.e(TAG, "Error calling IImsRcsController#isAvailable", e); + Log.w(TAG, "Error calling IImsRcsController#isAvailable", e); throw new ImsException("Remote IMS Service is not available", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } } + /** + * Add the {@link OnAvailabilityChangedListener} to collection for tracking. + * @param executor The executor that will be used when the publish state is changed and the + * {@link OnAvailabilityChangedListener} is called. + * @param listener The {@link OnAvailabilityChangedListener} to call the publish state changed. + * @return The {@link AvailabilityCallbackAdapter} to wrapper the + * {@link OnAvailabilityChangedListener} + */ + private AvailabilityCallbackAdapter addAvailabilityChangedListenerToCollection( + @NonNull Executor executor, @NonNull OnAvailabilityChangedListener listener) { + AvailabilityCallbackAdapter adapter = new AvailabilityCallbackAdapter(executor, listener); + synchronized (mAvailabilityChangedCallbacks) { + mAvailabilityChangedCallbacks.put(listener, adapter); + } + return adapter; + } + + /** + * Remove the existing {@link OnAvailabilityChangedListener} from the collection. + * @param listener The {@link OnAvailabilityChangedListener} to remove from the collection. + * @return The wrapper class {@link AvailabilityCallbackAdapter} associated with the + * {@link OnAvailabilityChangedListener}. + */ + private AvailabilityCallbackAdapter removeAvailabilityChangedListenerFromCollection( + @NonNull OnAvailabilityChangedListener listener) { + synchronized (mAvailabilityChangedCallbacks) { + return mAvailabilityChangedCallbacks.remove(listener); + } + } + private IImsRcsController getIImsRcsController() { IBinder binder = TelephonyFrameworkInitializer .getTelephonyServiceManager() diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java index f3c38bcba98a..08eec29d5ac2 100644 --- a/telephony/java/android/telephony/ims/ProvisioningManager.java +++ b/telephony/java/android/telephony/ims/ProvisioningManager.java @@ -25,6 +25,7 @@ import android.annotation.SdkConstant; 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { } /** - * 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 @@ public class ProvisioningManager { * 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 @@ public class ProvisioningManager { * 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/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java index 2e9eb94605a5..04421c9a2449 100644 --- a/telephony/java/android/telephony/ims/SipDelegateManager.java +++ b/telephony/java/android/telephony/ims/SipDelegateManager.java @@ -24,6 +24,7 @@ import android.annotation.Nullable; 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 @@ import java.util.concurrent.Executor; * 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 @@ public class SipDelegateManager { * {@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 7a6c28bddd09..8931a78709ed 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl @@ -47,7 +47,7 @@ interface IImsRcsController { void registerRcsAvailabilityCallback(int subId, IImsCapabilityCallback c); void unregisterRcsAvailabilityCallback(int subId, IImsCapabilityCallback c); boolean isCapable(int subId, int capability, int radioTech); - boolean isAvailable(int subId, int capability); + boolean isAvailable(int subId, int capability, int radioTech); // ImsUceAdapter specific void requestCapabilities(int subId, String callingPackage, String callingFeatureId, diff --git a/telephony/java/android/telephony/ims/aidl/IPublishResponseCallback.aidl b/telephony/java/android/telephony/ims/aidl/IPublishResponseCallback.aidl index 481e7f8b37b9..b99d8a7d6d38 100644 --- a/telephony/java/android/telephony/ims/aidl/IPublishResponseCallback.aidl +++ b/telephony/java/android/telephony/ims/aidl/IPublishResponseCallback.aidl @@ -26,4 +26,5 @@ import java.util.List; 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 a14199365b07..8cc8020df29a 100644 --- a/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl +++ b/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl @@ -30,6 +30,7 @@ import java.util.Map; 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 22985d0cf85c..65415ea441b5 100644 --- a/telephony/java/android/telephony/ims/aidl/RcsPublishResponseAidlWrapper.java +++ b/telephony/java/android/telephony/ims/aidl/RcsPublishResponseAidlWrapper.java @@ -34,10 +34,11 @@ public class RcsPublishResponseAidlWrapper implements PublishResponseCallback { } @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 @@ public class RcsPublishResponseAidlWrapper implements PublishResponseCallback { 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 1fb339c0cf89..11118c0617c2 100644 --- a/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java +++ b/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java @@ -40,10 +40,11 @@ public class RcsSubscribeResponseAidlWrapper implements SubscribeResponseCallbac } @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 @@ public class RcsSubscribeResponseAidlWrapper implements SubscribeResponseCallbac 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 @@ public class RcsSubscribeResponseAidlWrapper implements SubscribeResponseCallbac try { mResponseBinder.onNotifyCapabilitiesUpdate(pidfXmls); } catch (RemoteException e) { + throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } } @@ -69,6 +83,7 @@ public class RcsSubscribeResponseAidlWrapper implements SubscribeResponseCallbac try { mResponseBinder.onResourceTerminated(getTerminatedReasonList(uriTerminatedReason)); } catch (RemoteException e) { + throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } } @@ -90,6 +105,7 @@ public class RcsSubscribeResponseAidlWrapper implements SubscribeResponseCallbac 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/feature/CapabilityChangeRequest.java b/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java index 87a6873d00b2..47c56e13f829 100644 --- a/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java +++ b/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java @@ -36,12 +36,9 @@ import java.util.Set; public final class CapabilityChangeRequest implements Parcelable { /** - * Contains a feature capability, defined as - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE}, - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO}, - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, or - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS}, - * along with an associated technology, defined as + * Contains a MMTEL feature capability {@link MmTelFeature.MmTelCapabilities} and RCS feature + * capability {@link RcsFeature.RcsImsCapabilities}, along with an associated technology, + * defined as * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} */ @@ -49,7 +46,7 @@ public final class CapabilityChangeRequest implements Parcelable { private final int mCapability; private final int radioTech; - public CapabilityPair(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability, + public CapabilityPair(int capability, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) { this.mCapability = capability; this.radioTech = radioTech; @@ -80,13 +77,10 @@ public final class CapabilityChangeRequest implements Parcelable { } /** - * @return The stored capability, defined as - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE}, - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO}, - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, or - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS} + * @return The stored capability, defined as {@link MmTelFeature.MmTelCapabilities} and + * {@link RcsFeature.RcsImsCapabilities} */ - public @MmTelFeature.MmTelCapabilities.MmTelCapability int getCapability() { + public int getCapability() { return mCapability; } @@ -123,12 +117,11 @@ public final class CapabilityChangeRequest implements Parcelable { * Add one or many capabilities to the request to be enabled. * * @param capabilities A bitfield of capabilities to enable, valid values are defined in - * {@link MmTelFeature.MmTelCapabilities.MmTelCapability}. + * {@link MmTelFeature.MmTelCapabilities} and {@link RcsFeature.RcsImsCapabilities}. * @param radioTech the radio tech that these capabilities should be enabled for, valid * values are in {@link ImsRegistrationImplBase.ImsRegistrationTech}. */ - public void addCapabilitiesToEnableForTech( - @MmTelFeature.MmTelCapabilities.MmTelCapability int capabilities, + public void addCapabilitiesToEnableForTech(int capabilities, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) { addAllCapabilities(mCapabilitiesToEnable, capabilities, radioTech); } @@ -136,12 +129,11 @@ public final class CapabilityChangeRequest implements Parcelable { /** * Add one or many capabilities to the request to be disabled. * @param capabilities A bitfield of capabilities to diable, valid values are defined in - * {@link MmTelFeature.MmTelCapabilities.MmTelCapability}. + * {@link MmTelFeature.MmTelCapabilities} and {@link RcsFeature.RcsImsCapabilities}. * @param radioTech the radio tech that these capabilities should be disabled for, valid * values are in {@link ImsRegistrationImplBase.ImsRegistrationTech}. */ - public void addCapabilitiesToDisableForTech( - @MmTelFeature.MmTelCapabilities.MmTelCapability int capabilities, + public void addCapabilitiesToDisableForTech(int capabilities, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) { addAllCapabilities(mCapabilitiesToDisable, capabilities, radioTech); } diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java index 22df921c4214..85703f8de5e5 100644 --- a/telephony/java/android/telephony/ims/feature/RcsFeature.java +++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java @@ -194,7 +194,6 @@ public class RcsFeature extends ImsFeature { * of the capability and notify the capability status as true using * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This will signal to the * framework that the capability is available for usage. - * @hide */ public static class RcsImsCapabilities extends Capabilities { /** @hide*/ @@ -226,12 +225,21 @@ public class RcsFeature extends ImsFeature { */ public static final int CAPABILITY_TYPE_PRESENCE_UCE = 1 << 1; + /** + * Create a new {@link RcsImsCapabilities} instance with the provided capabilities. + * @param capabilities The capabilities that are supported for RCS in the form of a + * bitfield. + */ public RcsImsCapabilities(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) { super(capabilities); } - private RcsImsCapabilities(Capabilities c) { - super(c.getMask()); + /** + * Create a new {@link RcsImsCapabilities} instance with the provided capabilities. + * @param capabilities The capabilities instance that are supported for RCS + */ + private RcsImsCapabilities(Capabilities capabilities) { + super(capabilities.getMask()); } @Override @@ -307,7 +315,7 @@ public class RcsFeature extends ImsFeature { * set, the {@link RcsFeature} has brought up the capability and is ready for framework * requests. To change the status of the capabilities * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)} should be called. - * @hide + * @return A copy of the current RcsFeature capability status. */ @Override public @NonNull final RcsImsCapabilities queryCapabilityStatus() { @@ -318,13 +326,13 @@ public class RcsFeature extends ImsFeature { * Notify the framework that the capabilities status has changed. If a capability is enabled, * this signals to the framework that the capability has been initialized and is ready. * Call {@link #queryCapabilityStatus()} to return the current capability status. - * @hide + * @param capabilities The current capability status of the RcsFeature. */ - public final void notifyCapabilitiesStatusChanged(@NonNull RcsImsCapabilities c) { - if (c == null) { + public final void notifyCapabilitiesStatusChanged(@NonNull RcsImsCapabilities capabilities) { + if (capabilities == null) { throw new IllegalArgumentException("RcsImsCapabilities must be non-null!"); } - super.notifyCapabilitiesStatusChanged(c); + super.notifyCapabilitiesStatusChanged(capabilities); } /** @@ -333,7 +341,9 @@ public class RcsFeature extends ImsFeature { * {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)} to * enable or disable capability A, this method should return the correct configuration for * capability A afterwards (until it has changed). - * @hide + * @param capability The capability that we are querying the configuration for. + * @param radioTech The radio technology type that we are querying. + * @return true if the capability is enabled, false otherwise. */ public boolean queryCapabilityConfiguration( @RcsUceAdapter.RcsImsCapabilityFlag int capability, @@ -355,11 +365,12 @@ public class RcsFeature extends ImsFeature { * If for some reason one or more of these capabilities can not be enabled/disabled, * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError(int, int, int)} should * be called for each capability change that resulted in an error. - * @hide + * @param request The request to change the capability. + * @param callback To notify the framework that the result of the capability changes. */ @Override public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request, - @NonNull CapabilityCallbackProxy c) { + @NonNull CapabilityCallbackProxy callback) { // Base Implementation - Override to provide functionality } diff --git a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java index 2e35d27614d1..5f8e93d02a00 100644 --- a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java @@ -44,8 +44,12 @@ public class ImsEcbmImplBase { @Override public void setListener(IImsEcbmListener listener) { synchronized (mLock) { - if (mImsEcbm != null && listener != null && Objects.equals( - mImsEcbm.asBinder(), listener.asBinder())) { + 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) { diff --git a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java index 555a47eb8200..8e961acc7b36 100644 --- a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java @@ -48,6 +48,10 @@ public class ImsMultiEndpointImplBase { @Override public void setListener(IImsExternalCallStateListener listener) throws RemoteException { 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; diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java index eef4fcaceeaf..83b89aa8e814 100644 --- a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java @@ -23,6 +23,7 @@ import android.annotation.SystemApi; 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; @@ -41,6 +42,7 @@ import java.util.Objects; // 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 @@ -207,6 +209,11 @@ public class ImsUtImplBase { @Override public void setListener(IImsUtListener listener) throws RemoteException { 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; diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java index 7eba709a11da..ec98be6e5062 100644 --- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java +++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java @@ -140,6 +140,9 @@ public class RcsCapabilityExchangeImplBase { * Provide the framework with a subsequent network response update to * {@link #publishCapabilities(String, PublishResponseCallback)}. * + * 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 @@ -154,6 +157,31 @@ public class RcsCapabilityExchangeImplBase { */ 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; } /** @@ -222,6 +250,9 @@ public class RcsCapabilityExchangeImplBase { * {@link #onResourceTerminated}, and {@link #onTerminated} as required for the * subsequent NOTIFY responses to the subscription. * + * 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 @@ -236,6 +267,31 @@ public class RcsCapabilityExchangeImplBase { @NonNull String reason) throws ImsException; /** + * 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)}. diff --git a/tests/ApkVerityTest/OWNERS b/tests/ApkVerityTest/OWNERS new file mode 100644 index 000000000000..d67285ede44a --- /dev/null +++ b/tests/ApkVerityTest/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 36824 + +victorhsieh@google.com 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 02597d548361..e67354982b05 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 @@ public class StagedInstallInternalTest { 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 000000000000..34ac813f02e0 --- /dev/null +++ b/tests/UpdatableSystemFontTest/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 24939 + +include /graphics/java/android/graphics/fonts/OWNERS diff --git a/tests/net/Android.bp b/tests/net/Android.bp index e630da04a512..ffde68eab578 100644 --- a/tests/net/Android.bp +++ b/tests/net/Android.bp @@ -36,7 +36,7 @@ java_defaults { "libvndksupport", "libziparchive", "libz", - "netd_aidl_interface-cpp", + "netd_aidl_interface-V5-cpp", ], } diff --git a/tests/net/common/java/android/net/CaptivePortalDataTest.kt b/tests/net/common/java/android/net/CaptivePortalDataTest.kt index b2bcfeb9019d..ad5bbf220d57 100644 --- a/tests/net/common/java/android/net/CaptivePortalDataTest.kt +++ b/tests/net/common/java/android/net/CaptivePortalDataTest.kt @@ -54,12 +54,26 @@ class CaptivePortalDataTest { } .build() + private val dataFromPasspoint = CaptivePortalData.Builder() + .setUserPortalUrl(Uri.parse("https://tc.example.com/passpoint"), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) + .setVenueInfoUrl(Uri.parse("https://venue.example.com/passpoint"), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) + .setCaptive(true) + .apply { + if (SdkLevel.isAtLeastS()) { + setVenueFriendlyName("venue friendly name") + } + } + .build() + private fun makeBuilder() = CaptivePortalData.Builder(data) @Test fun testParcelUnparcel() { - val fieldCount = if (SdkLevel.isAtLeastS()) 8 else 7 + val fieldCount = if (SdkLevel.isAtLeastS()) 10 else 7 assertParcelSane(data, fieldCount) + assertParcelSane(dataFromPasspoint, fieldCount) assertParcelingIsLossless(makeBuilder().setUserPortalUrl(null).build()) assertParcelingIsLossless(makeBuilder().setVenueInfoUrl(null).build()) @@ -83,6 +97,27 @@ class CaptivePortalDataTest { assertNotEqualsAfterChange { it.setVenueFriendlyName("another friendly name") } assertNotEqualsAfterChange { it.setVenueFriendlyName(null) } } + + assertEquals(dataFromPasspoint, CaptivePortalData.Builder(dataFromPasspoint).build()) + assertNotEqualsAfterChange { it.setUserPortalUrl( + Uri.parse("https://tc.example.com/passpoint")) } + assertNotEqualsAfterChange { it.setUserPortalUrl( + Uri.parse("https://tc.example.com/passpoint"), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) } + assertNotEqualsAfterChange { it.setUserPortalUrl( + Uri.parse("https://tc.example.com/other"), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) } + assertNotEqualsAfterChange { it.setUserPortalUrl( + Uri.parse("https://tc.example.com/passpoint"), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) } + assertNotEqualsAfterChange { it.setVenueInfoUrl( + Uri.parse("https://venue.example.com/passpoint")) } + assertNotEqualsAfterChange { it.setVenueInfoUrl( + Uri.parse("https://venue.example.com/other"), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) } + assertNotEqualsAfterChange { it.setVenueInfoUrl( + Uri.parse("https://venue.example.com/passpoint"), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) } } @Test @@ -130,6 +165,22 @@ class CaptivePortalDataTest { assertEquals("venue friendly name", data.venueFriendlyName) } + @Test @IgnoreUpTo(Build.VERSION_CODES.R) + fun testGetVenueInfoUrlSource() { + assertEquals(CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER, + data.venueInfoUrlSource) + assertEquals(CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT, + dataFromPasspoint.venueInfoUrlSource) + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.R) + fun testGetUserPortalUrlSource() { + assertEquals(CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER, + data.userPortalUrlSource) + assertEquals(CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT, + dataFromPasspoint.userPortalUrlSource) + } + private fun CaptivePortalData.mutate(mutator: (CaptivePortalData.Builder) -> Unit) = CaptivePortalData.Builder(this).apply { mutator(this) }.build() diff --git a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java b/tests/net/common/java/android/net/OemNetworkPreferencesTest.java index cade5ba3771f..d232a507454d 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.MiscAsserts.assertThrows; 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 @@ public class OemNetworkPreferencesTest { 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 testBuilderAddNetworkPreferenceRequiresNonNullPackageName() { + assertThrows(NullPointerException.class, + () -> mBuilder.addNetworkPreference(null, TEST_PREF)); } @Test - public void builderAddNetworkPreferenceRequiresNonNullPackages() { + public void testBuilderRemoveNetworkPreferenceRequiresNonNullPackageName() { assertThrows(NullPointerException.class, - () -> mBuilder.addNetworkPreference(TEST_PREF, null)); + () -> mBuilder.removeNetworkPreference(null)); } @Test - public void getNetworkPreferencesReturnsCorrectValue() { + public void testGetNetworkPreferenceReturnsCorrectValue() { final int expectedNumberOfMappings = 1; - mBuilder.addNetworkPreference(TEST_PREF, mPackages); + mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF); - final SparseArray<List<String>> networkPreferences = + 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 @@ public class OemNetworkPreferencesTest { @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/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java index dc9e587332cb..e1da3d0ae2b3 100644 --- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java +++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java @@ -17,6 +17,7 @@ package com.android.server; 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.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; @@ -84,6 +85,7 @@ public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork { final String typeName = ConnectivityManager.getNetworkTypeName(type); mNetworkCapabilities = (ncTemplate != null) ? ncTemplate : new NetworkCapabilities(); mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_SUSPENDED); + mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); mNetworkCapabilities.addTransportType(transport); switch (transport) { case TRANSPORT_ETHERNET: diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java index c2fddf3d9e82..6a09b0237a38 100644 --- a/tests/net/java/android/net/ConnectivityManagerTest.java +++ b/tests/net/java/android/net/ConnectivityManagerTest.java @@ -35,6 +35,7 @@ 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; +import static android.net.NetworkRequest.Type.TRACK_SYSTEM_DEFAULT; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -329,6 +330,9 @@ public class ConnectivityManagerTest { mustFail(() -> { manager.registerDefaultNetworkCallback(null, handler); }); mustFail(() -> { manager.registerDefaultNetworkCallback(callback, null); }); + mustFail(() -> { manager.registerSystemDefaultNetworkCallback(null, handler); }); + mustFail(() -> { manager.registerSystemDefaultNetworkCallback(callback, null); }); + mustFail(() -> { manager.unregisterNetworkCallback(nullCallback); }); mustFail(() -> { manager.unregisterNetworkCallback(nullIntent); }); mustFail(() -> { manager.releaseNetworkRequest(nullIntent); }); @@ -345,15 +349,17 @@ public class ConnectivityManagerTest { @Test public void testRequestType() throws Exception { final String testPkgName = "MyPackage"; + final String testAttributionTag = "MyTag"; final ConnectivityManager manager = new ConnectivityManager(mCtx, mService); when(mCtx.getOpPackageName()).thenReturn(testPkgName); + when(mCtx.getAttributionTag()).thenReturn(testAttributionTag); final NetworkRequest request = makeRequest(1); final NetworkCallback callback = new ConnectivityManager.NetworkCallback(); manager.requestNetwork(request, callback); verify(mService).requestNetwork(eq(request.networkCapabilities), eq(REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), - eq(testPkgName), eq(null)); + eq(testPkgName), eq(testAttributionTag)); reset(mService); // Verify that register network callback does not calls requestNetwork at all. @@ -361,19 +367,26 @@ public class ConnectivityManagerTest { verify(mService, never()).requestNetwork(any(), anyInt(), any(), anyInt(), any(), anyInt(), any(), any()); verify(mService).listenForNetwork(eq(request.networkCapabilities), any(), any(), - eq(testPkgName)); + eq(testPkgName), eq(testAttributionTag)); reset(mService); manager.registerDefaultNetworkCallback(callback); verify(mService).requestNetwork(eq(null), eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), - eq(testPkgName), eq(null)); + eq(testPkgName), eq(testAttributionTag)); 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)); + eq(testPkgName), eq(testAttributionTag)); + reset(mService); + + Handler handler = new Handler(ConnectivityThread.getInstanceLooper()); + manager.registerSystemDefaultNetworkCallback(callback, handler); + verify(mService).requestNetwork(eq(null), + eq(TRACK_SYSTEM_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), + eq(testPkgName), eq(testAttributionTag)); reset(mService); } diff --git a/tests/net/java/android/net/NetworkTemplateTest.kt b/tests/net/java/android/net/NetworkTemplateTest.kt index 91fcbc0fd5d7..1f8f6f311069 100644 --- a/tests/net/java/android/net/NetworkTemplateTest.kt +++ b/tests/net/java/android/net/NetworkTemplateTest.kt @@ -35,7 +35,6 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 -import org.mockito.Mockito.doReturn import org.mockito.Mockito.mock import org.mockito.MockitoAnnotations import kotlin.test.assertFalse @@ -60,16 +59,13 @@ class NetworkTemplateTest { subscriberId: String? = null, ssid: String? = null ): NetworkState { - val info = mock(NetworkInfo::class.java) - doReturn(type).`when`(info).type - doReturn(NetworkInfo.State.CONNECTED).`when`(info).state val lp = LinkProperties() val caps = NetworkCapabilities().apply { setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false) setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true) setSSID(ssid) } - return NetworkState(info, lp, caps, mock(Network::class.java), subscriberId, ssid) + return NetworkState(type, lp, caps, mock(Network::class.java), subscriberId, ssid) } private fun NetworkTemplate.assertMatches(ident: NetworkIdentity) = diff --git a/tests/net/java/android/net/VpnTransportInfoTest.java b/tests/net/java/android/net/VpnTransportInfoTest.java new file mode 100644 index 000000000000..2fd5e3861cef --- /dev/null +++ b/tests/net/java/android/net/VpnTransportInfoTest.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import static com.android.testutils.ParcelUtils.assertParcelSane; +import static com.android.testutils.ParcelUtils.assertParcelingIsLossless; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class VpnTransportInfoTest { + + @Test + public void testParceling() { + VpnTransportInfo v = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM); + assertParcelSane(v, 1 /* fieldCount */); + assertParcelingIsLossless(v); + } + + @Test + public void testEqualsAndHashCode() { + VpnTransportInfo v1 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM); + VpnTransportInfo v2 = new VpnTransportInfo(VpnManager.TYPE_VPN_SERVICE); + VpnTransportInfo v3 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM); + assertNotEquals(v1, v2); + assertEquals(v1, v3); + assertEquals(v1.hashCode(), v3.hashCode()); + } +}
\ No newline at end of file diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 4c658e1be1e1..2c9aee0695c4 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -132,6 +132,7 @@ import static org.mockito.Mockito.when; import android.Manifest; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.AlarmManager; import android.app.AppOpsManager; import android.app.NotificationManager; @@ -204,6 +205,7 @@ import android.net.UidRangeParcel; import android.net.UnderlyingNetworkInfo; import android.net.Uri; import android.net.VpnManager; +import android.net.VpnTransportInfo; import android.net.metrics.IpConnectivityLog; import android.net.shared.NetworkMonitorUtils; import android.net.shared.PrivateDnsConfig; @@ -252,6 +254,7 @@ 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; @@ -329,11 +332,12 @@ public class ConnectivityServiceTest { 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 @@ -358,8 +362,15 @@ public class ConnectivityServiceTest { private static final String INTERFACE_NAME = "interface"; - private static final String TEST_VENUE_URL_NA = "https://android.com/"; + private static final String TEST_VENUE_URL_NA_PASSPOINT = "https://android.com/"; + private static final String TEST_VENUE_URL_NA_OTHER = "https://example.com/"; + private static final String TEST_TERMS_AND_CONDITIONS_URL_NA_PASSPOINT = + "https://android.com/terms/"; + private static final String TEST_TERMS_AND_CONDITIONS_URL_NA_OTHER = + "https://example.com/terms/"; private static final String TEST_VENUE_URL_CAPPORT = "https://android.com/capport/"; + private static final String TEST_USER_PORTAL_API_URL_CAPPORT = + "https://android.com/user/api/capport/"; private static final String TEST_FRIENDLY_NAME = "Network friendly name"; private static final String TEST_REDIRECT_URL = "http://example.com/firstPath"; @@ -906,28 +917,69 @@ public class ConnectivityServiceTest { } /** - * 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). @@ -936,7 +988,6 @@ public class ConnectivityServiceTest { public MockNetworkFactory(Looper looper, Context context, String logTag, NetworkCapabilities filter) { super(looper, context, logTag, filter); - mExpectations = new LinkedBlockingQueue<>(); } public int getMyRequestCount() { @@ -970,95 +1021,33 @@ public class ConnectivityServiceTest { @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) { @@ -1129,7 +1118,7 @@ public class ConnectivityServiceTest { } @Override - public int getActiveAppVpnType() { + public int getActiveVpnType() { return mVpnType; } @@ -1142,10 +1131,12 @@ public class ConnectivityServiceTest { private void registerAgent(boolean isAlwaysMetered, Set<UidRange> uids, LinkProperties lp) throws Exception { if (mAgentRegistered) throw new IllegalStateException("already registered"); + updateState(NetworkInfo.DetailedState.CONNECTING, "registerAgent"); mConfig = new VpnConfig(); setUids(uids); if (!isAlwaysMetered) mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED); mInterface = VPN_IFNAME; + mNetworkCapabilities.setTransportInfo(new VpnTransportInfo(getActiveVpnType())); mMockNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp, mNetworkCapabilities); mMockNetworkAgent.waitForIdle(TIMEOUT_MS); @@ -1282,22 +1273,35 @@ public class ConnectivityServiceTest { } } - private void updateUidNetworkingBlocked() { - doAnswer(i -> NetworkPolicyManagerInternal.isUidNetworkingBlocked( - i.getArgument(0) /* uid */, mUidRules, i.getArgument(1) /* metered */, - mRestrictBackground) + 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 { mUidRules = uidRules; - updateUidNetworkingBlocked(); mPolicyListener.onUidRulesChanged(Process.myUid(), mUidRules); } private void setRestrictBackgroundChanged(boolean restrictBackground) throws RemoteException { mRestrictBackground = restrictBackground; - updateUidNetworkingBlocked(); mPolicyListener.onRestrictBackgroundChanged(mRestrictBackground); } @@ -1409,6 +1413,7 @@ public class ConnectivityServiceTest { mMockNetd, mDeps); mService.mLingerDelayMs = TEST_LINGER_DELAY_MS; + mService.mNascentDelayMs = TEST_NASCENT_DELAY_MS; verify(mDeps).makeMultinetworkPolicyTracker(any(), any(), any()); final ArgumentCaptor<INetworkPolicyListener> policyListenerCaptor = @@ -1751,6 +1756,108 @@ public class ConnectivityServiceTest { 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 @@ -2595,12 +2702,6 @@ public class ConnectivityServiceTest { 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) @@ -2616,15 +2717,19 @@ public class ConnectivityServiceTest { NetworkCapabilities filter = new NetworkCapabilities(); filter.addCapability(capability); + // Add NOT_VCN_MANAGED capability into filter unconditionally since some request will add + // NOT_VCN_MANAGED automatically but not for NetworkCapabilities, + // see {@code NetworkCapabilities#deduceNotVcnManagedCapability} for more details. + filter.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); final HandlerThread handlerThread = new HandlerThread("testNetworkFactoryRequests"); handlerThread.start(); final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(), 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 @@ -2633,13 +2738,12 @@ public class ConnectivityServiceTest { 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. @@ -2653,15 +2757,14 @@ public class ConnectivityServiceTest { // 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]; @@ -2671,24 +2774,24 @@ public class ConnectivityServiceTest { 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()); @@ -2731,9 +2834,8 @@ public class ConnectivityServiceTest { 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(); @@ -3194,39 +3296,68 @@ public class ConnectivityServiceTest { } private class CaptivePortalTestData { - CaptivePortalTestData(CaptivePortalData naData, CaptivePortalData capportData, - CaptivePortalData expectedMergedData) { - mNaData = naData; + CaptivePortalTestData(CaptivePortalData naPasspointData, CaptivePortalData capportData, + CaptivePortalData naOtherData, CaptivePortalData expectedMergedPasspointData, + CaptivePortalData expectedMergedOtherData) { + mNaPasspointData = naPasspointData; mCapportData = capportData; - mExpectedMergedData = expectedMergedData; + mNaOtherData = naOtherData; + mExpectedMergedPasspointData = expectedMergedPasspointData; + mExpectedMergedOtherData = expectedMergedOtherData; } - public final CaptivePortalData mNaData; + public final CaptivePortalData mNaPasspointData; public final CaptivePortalData mCapportData; - public final CaptivePortalData mExpectedMergedData; + public final CaptivePortalData mNaOtherData; + public final CaptivePortalData mExpectedMergedPasspointData; + public final CaptivePortalData mExpectedMergedOtherData; + } private CaptivePortalTestData setupCaptivePortalData() { final CaptivePortalData capportData = new CaptivePortalData.Builder() .setUserPortalUrl(Uri.parse(TEST_REDIRECT_URL)) .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_CAPPORT)) + .setUserPortalUrl(Uri.parse(TEST_USER_PORTAL_API_URL_CAPPORT)) .setExpiryTime(1000000L) .setBytesRemaining(12345L) .build(); - final CaptivePortalData naData = new CaptivePortalData.Builder() + final CaptivePortalData naPasspointData = new CaptivePortalData.Builder() .setBytesRemaining(80802L) - .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_NA)) + .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_NA_PASSPOINT), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) + .setUserPortalUrl(Uri.parse(TEST_TERMS_AND_CONDITIONS_URL_NA_PASSPOINT), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) .setVenueFriendlyName(TEST_FRIENDLY_NAME).build(); - final CaptivePortalData expectedMergedData = new CaptivePortalData.Builder() + final CaptivePortalData naOtherData = new CaptivePortalData.Builder() + .setBytesRemaining(80802L) + .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_NA_OTHER), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) + .setUserPortalUrl(Uri.parse(TEST_TERMS_AND_CONDITIONS_URL_NA_OTHER), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) + .setVenueFriendlyName(TEST_FRIENDLY_NAME).build(); + + final CaptivePortalData expectedMergedPasspointData = new CaptivePortalData.Builder() .setUserPortalUrl(Uri.parse(TEST_REDIRECT_URL)) .setBytesRemaining(12345L) .setExpiryTime(1000000L) - .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_NA)) + .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_NA_PASSPOINT), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) + .setUserPortalUrl(Uri.parse(TEST_TERMS_AND_CONDITIONS_URL_NA_PASSPOINT), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) .setVenueFriendlyName(TEST_FRIENDLY_NAME).build(); - return new CaptivePortalTestData(naData, capportData, expectedMergedData); + final CaptivePortalData expectedMergedOtherData = new CaptivePortalData.Builder() + .setUserPortalUrl(Uri.parse(TEST_REDIRECT_URL)) + .setBytesRemaining(12345L) + .setExpiryTime(1000000L) + .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_CAPPORT)) + .setUserPortalUrl(Uri.parse(TEST_USER_PORTAL_API_URL_CAPPORT)) + .setVenueFriendlyName(TEST_FRIENDLY_NAME).build(); + return new CaptivePortalTestData(naPasspointData, capportData, naOtherData, + expectedMergedPasspointData, expectedMergedOtherData); } @Test @@ -3240,15 +3371,26 @@ public class ConnectivityServiceTest { captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp -> captivePortalTestData.mCapportData.equals(lp.getCaptivePortalData())); - // Venue URL and friendly name from Network agent, confirm that API data gets precedence - // on the bytes remaining. + // Venue URL, T&C URL and friendly name from Network agent with Passpoint source, confirm + // that API data gets precedence on the bytes remaining. final LinkProperties linkProperties = new LinkProperties(); - linkProperties.setCaptivePortalData(captivePortalTestData.mNaData); + linkProperties.setCaptivePortalData(captivePortalTestData.mNaPasspointData); + mWiFiNetworkAgent.sendLinkProperties(linkProperties); + + // Make sure that the capport data is merged + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> captivePortalTestData.mExpectedMergedPasspointData + .equals(lp.getCaptivePortalData())); + + // Now send this information from non-Passpoint source, confirm that Capport data takes + // precedence + linkProperties.setCaptivePortalData(captivePortalTestData.mNaOtherData); mWiFiNetworkAgent.sendLinkProperties(linkProperties); // Make sure that the capport data is merged captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, - lp -> captivePortalTestData.mExpectedMergedData.equals(lp.getCaptivePortalData())); + lp -> captivePortalTestData.mExpectedMergedOtherData + .equals(lp.getCaptivePortalData())); // Create a new LP with no Network agent capport data final LinkProperties newLps = new LinkProperties(); @@ -3265,12 +3407,12 @@ public class ConnectivityServiceTest { captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp -> lp.getCaptivePortalData() == null); - newLps.setCaptivePortalData(captivePortalTestData.mNaData); + newLps.setCaptivePortalData(captivePortalTestData.mNaPasspointData); mWiFiNetworkAgent.sendLinkProperties(newLps); // Make sure that only the network agent capport data is available captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, - lp -> captivePortalTestData.mNaData.equals(lp.getCaptivePortalData())); + lp -> captivePortalTestData.mNaPasspointData.equals(lp.getCaptivePortalData())); } @Test @@ -3281,12 +3423,12 @@ public class ConnectivityServiceTest { // Venue URL and friendly name from Network agent, confirm that API data gets precedence // on the bytes remaining. final LinkProperties linkProperties = new LinkProperties(); - linkProperties.setCaptivePortalData(captivePortalTestData.mNaData); + linkProperties.setCaptivePortalData(captivePortalTestData.mNaPasspointData); mWiFiNetworkAgent.sendLinkProperties(linkProperties); // Make sure that the data is saved correctly captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, - lp -> captivePortalTestData.mNaData.equals(lp.getCaptivePortalData())); + lp -> captivePortalTestData.mNaPasspointData.equals(lp.getCaptivePortalData())); // Expected merged data: Network agent data is preferred, and values that are not used by // it are merged from capport data @@ -3294,7 +3436,8 @@ public class ConnectivityServiceTest { // Make sure that the Capport data is merged correctly captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, - lp -> captivePortalTestData.mExpectedMergedData.equals(lp.getCaptivePortalData())); + lp -> captivePortalTestData.mExpectedMergedPasspointData.equals( + lp.getCaptivePortalData())); // Now set the naData to null linkProperties.setCaptivePortalData(null); @@ -3305,6 +3448,32 @@ public class ConnectivityServiceTest { lp -> captivePortalTestData.mCapportData.equals(lp.getCaptivePortalData())); } + @Test + public void testMergeCaptivePortalDataFromNetworkAgentOtherSourceFirstThenCapport() + throws Exception { + final TestNetworkCallback captivePortalCallback = setupNetworkCallbackAndConnectToWifi(); + final CaptivePortalTestData captivePortalTestData = setupCaptivePortalData(); + + // Venue URL and friendly name from Network agent, confirm that API data gets precedence + // on the bytes remaining. + final LinkProperties linkProperties = new LinkProperties(); + linkProperties.setCaptivePortalData(captivePortalTestData.mNaOtherData); + mWiFiNetworkAgent.sendLinkProperties(linkProperties); + + // Make sure that the data is saved correctly + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> captivePortalTestData.mNaOtherData.equals(lp.getCaptivePortalData())); + + // Expected merged data: Network agent data is preferred, and values that are not used by + // it are merged from capport data + mWiFiNetworkAgent.notifyCapportApiDataChanged(captivePortalTestData.mCapportData); + + // Make sure that the Capport data is merged correctly + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> captivePortalTestData.mExpectedMergedOtherData.equals( + lp.getCaptivePortalData())); + } + private NetworkRequest.Builder newWifiRequestBuilder() { return new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI); } @@ -3557,10 +3726,19 @@ public class ConnectivityServiceTest { @Test public void testRegisterDefaultNetworkCallback() throws Exception { + // NETWORK_SETTINGS is necessary to call registerSystemDefaultNetworkCallback. + mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS, + PERMISSION_GRANTED); + final TestNetworkCallback defaultNetworkCallback = new TestNetworkCallback(); mCm.registerDefaultNetworkCallback(defaultNetworkCallback); defaultNetworkCallback.assertNoCallback(); + final Handler handler = new Handler(ConnectivityThread.getInstanceLooper()); + final TestNetworkCallback systemDefaultCallback = new TestNetworkCallback(); + mCm.registerSystemDefaultNetworkCallback(systemDefaultCallback, handler); + systemDefaultCallback.assertNoCallback(); + // Create a TRANSPORT_CELLULAR request to keep the mobile interface up // whenever Wi-Fi is up. Without this, the mobile network agent is // reaped before any other activity can take place. @@ -3575,27 +3753,35 @@ public class ConnectivityServiceTest { mCellNetworkAgent.connect(true); cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + systemDefaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + assertEquals(systemDefaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); // Bring up wifi and expect CALLBACK_AVAILABLE. mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(true); cellNetworkCallback.assertNoCallback(); defaultNetworkCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); + systemDefaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + assertEquals(systemDefaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); // Bring down cell. Expect no default network callback, since it wasn't the default. mCellNetworkAgent.disconnect(); cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); defaultNetworkCallback.assertNoCallback(); + systemDefaultCallback.assertNoCallback(); assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + assertEquals(systemDefaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); // Bring up cell. Expect no default network callback, since it won't be the default. mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(true); cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); defaultNetworkCallback.assertNoCallback(); + systemDefaultCallback.assertNoCallback(); assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + assertEquals(systemDefaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); // Bring down wifi. Expect the default network callback to notified of LOST wifi // followed by AVAILABLE cell. @@ -3603,19 +3789,25 @@ public class ConnectivityServiceTest { cellNetworkCallback.assertNoCallback(); defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); defaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); + systemDefaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + systemDefaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); mCellNetworkAgent.disconnect(); cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + systemDefaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); waitForIdle(); assertEquals(null, mCm.getActiveNetwork()); mMockVpn.establishForMyUid(); assertUidRangesUpdatedForMyUid(true); defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn); + systemDefaultCallback.assertNoCallback(); assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + assertEquals(null, systemDefaultCallback.getLastAvailableNetwork()); mMockVpn.disconnect(); defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn); + systemDefaultCallback.assertNoCallback(); waitForIdle(); assertEquals(null, mCm.getActiveNetwork()); } @@ -3870,44 +4062,44 @@ public class ConnectivityServiceTest { handlerThread.start(); NetworkCapabilities filter = new NetworkCapabilities() .addTransportType(TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) .addCapability(NET_CAPABILITY_INTERNET); final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(), mServiceContext, "testFactory", filter); 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(); @@ -3915,12 +4107,12 @@ public class ConnectivityServiceTest { 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(); @@ -4224,46 +4416,33 @@ public class ConnectivityServiceTest { 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(); } @@ -5338,20 +5517,20 @@ public class ConnectivityServiceTest { // 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}); @@ -5400,25 +5579,25 @@ public class ConnectivityServiceTest { // 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}); @@ -5787,6 +5966,7 @@ public class ConnectivityServiceTest { .addTransportType(TRANSPORT_CELLULAR) .addCapability(NET_CAPABILITY_INTERNET) .addCapability(NET_CAPABILITY_NOT_CONGESTED) + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) .setLinkDownstreamBandwidthKbps(10); final NetworkCapabilities wifiNc = new NetworkCapabilities() .addTransportType(TRANSPORT_WIFI) @@ -5795,6 +5975,7 @@ public class ConnectivityServiceTest { .addCapability(NET_CAPABILITY_NOT_ROAMING) .addCapability(NET_CAPABILITY_NOT_CONGESTED) .addCapability(NET_CAPABILITY_NOT_SUSPENDED) + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) .setLinkUpstreamBandwidthKbps(20); mCellNetworkAgent.setNetworkCapabilities(cellNc, true /* sendToConnectivityService */); mWiFiNetworkAgent.setNetworkCapabilities(wifiNc, true /* sendToConnectivityService */); @@ -5893,7 +6074,7 @@ public class ConnectivityServiceTest { 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)); @@ -6053,6 +6234,10 @@ public class ConnectivityServiceTest { @Test public void testVpnNetworkActive() throws Exception { + // NETWORK_SETTINGS is necessary to call registerSystemDefaultNetworkCallback. + mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS, + PERMISSION_GRANTED); + final int uid = Process.myUid(); final TestNetworkCallback genericNetworkCallback = new TestNetworkCallback(); @@ -6060,6 +6245,7 @@ public class ConnectivityServiceTest { final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback(); final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback(); final TestNetworkCallback defaultCallback = new TestNetworkCallback(); + final TestNetworkCallback systemDefaultCallback = new TestNetworkCallback(); final NetworkRequest genericNotVpnRequest = new NetworkRequest.Builder().build(); final NetworkRequest genericRequest = new NetworkRequest.Builder() .removeCapability(NET_CAPABILITY_NOT_VPN).build(); @@ -6073,6 +6259,8 @@ public class ConnectivityServiceTest { mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback); mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback); mCm.registerDefaultNetworkCallback(defaultCallback); + mCm.registerSystemDefaultNetworkCallback(systemDefaultCallback, + new Handler(ConnectivityThread.getInstanceLooper())); defaultCallback.assertNoCallback(); mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); @@ -6082,12 +6270,13 @@ public class ConnectivityServiceTest { genericNotVpnNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); wifiNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + systemDefaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); vpnNetworkCallback.assertNoCallback(); assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); 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 @@ -6102,7 +6291,10 @@ public class ConnectivityServiceTest { wifiNetworkCallback.assertNoCallback(); vpnNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn); defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn); + systemDefaultCallback.assertNoCallback(); assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); + assertEquals(mWiFiNetworkAgent.getNetwork(), + systemDefaultCallback.getLastAvailableNetwork()); ranges.clear(); mMockVpn.setUids(ranges); @@ -6119,6 +6311,7 @@ public class ConnectivityServiceTest { // much, but that is the reason the test here has to check for an update to the // capabilities instead of the expected LOST then AVAILABLE. defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn); + systemDefaultCallback.assertNoCallback(); ranges.add(new UidRange(uid, uid)); mMockVpn.setUids(ranges); @@ -6130,6 +6323,7 @@ public class ConnectivityServiceTest { // TODO : Here like above, AVAILABLE would be correct, but because this can't actually // happen outside of the test, ConnectivityService does not rematch callbacks. defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn); + systemDefaultCallback.assertNoCallback(); mWiFiNetworkAgent.disconnect(); @@ -6138,6 +6332,7 @@ public class ConnectivityServiceTest { wifiNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); vpnNetworkCallback.assertNoCallback(); defaultCallback.assertNoCallback(); + systemDefaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); mMockVpn.disconnect(); @@ -6146,12 +6341,14 @@ public class ConnectivityServiceTest { wifiNetworkCallback.assertNoCallback(); vpnNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn); defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn); + systemDefaultCallback.assertNoCallback(); assertEquals(null, mCm.getActiveNetwork()); mCm.unregisterNetworkCallback(genericNetworkCallback); mCm.unregisterNetworkCallback(wifiNetworkCallback); mCm.unregisterNetworkCallback(vpnNetworkCallback); mCm.unregisterNetworkCallback(defaultCallback); + mCm.unregisterNetworkCallback(systemDefaultCallback); } @Test @@ -6291,7 +6488,7 @@ public class ConnectivityServiceTest { private void assertDefaultNetworkCapabilities(int userId, NetworkAgentWrapper... networks) { final NetworkCapabilities[] defaultCaps = mService.getDefaultNetworkCapabilitiesForUser( - userId, "com.android.calling.package"); + userId, "com.android.calling.package", "com.test"); final String defaultCapsString = Arrays.toString(defaultCaps); assertEquals(defaultCapsString, defaultCaps.length, networks.length); final Set<NetworkCapabilities> defaultCapsSet = new ArraySet<>(defaultCaps); @@ -6335,7 +6532,7 @@ public class ConnectivityServiceTest { mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED); mCellNetworkAgent.connect(true); - mService.setUnderlyingNetworksForVpn( + mMockVpn.setUnderlyingNetworks( new Network[] { mCellNetworkAgent.getNetwork() }); vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, @@ -6350,7 +6547,7 @@ public class ConnectivityServiceTest { mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED); mWiFiNetworkAgent.connect(true); - mService.setUnderlyingNetworksForVpn( + mMockVpn.setUnderlyingNetworks( new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() }); vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, @@ -6361,7 +6558,7 @@ public class ConnectivityServiceTest { 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, @@ -6392,7 +6589,7 @@ public class ConnectivityServiceTest { 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, @@ -6403,7 +6600,7 @@ public class ConnectivityServiceTest { assertDefaultNetworkCapabilities(userId, mWiFiNetworkAgent); // Use both again. - mService.setUnderlyingNetworksForVpn( + mMockVpn.setUnderlyingNetworks( new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() }); vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, @@ -6418,7 +6615,7 @@ public class ConnectivityServiceTest { 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) @@ -6429,7 +6626,7 @@ public class ConnectivityServiceTest { assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent); // Use both again. - mService.setUnderlyingNetworksForVpn( + mMockVpn.setUnderlyingNetworks( new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() }); vpnNetworkCallback.expectCapabilitiesThat(mMockVpn, @@ -6564,9 +6761,7 @@ public class ConnectivityServiceTest { 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. @@ -6590,7 +6785,7 @@ public class ConnectivityServiceTest { // 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. @@ -6647,9 +6842,7 @@ public class ConnectivityServiceTest { // 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)); @@ -6659,8 +6852,7 @@ public class ConnectivityServiceTest { // 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)); @@ -6762,7 +6954,7 @@ public class ConnectivityServiceTest { // 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(); @@ -6770,7 +6962,7 @@ public class ConnectivityServiceTest { assertTrue(mCm.isActiveNetworkMetered()); // VPN is now using WiFi - mService.setUnderlyingNetworksForVpn( + mMockVpn.setUnderlyingNetworks( new Network[] { mWiFiNetworkAgent.getNetwork() }); waitForIdle(); @@ -6778,7 +6970,7 @@ public class ConnectivityServiceTest { assertFalse(mCm.isActiveNetworkMetered()); // VPN is using Cell | WiFi. - mService.setUnderlyingNetworksForVpn( + mMockVpn.setUnderlyingNetworks( new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() }); waitForIdle(); @@ -6786,7 +6978,7 @@ public class ConnectivityServiceTest { assertTrue(mCm.isActiveNetworkMetered()); // VPN is using WiFi | Cell. - mService.setUnderlyingNetworksForVpn( + mMockVpn.setUnderlyingNetworks( new Network[] { mWiFiNetworkAgent.getNetwork(), mCellNetworkAgent.getNetwork() }); waitForIdle(); @@ -6794,7 +6986,7 @@ public class ConnectivityServiceTest { 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. @@ -6821,7 +7013,7 @@ public class ConnectivityServiceTest { 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. @@ -6829,7 +7021,7 @@ public class ConnectivityServiceTest { // VPN explicitly declares WiFi as its underlying network. - mService.setUnderlyingNetworksForVpn( + mMockVpn.setUnderlyingNetworks( new Network[] { mWiFiNetworkAgent.getNetwork() }); waitForIdle(); @@ -6853,6 +7045,7 @@ public class ConnectivityServiceTest { .addTransportType(TRANSPORT_CELLULAR) .build(); mCm.registerNetworkCallback(cellRequest, cellNetworkCallback); + mockUidNetworkingBlocked(); mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(true); @@ -6935,6 +7128,7 @@ public class ConnectivityServiceTest { 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); @@ -7204,6 +7398,14 @@ public class ConnectivityServiceTest { when(mKeyStore.get(Credentials.VPN + profileName)).thenReturn(encodedProfile); } + private void establishLegacyLockdownVpn() throws Exception { + mMockVpn.setVpnType(VpnManager.TYPE_VPN_LEGACY); + // 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( @@ -7228,9 +7430,7 @@ public class ConnectivityServiceTest { 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()); @@ -7298,22 +7498,33 @@ public class ConnectivityServiceTest { 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)); + VpnTransportInfo ti = (VpnTransportInfo) vpnNc.getTransportInfo(); + assertNotNull(ti); + assertEquals(VpnManager.TYPE_VPN_LEGACY, ti.type); // 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. @@ -7346,16 +7557,20 @@ public class ConnectivityServiceTest { // 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 @@ -7426,19 +7641,13 @@ public class ConnectivityServiceTest { 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); - } + verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId)); + reset(mMockNetd); mCellNetworkAgent.removeCapability(testCap); callbackWithCap.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); callbackWithoutCap.assertNoCallback(); - if (testCap == NET_CAPABILITY_TRUSTED) { - verify(mMockNetd).networkClearDefault(); - } + verify(mMockNetd).networkClearDefault(); mCm.unregisterNetworkCallback(callbackWithCap); mCm.unregisterNetworkCallback(callbackWithoutCap); @@ -8289,7 +8498,8 @@ public class ConnectivityServiceTest { when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(locationToggle); if (op != null) { - when(mAppOpsManager.noteOp(eq(op), eq(Process.myUid()), eq(mContext.getPackageName()))) + when(mAppOpsManager.noteOp(eq(op), eq(Process.myUid()), + eq(mContext.getPackageName()), eq(getAttributionTag()), anyString())) .thenReturn(AppOpsManager.MODE_ALLOWED); } @@ -8302,7 +8512,7 @@ public class ConnectivityServiceTest { final NetworkCapabilities netCap = new NetworkCapabilities().setOwnerUid(ownerUid); return mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled( - netCap, callerUid, mContext.getPackageName()).getOwnerUid(); + netCap, callerUid, mContext.getPackageName(), getAttributionTag()).getOwnerUid(); } private void verifyWifiInfoCopyNetCapsForCallerPermission( @@ -8312,7 +8522,7 @@ public class ConnectivityServiceTest { final NetworkCapabilities netCap = new NetworkCapabilities().setTransportInfo(wifiInfo); mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled( - netCap, callerUid, mContext.getPackageName()); + netCap, callerUid, mContext.getPackageName(), getAttributionTag()); verify(wifiInfo).makeCopy(eq(shouldMakeCopyWithLocationSensitiveFieldsParcelable)); } @@ -8399,13 +8609,14 @@ public class ConnectivityServiceTest { 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 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) @@ -8430,11 +8641,7 @@ public class ConnectivityServiceTest { final int myUid = Process.myUid(); setupConnectionOwnerUidAsVpnApp(myUid, VpnManager.TYPE_VPN_PLATFORM); - try { - mService.getConnectionOwnerUid(getTestConnectionInfo()); - fail("Expected SecurityException for non-VpnService app"); - } catch (SecurityException expected) { - } + assertEquals(INVALID_UID, mService.getConnectionOwnerUid(getTestConnectionInfo())); } @Test @@ -8442,11 +8649,7 @@ public class ConnectivityServiceTest { final int myUid = Process.myUid(); setupConnectionOwnerUidAsVpnApp(myUid + 1, VpnManager.TYPE_VPN_SERVICE); - try { - mService.getConnectionOwnerUid(getTestConnectionInfo()); - fail("Expected SecurityException for non-VpnService app"); - } catch (SecurityException expected) { - } + assertEquals(INVALID_UID, mService.getConnectionOwnerUid(getTestConnectionInfo())); } @Test @@ -8454,8 +8657,7 @@ public class ConnectivityServiceTest { 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 @@ -8465,8 +8667,7 @@ public class ConnectivityServiceTest { mServiceContext.setPermission( android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED); - // TODO: Test the returned UID - mService.getConnectionOwnerUid(getTestConnectionInfo()); + assertEquals(42, mService.getConnectionOwnerUid(getTestConnectionInfo())); } @Test @@ -8477,8 +8678,7 @@ public class ConnectivityServiceTest { 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) { @@ -8662,7 +8862,7 @@ public class ConnectivityServiceTest { 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", @@ -8670,7 +8870,7 @@ public class ConnectivityServiceTest { 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/NetworkNotificationManagerTest.java b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java index b47be97ed002..7e85acb05dee 100644 --- a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java +++ b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java @@ -63,6 +63,8 @@ import java.util.List; @SmallTest public class NetworkNotificationManagerTest { + private static final String TEST_SSID = "Test SSID"; + private static final String TEST_EXTRA_INFO = "extra"; static final NetworkCapabilities CELL_CAPABILITIES = new NetworkCapabilities(); static final NetworkCapabilities WIFI_CAPABILITIES = new NetworkCapabilities(); static final NetworkCapabilities VPN_CAPABILITIES = new NetworkCapabilities(); @@ -72,6 +74,7 @@ public class NetworkNotificationManagerTest { WIFI_CAPABILITIES.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); WIFI_CAPABILITIES.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); + WIFI_CAPABILITIES.setSSID(TEST_SSID); // Set the underyling network to wifi. VPN_CAPABILITIES.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); @@ -112,7 +115,7 @@ public class NetworkNotificationManagerTest { when(mCtx.createContextAsUser(eq(UserHandle.ALL), anyInt())).thenReturn(asUserCtx); when(mCtx.getSystemService(eq(Context.NOTIFICATION_SERVICE))) .thenReturn(mNotificationManager); - when(mNetworkInfo.getExtraInfo()).thenReturn("extra"); + when(mNetworkInfo.getExtraInfo()).thenReturn(TEST_EXTRA_INFO); when(mResources.getColor(anyInt(), any())).thenReturn(0xFF607D8B); mManager = new NetworkNotificationManager(mCtx, mTelephonyManager); @@ -125,11 +128,11 @@ public class NetworkNotificationManagerTest { .notify(eq(tag), eq(PRIVATE_DNS_BROKEN.eventId), any()); final int transportType = NetworkNotificationManager.approximateTransportType(nai); if (transportType == NetworkCapabilities.TRANSPORT_WIFI) { - verify(mResources, times(1)).getString(title, eq(any())); + verify(mResources, times(1)).getString(eq(title), eq(TEST_EXTRA_INFO)); } else { verify(mResources, times(1)).getString(title); } - verify(mResources, times(1)).getString(R.string.private_dns_broken_detailed); + verify(mResources, times(1)).getString(eq(R.string.private_dns_broken_detailed)); } @Test diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java index 68aaaeda1b12..cffd2d1d428f 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/tests/net/java/com/android/server/connectivity/VpnTest.java @@ -25,6 +25,7 @@ import static android.net.ConnectivityManager.NetworkCallback; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; @@ -49,6 +50,7 @@ import android.annotation.NonNull; 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; @@ -73,6 +75,7 @@ import android.net.UidRange; import android.net.UidRangeParcel; import android.net.VpnManager; import android.net.VpnService; +import android.net.VpnTransportInfo; import android.net.ipsec.ike.IkeSessionCallback; import android.net.ipsec.ike.exceptions.IkeProtocolException; import android.os.Build.VERSION_CODES; @@ -119,6 +122,7 @@ import java.util.ArrayList; 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 +152,7 @@ public class VpnTest { 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 +217,8 @@ public class VpnTest { 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 +259,14 @@ public class VpnTest { @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 +275,6 @@ public class VpnTest { @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 +297,6 @@ public class VpnTest { @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 +322,6 @@ public class VpnTest { @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 +346,6 @@ public class VpnTest { @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 +373,6 @@ public class VpnTest { @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 +447,6 @@ public class VpnTest { @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 +479,6 @@ public class VpnTest { @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 +965,7 @@ public class VpnTest { 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; } @@ -984,6 +986,13 @@ public class VpnTest { startRacoon("hostname", "5.6.7.8"); // address returned by deps.resolve } + private void assertTransportInfoMatches(NetworkCapabilities nc, int type) { + assertNotNull(nc); + VpnTransportInfo ti = (VpnTransportInfo) nc.getTransportInfo(); + assertNotNull(ti); + assertEquals(type, ti.type); + } + public void startRacoon(final String serverAddr, final String expectedAddr) throws Exception { final ConditionVariable legacyRunnerReady = new ConditionVariable(); @@ -996,14 +1005,12 @@ public class VpnTest { 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 +1026,24 @@ public class VpnTest { "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); + ArgumentCaptor<NetworkCapabilities> ncCaptor = + ArgumentCaptor.forClass(NetworkCapabilities.class); + verify(mConnectivityManager, timeout(10_000)).registerNetworkAgent(any(), any(), + lpCaptor.capture(), ncCaptor.capture(), 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)); + + assertTransportInfoMatches(ncCaptor.getValue(), VpnManager.TYPE_VPN_LEGACY); } finally { // Now interrupt the thread, unblock the runner and clean up. vpn.mVpnRunner.exitVpnRunner(); @@ -1082,6 +1099,11 @@ public class VpnTest { } @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 +1200,6 @@ public class VpnTest { 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/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java index dde78aa54199..214c82da17dc 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java @@ -80,8 +80,6 @@ import android.net.INetworkStatsSession; import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; -import android.net.NetworkInfo; -import android.net.NetworkInfo.DetailedState; import android.net.NetworkState; import android.net.NetworkStats; import android.net.NetworkStatsHistory; @@ -1456,8 +1454,6 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { } private static NetworkState buildWifiState(boolean isMetered, @NonNull String iface) { - final NetworkInfo info = new NetworkInfo(TYPE_WIFI, 0, null, null); - info.setDetailedState(DetailedState.CONNECTED, null, null); final LinkProperties prop = new LinkProperties(); prop.setInterfaceName(iface); final NetworkCapabilities capabilities = new NetworkCapabilities(); @@ -1465,7 +1461,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true); capabilities.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); capabilities.setSSID(TEST_SSID); - return new NetworkState(info, prop, capabilities, WIFI_NETWORK, null, TEST_SSID); + return new NetworkState(TYPE_WIFI, prop, capabilities, WIFI_NETWORK, null, TEST_SSID); } private static NetworkState buildMobile3gState(String subscriberId) { @@ -1473,17 +1469,14 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { } private static NetworkState buildMobile3gState(String subscriberId, boolean isRoaming) { - final NetworkInfo info = new NetworkInfo( - TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UMTS, null, null); - info.setDetailedState(DetailedState.CONNECTED, null, null); - info.setRoaming(isRoaming); final LinkProperties prop = new LinkProperties(); prop.setInterfaceName(TEST_IFACE); final NetworkCapabilities capabilities = new NetworkCapabilities(); capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false); capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, !isRoaming); capabilities.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); - return new NetworkState(info, prop, capabilities, MOBILE_NETWORK, subscriberId, null); + return new NetworkState( + TYPE_MOBILE, prop, capabilities, MOBILE_NETWORK, subscriberId, null); } private NetworkStats buildEmptyStats() { @@ -1491,11 +1484,9 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { } private static NetworkState buildVpnState() { - final NetworkInfo info = new NetworkInfo(TYPE_VPN, 0, null, null); - info.setDetailedState(DetailedState.CONNECTED, null, null); final LinkProperties prop = new LinkProperties(); prop.setInterfaceName(TUN_IFACE); - return new NetworkState(info, prop, new NetworkCapabilities(), VPN_NETWORK, null, null); + return new NetworkState(TYPE_VPN, prop, new NetworkCapabilities(), VPN_NETWORK, null, null); } private long getElapsedRealtime() { diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java index 86a15912b6b4..3e659d0bc128 100644 --- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java +++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java @@ -59,12 +59,17 @@ public class VcnGatewayConnectionConfigTest { // 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); } diff --git a/tests/vcn/java/android/net/vcn/VcnManagerTest.java b/tests/vcn/java/android/net/vcn/VcnManagerTest.java index f9db408462b7..7dada9d1b6d4 100644 --- a/tests/vcn/java/android/net/vcn/VcnManagerTest.java +++ b/tests/vcn/java/android/net/vcn/VcnManagerTest.java @@ -65,7 +65,7 @@ public class VcnManagerTest { ArgumentCaptor.forClass(IVcnUnderlyingNetworkPolicyListener.class); verify(mMockVcnManagementService).addVcnUnderlyingNetworkPolicyListener(captor.capture()); - assertTrue(VcnManager.REGISTERED_POLICY_LISTENERS.containsKey(mMockPolicyListener)); + assertTrue(VcnManager.getAllPolicyListeners().containsKey(mMockPolicyListener)); IVcnUnderlyingNetworkPolicyListener listenerWrapper = captor.getValue(); listenerWrapper.onPolicyChanged(); @@ -78,7 +78,7 @@ public class VcnManagerTest { mVcnManager.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); - assertFalse(VcnManager.REGISTERED_POLICY_LISTENERS.containsKey(mMockPolicyListener)); + assertFalse(VcnManager.getAllPolicyListeners().containsKey(mMockPolicyListener)); verify(mMockVcnManagementService) .addVcnUnderlyingNetworkPolicyListener( any(IVcnUnderlyingNetworkPolicyListener.class)); @@ -88,7 +88,7 @@ public class VcnManagerTest { public void testRemoveVcnUnderlyingNetworkPolicyListenerUnknownListener() throws Exception { mVcnManager.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); - assertFalse(VcnManager.REGISTERED_POLICY_LISTENERS.containsKey(mMockPolicyListener)); + assertFalse(VcnManager.getAllPolicyListeners().containsKey(mMockPolicyListener)); verify(mMockVcnManagementService, never()) .addVcnUnderlyingNetworkPolicyListener( any(IVcnUnderlyingNetworkPolicyListener.class)); diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java index e7d334ebd490..c290bff188c1 100644 --- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java +++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java @@ -16,6 +16,10 @@ package com.android.server; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI; + import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback; import static com.android.server.vcn.VcnTestUtils.setupSystemService; @@ -66,6 +70,7 @@ import android.telephony.TelephonyManager; 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; @@ -105,6 +110,7 @@ public class VcnManagementServiceTest { Collections.unmodifiableMap(Collections.singletonMap(TEST_UUID_1, TEST_VCN_CONFIG)); private static final int TEST_SUBSCRIPTION_ID = 1; + private static final int TEST_SUBSCRIPTION_ID_2 = 2; private static final SubscriptionInfo TEST_SUBSCRIPTION_INFO = new SubscriptionInfo( TEST_SUBSCRIPTION_ID /* id */, @@ -142,6 +148,9 @@ public class VcnManagementServiceTest { private final TelephonySubscriptionTracker mSubscriptionTracker = mock(TelephonySubscriptionTracker.class); + private final ArgumentCaptor<VcnSafemodeCallback> mSafemodeCallbackCaptor = + ArgumentCaptor.forClass(VcnSafemodeCallback.class); + private final VcnManagementService mVcnMgmtSvc; private final IVcnUnderlyingNetworkPolicyListener mMockPolicyListener = @@ -184,7 +193,7 @@ public class VcnManagementServiceTest { 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( @@ -304,14 +313,17 @@ public class VcnManagementServiceTest { @Test public void testTelephonyNetworkTrackerCallbackStartsInstances() throws Exception { - triggerSubscriptionTrackerCbAndGetSnapshot(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); triggerSubscriptionTrackerCbAndGetSnapshot(Collections.emptySet()); @@ -319,6 +331,7 @@ public class VcnManagementServiceTest { mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS); mTestLooper.dispatchAll(); verify(vcn).teardownAsynchronously(); + verify(mMockPolicyListener).onPolicyChanged(); } @Test @@ -389,6 +402,7 @@ public class VcnManagementServiceTest { 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(); } } @@ -400,6 +414,7 @@ public class VcnManagementServiceTest { 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(); } } @@ -409,6 +424,7 @@ public class VcnManagementServiceTest { 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(); } } @@ -473,7 +489,13 @@ public class VcnManagementServiceTest { 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); @@ -521,58 +543,120 @@ public class VcnManagementServiceTest { } private void verifyMergedNetworkCapabilities( - NetworkCapabilities mergedCapabilities, @Transport int transportType) { + NetworkCapabilities mergedCapabilities, + @Transport int transportType, + boolean isVcnManaged, + boolean isRestricted) { assertTrue(mergedCapabilities.hasTransport(transportType)); - assertFalse( + assertEquals( + !isVcnManaged, mergedCapabilities.hasCapability( NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)); + assertEquals( + !isRestricted, + mergedCapabilities.hasCapability( + NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)); + } + + private void setupSubscriptionAndStartVcn(int subId, ParcelUuid subGrp, boolean isVcnActive) { + setUpVcnSubscription(subId, subGrp); + final Vcn vcn = startAndGetVcnInstance(subGrp); + doReturn(isVcnActive).when(vcn).isActive(); + } + + private VcnUnderlyingNetworkPolicy startVcnAndGetPolicyForTransport( + int subId, ParcelUuid subGrp, boolean isVcnActive, int transport) { + setupSubscriptionAndStartVcn(subId, subGrp, isVcnActive); + + final NetworkCapabilities.Builder ncBuilder = new NetworkCapabilities.Builder(); + ncBuilder.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); + if (transport == TRANSPORT_CELLULAR) { + ncBuilder + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID)); + } else if (transport == TRANSPORT_WIFI) { + WifiInfo wifiInfo = mock(WifiInfo.class); + when(wifiInfo.makeCopy(anyBoolean())).thenReturn(wifiInfo); + when(mMockDeps.getSubIdForWifiInfo(eq(wifiInfo))).thenReturn(TEST_SUBSCRIPTION_ID); + + ncBuilder + .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .setTransportInfo(wifiInfo); + } else { + throw new IllegalArgumentException("Unknown transport"); + } + + return mVcnMgmtSvc.getUnderlyingNetworkPolicy(ncBuilder.build(), new LinkProperties()); } @Test - public void testGetUnderlyingNetworkPolicyTransportCell() throws Exception { - setUpVcnSubscription(TEST_SUBSCRIPTION_ID, TEST_UUID_2); + public void testGetUnderlyingNetworkPolicyCellular() throws Exception { + final VcnUnderlyingNetworkPolicy policy = + startVcnAndGetPolicyForTransport( + TEST_SUBSCRIPTION_ID, TEST_UUID_2, true /* isActive */, TRANSPORT_CELLULAR); - NetworkCapabilities nc = - new NetworkCapabilities.Builder() - .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) - .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID)) - .build(); + assertFalse(policy.isTeardownRequested()); + verifyMergedNetworkCapabilities( + policy.getMergedNetworkCapabilities(), + TRANSPORT_CELLULAR, + true /* isVcnManaged */, + false /* isRestricted */); + } - VcnUnderlyingNetworkPolicy policy = - mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties()); + @Test + public void testGetUnderlyingNetworkPolicyCellular_safeMode() throws Exception { + final VcnUnderlyingNetworkPolicy policy = + startVcnAndGetPolicyForTransport( + TEST_SUBSCRIPTION_ID, + TEST_UUID_2, + false /* isActive */, + TRANSPORT_CELLULAR); assertFalse(policy.isTeardownRequested()); verifyMergedNetworkCapabilities( - policy.getMergedNetworkCapabilities(), NetworkCapabilities.TRANSPORT_CELLULAR); + policy.getMergedNetworkCapabilities(), + NetworkCapabilities.TRANSPORT_CELLULAR, + false /* isVcnManaged */, + false /* isRestricted */); } @Test - public void testGetUnderlyingNetworkPolicyTransportWifi() throws Exception { - setUpVcnSubscription(TEST_SUBSCRIPTION_ID, TEST_UUID_2); + public void testGetUnderlyingNetworkPolicyWifi() throws Exception { + final VcnUnderlyingNetworkPolicy policy = + startVcnAndGetPolicyForTransport( + TEST_SUBSCRIPTION_ID, TEST_UUID_2, true /* isActive */, TRANSPORT_WIFI); - 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(); + assertFalse(policy.isTeardownRequested()); + verifyMergedNetworkCapabilities( + policy.getMergedNetworkCapabilities(), + NetworkCapabilities.TRANSPORT_WIFI, + true /* isVcnManaged */, + true /* isRestricted */); + } - VcnUnderlyingNetworkPolicy policy = - mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties()); + @Test + public void testGetUnderlyingNetworkPolicyVcnWifi_safeMode() throws Exception { + final VcnUnderlyingNetworkPolicy policy = + startVcnAndGetPolicyForTransport( + TEST_SUBSCRIPTION_ID, TEST_UUID_2, false /* isActive */, TRANSPORT_WIFI); assertFalse(policy.isTeardownRequested()); verifyMergedNetworkCapabilities( - policy.getMergedNetworkCapabilities(), NetworkCapabilities.TRANSPORT_WIFI); + policy.getMergedNetworkCapabilities(), + NetworkCapabilities.TRANSPORT_WIFI, + false /* isVcnManaged */, + true /* isRestricted */); } @Test public void testGetUnderlyingNetworkPolicyNonVcnNetwork() throws Exception { + setupSubscriptionAndStartVcn(TEST_SUBSCRIPTION_ID, TEST_UUID_1, true /* isActive */); + NetworkCapabilities nc = new NetworkCapabilities.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) - .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID)) + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) + .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID_2)) .build(); VcnUnderlyingNetworkPolicy policy = @@ -591,4 +675,56 @@ public class VcnManagementServiceTest { 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 index 48e068d14182..1d459a347526 100644 --- a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java +++ b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java @@ -42,8 +42,9 @@ import android.net.TelephonyNetworkSpecifier; import android.os.ParcelUuid; import android.os.test.TestLooper; import android.telephony.SubscriptionInfo; -import android.telephony.SubscriptionManager; +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; @@ -58,13 +59,19 @@ import org.mockito.MockitoAnnotations; import java.util.Arrays; import java.util.Collections; -import java.util.List; +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() @@ -90,7 +97,7 @@ public class UnderlyingNetworkTrackerTest { @Mock private Context mContext; @Mock private VcnNetworkProvider mVcnNetworkProvider; @Mock private ConnectivityManager mConnectivityManager; - @Mock private SubscriptionManager mSubscriptionManager; + @Mock private TelephonySubscriptionSnapshot mSubscriptionSnapshot; @Mock private UnderlyingNetworkTrackerCallback mNetworkTrackerCb; @Mock private Network mNetwork; @@ -113,23 +120,14 @@ public class UnderlyingNetworkTrackerTest { mConnectivityManager, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class); - setupSystemService( - mContext, - mSubscriptionManager, - Context.TELEPHONY_SUBSCRIPTION_SERVICE, - SubscriptionManager.class); - List<SubscriptionInfo> initialSubInfos = - Arrays.asList( - getSubscriptionInfoForSubId(INITIAL_SUB_ID_1), - getSubscriptionInfoForSubId(INITIAL_SUB_ID_2)); - when(mSubscriptionManager.getSubscriptionsInGroup(eq(SUB_GROUP))) - .thenReturn(initialSubInfos); + when(mSubscriptionSnapshot.getAllSubIdsInGroup(eq(SUB_GROUP))).thenReturn(INITIAL_SUB_IDS); mUnderlyingNetworkTracker = new UnderlyingNetworkTracker( mVcnContext, SUB_GROUP, + mSubscriptionSnapshot, Collections.singleton(NetworkCapabilities.NET_CAPABILITY_INTERNET), mNetworkTrackerCb); } @@ -154,23 +152,45 @@ public class UnderlyingNetworkTrackerTest { eq(getWifiRequest()), any(), any(NetworkBringupCallback.class)); - verify(mConnectivityManager) - .requestBackgroundNetwork( - eq(getCellRequestForSubId(INITIAL_SUB_ID_1)), - any(), - any(NetworkBringupCallback.class)); - verify(mConnectivityManager) - .requestBackgroundNetwork( - eq(getCellRequestForSubId(INITIAL_SUB_ID_2)), - 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)); + } + } - verify(mSubscriptionManager).getSubscriptionsInGroup(eq(SUB_GROUP)); + @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() { 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 000000000000..278d93a1b17b --- /dev/null +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java @@ -0,0 +1,183 @@ +/* + * 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 android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI; + +import static com.android.server.vcn.VcnGatewayConnection.VcnChildSessionConfiguration; +import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import android.net.LinkProperties; +import android.net.NetworkCapabilities; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; + +import java.util.Collections; + +/** 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 testChildOpenedRegistersNetwork() throws Exception { + final VcnChildSessionConfiguration mMockChildSessionConfig = + mock(VcnChildSessionConfiguration.class); + doReturn(Collections.singletonList(TEST_INTERNAL_ADDR)) + .when(mMockChildSessionConfig) + .getInternalAddresses(); + doReturn(Collections.singletonList(TEST_DNS_ADDR)) + .when(mMockChildSessionConfig) + .getInternalDnsServers(); + + getChildSessionCallback().onOpened(mMockChildSessionConfig); + mTestLooper.dispatchAll(); + + assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState()); + + final ArgumentCaptor<LinkProperties> lpCaptor = + ArgumentCaptor.forClass(LinkProperties.class); + final ArgumentCaptor<NetworkCapabilities> ncCaptor = + ArgumentCaptor.forClass(NetworkCapabilities.class); + verify(mConnMgr) + .registerNetworkAgent( + any(), + any(), + lpCaptor.capture(), + ncCaptor.capture(), + anyInt(), + any(), + anyInt()); + verify(mIpSecSvc) + .addAddressToTunnelInterface( + eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(TEST_INTERNAL_ADDR), any()); + + final LinkProperties lp = lpCaptor.getValue(); + assertEquals(Collections.singletonList(TEST_INTERNAL_ADDR), lp.getLinkAddresses()); + assertEquals(Collections.singletonList(TEST_DNS_ADDR), lp.getDnsServers()); + + final NetworkCapabilities nc = ncCaptor.getValue(); + assertTrue(nc.hasTransport(TRANSPORT_CELLULAR)); + assertFalse(nc.hasTransport(TRANSPORT_WIFI)); + for (int cap : mConfig.getAllExposedCapabilities()) { + assertTrue(nc.hasCapability(cap)); + } + } + + @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(); + } +} diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java index 4ecd21503165..8643d8a2ea8a 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 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect @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/VcnGatewayConnectionRetryTimeoutStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java new file mode 100644 index 000000000000..3f2b47cd58fd --- /dev/null +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java @@ -0,0 +1,78 @@ +/* + * 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 org.junit.Assert.assertEquals; + +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.RetryTimeoutState */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class VcnGatewayConnectionRetryTimeoutStateTest extends VcnGatewayConnectionTestBase { + @Before + public void setUp() throws Exception { + super.setUp(); + + mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1); + mGatewayConnection.transitionTo(mGatewayConnection.mRetryTimeoutState); + mTestLooper.dispatchAll(); + } + + @Test + public void testNewNetworkTriggerRetry() throws Exception { + mGatewayConnection + .getUnderlyingNetworkTrackerCallback() + .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2); + mTestLooper.dispatchAll(); + + assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState()); + } + + @Test + public void testSameNetworkDoesNotTriggerRetry() throws Exception { + mGatewayConnection + .getUnderlyingNetworkTrackerCallback() + .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1); + mTestLooper.dispatchAll(); + + assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState()); + } + + @Test + public void testNullNetworkTriggersDisconnect() throws Exception { + mGatewayConnection + .getUnderlyingNetworkTrackerCallback() + .onSelectedUnderlyingNetworkChanged(null); + mTestLooper.dispatchAll(); + + assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState()); + } + + @Test + public void testTimeoutElapsingTriggersRetry() throws Exception { + mTestLooper.moveTimeForward(mConfig.getRetryIntervalsMs()[0]); + mTestLooper.dispatchAll(); + + assertEquals(mGatewayConnection.mConnectingState, 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 d741e5cf4b35..bc6bee28d14f 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 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 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.annotation.NonNull; -import android.content.Context; +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 @@ import java.util.UUID; /** 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 @@ public class VcnGatewayConnectionTest { 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); } - @Test - public void testBuildNetworkCapabilities() throws Exception { - final NetworkCapabilities caps = + 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()); + VcnGatewayConnectionConfigTest.buildTestConfig(), record); - for (int exposedCapability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) { - assertTrue(caps.hasCapability(exposedCapability)); + 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 testBuildNetworkCapabilitiesUnderlyingWifi() throws Exception { + verifyBuildNetworkCapabilitiesCommon(TRANSPORT_WIFI); + } + + @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 4d92fb9c42f2..d449eab494f6 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java @@ -27,8 +27,13 @@ import static org.mockito.Mockito.verify; import android.annotation.NonNull; import android.content.Context; +import android.net.ConnectivityManager; +import android.net.InetAddresses; +import android.net.IpSecConfig; import android.net.IpSecManager; +import android.net.IpSecTransform; import android.net.IpSecTunnelInterfaceResponse; +import android.net.LinkAddress; import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; @@ -40,15 +45,29 @@ 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 com.android.server.vcn.VcnGatewayConnection.VcnChildSessionCallback; import org.junit.Before; import org.mockito.ArgumentCaptor; +import java.net.InetAddress; +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 InetAddress TEST_DNS_ADDR = + InetAddresses.parseNumericAddress("2001:DB8:0:1::"); + protected static final LinkAddress TEST_INTERNAL_ADDR = + new LinkAddress(InetAddresses.parseNumericAddress("2001:DB8:0:2::"), 64); + + 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( @@ -63,15 +82,21 @@ public class VcnGatewayConnectionTestBase { 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; + @NonNull protected final ConnectivityManager mConnMgr; protected VcnIkeSession mMockIkeSession; protected VcnGatewayConnection mGatewayConnection; @@ -82,19 +107,24 @@ public class VcnGatewayConnectionTestBase { mVcnNetworkProvider = mock(VcnNetworkProvider.class); mVcnContext = mock(VcnContext.class); mConfig = VcnGatewayConnectionConfigTest.buildTestConfig(); + mGatewayStatusCallback = mock(VcnGatewayStatusCallback.class); mDeps = mock(VcnGatewayConnection.Dependencies.class); mUnderlyingNetworkTracker = mock(UnderlyingNetworkTracker.class); mIpSecSvc = mock(IpSecService.class); setupIpSecManager(mContext, mIpSecSvc); + mConnMgr = mock(ConnectivityManager.class); + VcnTestUtils.setupSystemService( + mContext, mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class); + doReturn(mContext).when(mVcnContext).getContext(); doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper(); doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider(); doReturn(mUnderlyingNetworkTracker) .when(mDeps) - .newUnderlyingNetworkTracker(any(), any(), any(), any()); + .newUnderlyingNetworkTracker(any(), any(), any(), any(), any()); } @Before @@ -109,7 +139,18 @@ public class VcnGatewayConnectionTestBase { mMockIkeSession = mock(VcnIkeSession.class); doReturn(mMockIkeSession).when(mDeps).newIkeSession(any(), any(), any(), any(), any()); - mGatewayConnection = new VcnGatewayConnection(mVcnContext, TEST_SUB_GRP, mConfig, mDeps); + 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() { @@ -119,10 +160,10 @@ public class VcnGatewayConnectionTestBase { return captor.getValue(); } - protected ChildSessionCallback getChildSessionCallback() { + protected VcnChildSessionCallback getChildSessionCallback() { ArgumentCaptor<ChildSessionCallback> captor = ArgumentCaptor.forClass(ChildSessionCallback.class); verify(mDeps).newIkeSession(any(), any(), any(), any(), captor.capture()); - return captor.getValue(); + return (VcnChildSessionCallback) 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 000000000000..66cbf84619ab --- /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(); + } +} diff --git a/tools/hiddenapi/generate_hiddenapi_lists.py b/tools/hiddenapi/generate_hiddenapi_lists.py deleted file mode 100755 index 28ff606d0381..000000000000 --- a/tools/hiddenapi/generate_hiddenapi_lists.py +++ /dev/null @@ -1,383 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Generate API lists for non-SDK API enforcement.""" -import argparse -from collections import defaultdict, namedtuple -import functools -import os -import re -import sys - -# Names of flags recognized by the `hiddenapi` tool. -FLAG_SDK = 'sdk' -FLAG_UNSUPPORTED = 'unsupported' -FLAG_BLOCKED = 'blocked' -FLAG_MAX_TARGET_O = 'max-target-o' -FLAG_MAX_TARGET_P = 'max-target-p' -FLAG_MAX_TARGET_Q = 'max-target-q' -FLAG_MAX_TARGET_R = 'max-target-r' -FLAG_CORE_PLATFORM_API = 'core-platform-api' -FLAG_PUBLIC_API = 'public-api' -FLAG_SYSTEM_API = 'system-api' -FLAG_TEST_API = 'test-api' - -# List of all known flags. -FLAGS_API_LIST = [ - FLAG_SDK, - FLAG_UNSUPPORTED, - FLAG_BLOCKED, - FLAG_MAX_TARGET_O, - FLAG_MAX_TARGET_P, - FLAG_MAX_TARGET_Q, - FLAG_MAX_TARGET_R, -] -ALL_FLAGS = FLAGS_API_LIST + [ - FLAG_CORE_PLATFORM_API, - FLAG_PUBLIC_API, - FLAG_SYSTEM_API, - FLAG_TEST_API, -] - -FLAGS_API_LIST_SET = set(FLAGS_API_LIST) -ALL_FLAGS_SET = set(ALL_FLAGS) - -# Option specified after one of FLAGS_API_LIST to indicate that -# only known and otherwise unassigned entries should be assign the -# given flag. -# For example, the max-target-P list is checked in as it was in P, -# but signatures have changes since then. The flag instructs this -# script to skip any entries which do not exist any more. -FLAG_IGNORE_CONFLICTS = "ignore-conflicts" - -# Option specified after one of FLAGS_API_LIST to express that all -# apis within a given set of packages should be assign the given flag. -FLAG_PACKAGES = "packages" - -# Option specified after one of FLAGS_API_LIST to indicate an extra -# tag that should be added to the matching APIs. -FLAG_TAG = "tag" - -# Regex patterns of fields/methods used in serialization. These are -# considered public API despite being hidden. -SERIALIZATION_PATTERNS = [ - r'readObject\(Ljava/io/ObjectInputStream;\)V', - r'readObjectNoData\(\)V', - r'readResolve\(\)Ljava/lang/Object;', - r'serialVersionUID:J', - r'serialPersistentFields:\[Ljava/io/ObjectStreamField;', - r'writeObject\(Ljava/io/ObjectOutputStream;\)V', - r'writeReplace\(\)Ljava/lang/Object;', -] - -# Single regex used to match serialization API. It combines all the -# SERIALIZATION_PATTERNS into a single regular expression. -SERIALIZATION_REGEX = re.compile(r'.*->(' + '|'.join(SERIALIZATION_PATTERNS) + r')$') - -# Predicates to be used with filter_apis. -HAS_NO_API_LIST_ASSIGNED = lambda api, flags: not FLAGS_API_LIST_SET.intersection(flags) -IS_SERIALIZATION = lambda api, flags: SERIALIZATION_REGEX.match(api) - - -class StoreOrderedOptions(argparse.Action): - """An argparse action that stores a number of option arguments in the order that - they were specified. - """ - def __call__(self, parser, args, values, option_string = None): - items = getattr(args, self.dest, None) - if items is None: - items = [] - items.append([option_string.lstrip('-'), values]) - setattr(args, self.dest, items) - -def get_args(): - """Parses command line arguments. - - Returns: - Namespace: dictionary of parsed arguments - """ - parser = argparse.ArgumentParser() - parser.add_argument('--output', required=True) - parser.add_argument('--csv', nargs='*', default=[], metavar='CSV_FILE', - help='CSV files to be merged into output') - - for flag in ALL_FLAGS: - parser.add_argument('--' + flag, dest='ordered_flags', metavar='TXT_FILE', - action=StoreOrderedOptions, help='lists of entries with flag "' + flag + '"') - parser.add_argument('--' + FLAG_IGNORE_CONFLICTS, dest='ordered_flags', nargs=0, - action=StoreOrderedOptions, help='Indicates that only known and otherwise unassigned ' - 'entries should be assign the given flag. Must follow a list of entries and applies ' - 'to the preceding such list.') - parser.add_argument('--' + FLAG_PACKAGES, dest='ordered_flags', nargs=0, - action=StoreOrderedOptions, help='Indicates that the previous list of entries ' - 'is a list of packages. All members in those packages will be given the flag. ' - 'Must follow a list of entries and applies to the preceding such list.') - parser.add_argument('--' + FLAG_TAG, dest='ordered_flags', nargs=1, - action=StoreOrderedOptions, help='Adds an extra tag to the previous list of entries. ' - 'Must follow a list of entries and applies to the preceding such list.') - - return parser.parse_args() - - -def read_lines(filename): - """Reads entire file and return it as a list of lines. - - Lines which begin with a hash are ignored. - - Args: - filename (string): Path to the file to read from. - - Returns: - Lines of the file as a list of string. - """ - with open(filename, 'r') as f: - lines = f.readlines(); - lines = filter(lambda line: not line.startswith('#'), lines) - lines = map(lambda line: line.strip(), lines) - return set(lines) - - -def write_lines(filename, lines): - """Writes list of lines into a file, overwriting the file if it exists. - - Args: - filename (string): Path to the file to be writting into. - lines (list): List of strings to write into the file. - """ - lines = map(lambda line: line + '\n', lines) - with open(filename, 'w') as f: - f.writelines(lines) - - -def extract_package(signature): - """Extracts the package from a signature. - - Args: - signature (string): JNI signature of a method or field. - - Returns: - The package name of the class containing the field/method. - """ - full_class_name = signature.split(";->")[0] - # Example: Landroid/hardware/radio/V1_2/IRadio$Proxy - if (full_class_name[0] != "L"): - raise ValueError("Expected to start with 'L': %s" % full_class_name) - full_class_name = full_class_name[1:] - # If full_class_name doesn't contain '/', then package_name will be ''. - package_name = full_class_name.rpartition("/")[0] - return package_name.replace('/', '.') - - -class FlagsDict: - def __init__(self): - self._dict_keyset = set() - self._dict = defaultdict(set) - - def _check_entries_set(self, keys_subset, source): - assert isinstance(keys_subset, set) - assert keys_subset.issubset(self._dict_keyset), ( - "Error: {} specifies signatures not present in code:\n" - "{}" - "Please visit go/hiddenapi for more information.").format( - source, "".join(map(lambda x: " " + str(x) + "\n", keys_subset - self._dict_keyset))) - - def _check_flags_set(self, flags_subset, source): - assert isinstance(flags_subset, set) - assert flags_subset.issubset(ALL_FLAGS_SET), ( - "Error processing: {}\n" - "The following flags were not recognized: \n" - "{}\n" - "Please visit go/hiddenapi for more information.").format( - source, "\n".join(flags_subset - ALL_FLAGS_SET)) - - def filter_apis(self, filter_fn): - """Returns APIs which match a given predicate. - - This is a helper function which allows to filter on both signatures (keys) and - flags (values). The built-in filter() invokes the lambda only with dict's keys. - - Args: - filter_fn : Function which takes two arguments (signature/flags) and returns a boolean. - - Returns: - A set of APIs which match the predicate. - """ - return set(filter(lambda x: filter_fn(x, self._dict[x]), self._dict_keyset)) - - def get_valid_subset_of_unassigned_apis(self, api_subset): - """Sanitizes a key set input to only include keys which exist in the dictionary - and have not been assigned any API list flags. - - Args: - entries_subset (set/list): Key set to be sanitized. - - Returns: - Sanitized key set. - """ - assert isinstance(api_subset, set) - return api_subset.intersection(self.filter_apis(HAS_NO_API_LIST_ASSIGNED)) - - def generate_csv(self): - """Constructs CSV entries from a dictionary. - - Old versions of flags are used to generate the file. - - Returns: - List of lines comprising a CSV file. See "parse_and_merge_csv" for format description. - """ - lines = [] - for api in self._dict: - flags = sorted(self._dict[api]) - lines.append(",".join([api] + flags)) - return sorted(lines) - - def parse_and_merge_csv(self, csv_lines, source = "<unknown>"): - """Parses CSV entries and merges them into a given dictionary. - - The expected CSV format is: - <api signature>,<flag1>,<flag2>,...,<flagN> - - Args: - csv_lines (list of strings): Lines read from a CSV file. - source (string): Origin of `csv_lines`. Will be printed in error messages. - - Throws: - AssertionError if parsed flags are invalid. - """ - # Split CSV lines into arrays of values. - csv_values = [ line.split(',') for line in csv_lines ] - - # Update the full set of API signatures. - self._dict_keyset.update([ csv[0] for csv in csv_values ]) - - # Check that all flags are known. - csv_flags = set() - for csv in csv_values: - csv_flags.update(csv[1:]) - self._check_flags_set(csv_flags, source) - - # Iterate over all CSV lines, find entry in dict and append flags to it. - for csv in csv_values: - flags = csv[1:] - if (FLAG_PUBLIC_API in flags) or (FLAG_SYSTEM_API in flags): - flags.append(FLAG_SDK) - self._dict[csv[0]].update(flags) - - def assign_flag(self, flag, apis, source="<unknown>", tag = None): - """Assigns a flag to given subset of entries. - - Args: - flag (string): One of ALL_FLAGS. - apis (set): Subset of APIs to receive the flag. - source (string): Origin of `entries_subset`. Will be printed in error messages. - - Throws: - AssertionError if parsed API signatures of flags are invalid. - """ - # Check that all APIs exist in the dict. - self._check_entries_set(apis, source) - - # Check that the flag is known. - self._check_flags_set(set([ flag ]), source) - - # Iterate over the API subset, find each entry in dict and assign the flag to it. - for api in apis: - self._dict[api].add(flag) - if tag: - self._dict[api].add(tag) - - -FlagFile = namedtuple('FlagFile', ('flag', 'file', 'ignore_conflicts', 'packages', 'tag')) - -def parse_ordered_flags(ordered_flags): - r = [] - currentflag, file, ignore_conflicts, packages, tag = None, None, False, False, None - for flag_value in ordered_flags: - flag, value = flag_value[0], flag_value[1] - if flag in ALL_FLAGS_SET: - if currentflag: - r.append(FlagFile(currentflag, file, ignore_conflicts, packages, tag)) - ignore_conflicts, packages, tag = False, False, None - currentflag = flag - file = value - else: - if currentflag is None: - raise argparse.ArgumentError('--%s is only allowed after one of %s' % ( - flag, ' '.join(['--%s' % f for f in ALL_FLAGS_SET]))) - if flag == FLAG_IGNORE_CONFLICTS: - ignore_conflicts = True - elif flag == FLAG_PACKAGES: - packages = True - elif flag == FLAG_TAG: - tag = value[0] - - - if currentflag: - r.append(FlagFile(currentflag, file, ignore_conflicts, packages, tag)) - return r - - -def main(argv): - # Parse arguments. - args = vars(get_args()) - flagfiles = parse_ordered_flags(args['ordered_flags']) - - # Initialize API->flags dictionary. - flags = FlagsDict() - - # Merge input CSV files into the dictionary. - # Do this first because CSV files produced by parsing API stubs will - # contain the full set of APIs. Subsequent additions from text files - # will be able to detect invalid entries, and/or filter all as-yet - # unassigned entries. - for filename in args["csv"]: - flags.parse_and_merge_csv(read_lines(filename), filename) - - # Combine inputs which do not require any particular order. - # (1) Assign serialization API to SDK. - flags.assign_flag(FLAG_SDK, flags.filter_apis(IS_SERIALIZATION)) - - # (2) Merge text files with a known flag into the dictionary. - for info in flagfiles: - if (not info.ignore_conflicts) and (not info.packages): - flags.assign_flag(info.flag, read_lines(info.file), info.file, info.tag) - - # Merge text files where conflicts should be ignored. - # This will only assign the given flag if: - # (a) the entry exists, and - # (b) it has not been assigned any other flag. - # Because of (b), this must run after all strict assignments have been performed. - for info in flagfiles: - if info.ignore_conflicts: - valid_entries = flags.get_valid_subset_of_unassigned_apis(read_lines(info.file)) - flags.assign_flag(info.flag, valid_entries, filename, info.tag) - - # All members in the specified packages will be assigned the appropriate flag. - for info in flagfiles: - if info.packages: - packages_needing_list = set(read_lines(info.file)) - should_add_signature_to_list = lambda sig,lists: extract_package( - sig) in packages_needing_list and not lists - valid_entries = flags.filter_apis(should_add_signature_to_list) - flags.assign_flag(info.flag, valid_entries, info.file, info.tag) - - # Mark all remaining entries as blocked. - flags.assign_flag(FLAG_BLOCKED, flags.filter_apis(HAS_NO_API_LIST_ASSIGNED)) - - # Write output. - write_lines(args["output"], flags.generate_csv()) - -if __name__ == "__main__": - main(sys.argv) diff --git a/tools/hiddenapi/generate_hiddenapi_lists_test.py b/tools/hiddenapi/generate_hiddenapi_lists_test.py deleted file mode 100755 index 82d117fbbbab..000000000000 --- a/tools/hiddenapi/generate_hiddenapi_lists_test.py +++ /dev/null @@ -1,104 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the 'License'); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an 'AS IS' BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Unit tests for Hidden API list generation.""" -import unittest -from generate_hiddenapi_lists import * - -class TestHiddenapiListGeneration(unittest.TestCase): - - def test_filter_apis(self): - # Initialize flags so that A and B are put on the whitelist and - # C, D, E are left unassigned. Try filtering for the unassigned ones. - flags = FlagsDict() - flags.parse_and_merge_csv(['A,' + FLAG_SDK, 'B,' + FLAG_SDK, - 'C', 'D', 'E']) - filter_set = flags.filter_apis(lambda api, flags: not flags) - self.assertTrue(isinstance(filter_set, set)) - self.assertEqual(filter_set, set([ 'C', 'D', 'E' ])) - - def test_get_valid_subset_of_unassigned_keys(self): - # Create flags where only A is unassigned. - flags = FlagsDict() - flags.parse_and_merge_csv(['A,' + FLAG_SDK, 'B', 'C']) - flags.assign_flag(FLAG_UNSUPPORTED, set(['C'])) - self.assertEqual(flags.generate_csv(), - [ 'A,' + FLAG_SDK, 'B', 'C,' + FLAG_UNSUPPORTED ]) - - # Check three things: - # (1) B is selected as valid unassigned - # (2) A is not selected because it is assigned 'whitelist' - # (3) D is not selected because it is not a valid key - self.assertEqual( - flags.get_valid_subset_of_unassigned_apis(set(['A', 'B', 'D'])), set([ 'B' ])) - - def test_parse_and_merge_csv(self): - flags = FlagsDict() - - # Test empty CSV entry. - self.assertEqual(flags.generate_csv(), []) - - # Test new additions. - flags.parse_and_merge_csv([ - 'A,' + FLAG_UNSUPPORTED, - 'B,' + FLAG_BLOCKED + ',' + FLAG_MAX_TARGET_O, - 'C,' + FLAG_SDK + ',' + FLAG_SYSTEM_API, - 'D,' + FLAG_UNSUPPORTED + ',' + FLAG_TEST_API, - 'E,' + FLAG_BLOCKED + ',' + FLAG_TEST_API, - ]) - self.assertEqual(flags.generate_csv(), [ - 'A,' + FLAG_UNSUPPORTED, - 'B,' + FLAG_BLOCKED + "," + FLAG_MAX_TARGET_O, - 'C,' + FLAG_SYSTEM_API + ',' + FLAG_SDK, - 'D,' + FLAG_UNSUPPORTED + ',' + FLAG_TEST_API, - 'E,' + FLAG_BLOCKED + ',' + FLAG_TEST_API, - ]) - - # Test unknown flag. - with self.assertRaises(AssertionError): - flags.parse_and_merge_csv([ 'Z,foo' ]) - - def test_assign_flag(self): - flags = FlagsDict() - flags.parse_and_merge_csv(['A,' + FLAG_SDK, 'B']) - - # Test new additions. - flags.assign_flag(FLAG_UNSUPPORTED, set([ 'A', 'B' ])) - self.assertEqual(flags.generate_csv(), - [ 'A,' + FLAG_UNSUPPORTED + "," + FLAG_SDK, 'B,' + FLAG_UNSUPPORTED ]) - - # Test invalid API signature. - with self.assertRaises(AssertionError): - flags.assign_flag(FLAG_SDK, set([ 'C' ])) - - # Test invalid flag. - with self.assertRaises(AssertionError): - flags.assign_flag('foo', set([ 'A' ])) - - def test_extract_package(self): - signature = 'Lcom/foo/bar/Baz;->method1()Lcom/bar/Baz;' - expected_package = 'com.foo.bar' - self.assertEqual(extract_package(signature), expected_package) - - signature = 'Lcom/foo1/bar/MyClass;->method2()V' - expected_package = 'com.foo1.bar' - self.assertEqual(extract_package(signature), expected_package) - - signature = 'Lcom/foo_bar/baz/MyClass;->method3()V' - expected_package = 'com.foo_bar.baz' - self.assertEqual(extract_package(signature), expected_package) - -if __name__ == '__main__': - unittest.main() diff --git a/tools/hiddenapi/merge_csv.py b/tools/hiddenapi/merge_csv.py deleted file mode 100755 index 6a5b0e1347b2..000000000000 --- a/tools/hiddenapi/merge_csv.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" -Merge multiple CSV files, possibly with different columns. -""" - -import argparse -import csv -import io - -from zipfile import ZipFile - -args_parser = argparse.ArgumentParser(description='Merge given CSV files into a single one.') -args_parser.add_argument('--header', help='Comma separated field names; ' - 'if missing determines the header from input files.') -args_parser.add_argument('--zip_input', help='ZIP archive with all CSV files to merge.') -args_parser.add_argument('--output', help='Output file for merged CSV.', - default='-', type=argparse.FileType('w')) -args_parser.add_argument('files', nargs=argparse.REMAINDER) -args = args_parser.parse_args() - - -def dict_reader(input): - return csv.DictReader(input, delimiter=',', quotechar='|') - - -if args.zip_input and len(args.files) > 0: - raise ValueError('Expecting either a single ZIP with CSV files' - ' or a list of CSV files as input; not both.') - -csv_readers = [] -if len(args.files) > 0: - for file in args.files: - csv_readers.append(dict_reader(open(file, 'r'))) -elif args.zip_input: - with ZipFile(args.zip_input) as zip: - for entry in zip.namelist(): - if entry.endswith('.uau'): - csv_readers.append(dict_reader(io.TextIOWrapper(zip.open(entry, 'r')))) - -headers = set() -if args.header: - fieldnames = args.header.split(',') -else: - # Build union of all columns from source files: - for reader in csv_readers: - headers = headers.union(reader.fieldnames) - fieldnames = sorted(headers) - -# Concatenate all files to output: -writer = csv.DictWriter(args.output, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL, - dialect='unix', fieldnames=fieldnames) -writer.writeheader() -for reader in csv_readers: - for row in reader: - writer.writerow(row) |