diff options
123 files changed, 5051 insertions, 1403 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index 02fee102b4c3..59a1cbc38216 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -13,20 +13,43 @@ // limitations under the License. aconfig_srcjars = [ + // !!! KEEP THIS LIST ALPHABETICAL !!! + ":aconfig_mediacodec_flags_java_lib{.generated_srcjars}", ":android.content.pm.flags-aconfig-java{.generated_srcjars}", ":android.content.res.flags-aconfig-java{.generated_srcjars}", ":android.hardware.biometrics.flags-aconfig-java{.generated_srcjars}", + ":android.media.playback.flags-aconfig-java{.generated_srcjars}", ":android.net.vcn.flags-aconfig-java{.generated_srcjars}", ":android.nfc.flags-aconfig-java{.generated_srcjars}", ":android.os.flags-aconfig-java{.generated_srcjars}", ":android.security.flags-aconfig-java{.generated_srcjars}", ":com.android.hardware.camera2-aconfig-java{.generated_srcjars}", - ":com.android.window.flags.window-aconfig-java{.generated_srcjars}", ":com.android.hardware.input-aconfig-java{.generated_srcjars}", - ":com.android.text.flags-aconfig-java{.generated_srcjars}", ":com.android.net.flags-aconfig-java{.generated_srcjars}", + ":com.android.text.flags-aconfig-java{.generated_srcjars}", + ":com.android.window.flags.window-aconfig-java{.generated_srcjars}", + // !!! KEEP THIS LIST ALPHABETICAL !!! ] +stubs_defaults { + name: "framework-minus-apex-aconfig-declarations", + aconfig_declarations: [ + "android.content.pm.flags-aconfig", + "android.content.res.flags-aconfig", + "android.hardware.biometrics.flags-aconfig", + "android.media.playback.flags-aconfig", + "android.net.vcn.flags-aconfig", + "android.nfc.flags-aconfig", + "android.os.flags-aconfig", + "android.security.flags-aconfig", + "com.android.hardware.camera2-aconfig", + "com.android.hardware.input.input-aconfig", + "com.android.window.flags.window-aconfig", + "com.android.text.flags-aconfig", + "com.android.net.flags-aconfig", + ], +} + filegroup { name: "framework-minus-apex-aconfig-srcjars", srcs: aconfig_srcjars, diff --git a/Android.bp b/Android.bp index 5f02a15ad185..2113e4b85248 100644 --- a/Android.bp +++ b/Android.bp @@ -205,7 +205,6 @@ java_library { "apex_aidl_interface-java", "packagemanager_aidl-java", "framework-protos", - "libtombstone_proto_java", "updatable-driver-protos", "ota_metadata_proto_java", "android.hidl.base-V1.0-java", diff --git a/ProtoLibraries.bp b/ProtoLibraries.bp index d03bbd249b00..e7adf203334e 100644 --- a/ProtoLibraries.bp +++ b/ProtoLibraries.bp @@ -34,6 +34,7 @@ gensrcs { ":ipconnectivity-proto-src", ":libstats_atom_enum_protos", ":libstats_atom_message_protos", + ":libtombstone_proto-src", "core/proto/**/*.proto", "libs/incident/**/*.proto", ], diff --git a/STABILITY_OWNERS b/STABILITY_OWNERS deleted file mode 100644 index a7ecb4dfdd44..000000000000 --- a/STABILITY_OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -gaillard@google.com - diff --git a/api/Android.bp b/api/Android.bp index 00841264c0f1..7fb427eb715a 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -69,8 +69,40 @@ genrule { visibility: ["//visibility:public"], } +soong_config_module_type { + name: "enable_crashrecovery_module", + module_type: "combined_apis_defaults", + config_namespace: "ANDROID", + bool_variables: ["release_crashrecovery_module"], + properties: [ + "bootclasspath", + "system_server_classpath", + ], +} + +soong_config_bool_variable { + name: "release_crashrecovery_module", +} + +enable_crashrecovery_module { + name: "crashrecovery_module_defaults", + soong_config_variables: { + release_crashrecovery_module: { + bootclasspath: [ + "framework-crashrecovery", + ], + system_server_classpath: [ + "service-crashrecovery", + ], + }, + }, +} + combined_apis { name: "frameworks-base-api", + defaults: [ + "crashrecovery_module_defaults", + ], bootclasspath: [ "android.net.ipsec.ike", "art.module.public.api", @@ -278,6 +310,7 @@ packages_to_document = [ // classpath (or sources) somehow. stubs_defaults { name: "android-non-updatable-stubs-defaults", + defaults: ["framework-minus-apex-aconfig-declarations"], srcs: [":android-non-updatable-stub-sources"], sdk_version: "none", system_modules: "none", diff --git a/api/ApiDocs.bp b/api/ApiDocs.bp index 5744bdfd4b28..d7e25834905c 100644 --- a/api/ApiDocs.bp +++ b/api/ApiDocs.bp @@ -171,6 +171,7 @@ doc_defaults { "-federationapi AndroidX $(location :current-androidx-api)", // doclava contains checks for a few issues that are have been migrated to metalava. // disable them in doclava, to avoid mistriggering or double triggering. + "-hide 101", // TODO: turn Lint 101 back into an error again "-hide 111", // HIDDEN_SUPERCLASS "-hide 113", // DEPRECATION_MISMATCH "-hide 125", // REQUIRES_PERMISSION diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp index ef1fa6097056..ec4fc46a76fb 100644 --- a/api/StubLibraries.bp +++ b/api/StubLibraries.bp @@ -239,6 +239,10 @@ java_defaults { name: "android-non-updatable_from_source_defaults", libs: ["stub-annotations"], static_libs: ["framework-res-package-jar"], // Export package of framework-res +} + +java_defaults { + name: "android-non-updatable_exportable_from_source_defaults", dist: { targets: ["sdk"], tag: ".jar", @@ -265,6 +269,14 @@ java_library { } java_library { + name: "android-non-updatable.stubs.exportable", + defaults: ["android-non-updatable_defaults"], + static_libs: [ + "android-non-updatable.stubs.exportable.from-source", + ], +} + +java_library { name: "android-non-updatable.stubs.system", defaults: ["android-non-updatable_defaults"], static_libs: [ @@ -283,6 +295,14 @@ java_library { } java_library { + name: "android-non-updatable.stubs.exportable.system", + defaults: ["android-non-updatable_defaults"], + static_libs: [ + "android-non-updatable.stubs.exportable.system.from-source", + ], +} + +java_library { name: "android-non-updatable.stubs.module_lib", defaults: ["android-non-updatable_defaults"], static_libs: [ @@ -301,6 +321,14 @@ java_library { } java_library { + name: "android-non-updatable.stubs.exportable.module_lib", + defaults: ["android-non-updatable_defaults"], + static_libs: [ + "android-non-updatable.stubs.exportable.module_lib.from-source", + ], +} + +java_library { name: "android-non-updatable.stubs.test", defaults: ["android-non-updatable_defaults"], static_libs: [ @@ -319,6 +347,14 @@ java_library { } java_library { + name: "android-non-updatable.stubs.exportable.test", + defaults: ["android-non-updatable_defaults"], + static_libs: [ + "android-non-updatable.stubs.exportable.test.from-source", + ], +} + +java_library { name: "android-non-updatable.stubs.from-source", defaults: [ "android-non-updatable_defaults", @@ -326,6 +362,17 @@ java_library { ], srcs: [":api-stubs-docs-non-updatable"], libs: ["all-modules-public-stubs"], +} + +java_library { + name: "android-non-updatable.stubs.exportable.from-source", + defaults: [ + "android-non-updatable_defaults", + "android-non-updatable_from_source_defaults", + "android-non-updatable_exportable_from_source_defaults", + ], + srcs: [":api-stubs-docs-non-updatable{.exportable}"], + libs: ["all-modules-public-stubs"], dist: { dir: "apistubs/android/public", }, @@ -339,6 +386,17 @@ java_library { ], srcs: [":system-api-stubs-docs-non-updatable"], libs: ["all-modules-system-stubs"], +} + +java_library { + name: "android-non-updatable.stubs.exportable.system.from-source", + defaults: [ + "android-non-updatable_defaults", + "android-non-updatable_from_source_defaults", + "android-non-updatable_exportable_from_source_defaults", + ], + srcs: [":system-api-stubs-docs-non-updatable{.exportable}"], + libs: ["all-modules-system-stubs"], dist: { dir: "apistubs/android/system", }, @@ -352,6 +410,17 @@ java_library { ], srcs: [":module-lib-api-stubs-docs-non-updatable"], libs: non_updatable_api_deps_on_modules, +} + +java_library { + name: "android-non-updatable.stubs.exportable.module_lib.from-source", + defaults: [ + "android-non-updatable_defaults", + "android-non-updatable_from_source_defaults", + "android-non-updatable_exportable_from_source_defaults", + ], + srcs: [":module-lib-api-stubs-docs-non-updatable{.exportable}"], + libs: non_updatable_api_deps_on_modules, dist: { dir: "apistubs/android/module-lib", }, @@ -365,6 +434,17 @@ java_library { ], srcs: [":test-api-stubs-docs-non-updatable"], libs: ["all-modules-system-stubs"], +} + +java_library { + name: "android-non-updatable.stubs.exportable.test.from-source", + defaults: [ + "android-non-updatable_defaults", + "android-non-updatable_from_source_defaults", + "android-non-updatable_exportable_from_source_defaults", + ], + srcs: [":test-api-stubs-docs-non-updatable{.exportable}"], + libs: ["all-modules-system-stubs"], dist: { dir: "apistubs/android/test", }, @@ -462,6 +542,16 @@ java_library { } java_library { + name: "android_stubs_current_exportable.from-source", + static_libs: [ + "all-modules-public-stubs-exportable", + "android-non-updatable.stubs.exportable", + "private-stub-annotations-jar", + ], + defaults: ["android.jar_defaults"], +} + +java_library { name: "android_system_stubs_current.from-source", static_libs: [ "all-modules-system-stubs", @@ -470,6 +560,19 @@ java_library { ], defaults: [ "android.jar_defaults", + ], + visibility: ["//frameworks/base/services"], +} + +java_library { + name: "android_system_stubs_current_exportable.from-source", + static_libs: [ + "all-modules-system-stubs-exportable", + "android-non-updatable.stubs.exportable.system", + "private-stub-annotations-jar", + ], + defaults: [ + "android.jar_defaults", "android_stubs_dists_default", ], dist: { @@ -498,6 +601,23 @@ java_library { ], defaults: [ "android.jar_defaults", + ], + visibility: ["//frameworks/base/services"], +} + +java_library { + name: "android_test_stubs_current_exportable.from-source", + static_libs: [ + // Updatable modules do not have test APIs, but we want to include their SystemApis, like we + // include the SystemApi of framework-non-updatable-sources. + "all-updatable-modules-system-stubs-exportable", + // Non-updatable modules on the other hand can have test APIs, so include their test-stubs. + "all-non-updatable-modules-test-stubs-exportable", + "android-non-updatable.stubs.exportable.test", + "private-stub-annotations-jar", + ], + defaults: [ + "android.jar_defaults", "android_stubs_dists_default", ], dist: { @@ -505,6 +625,7 @@ java_library { }, } +// This module does not need to be copied to dist java_library { name: "android_test_frameworks_core_stubs_current.from-source", static_libs: [ @@ -513,24 +634,34 @@ java_library { ], defaults: [ "android.jar_defaults", - "android_stubs_dists_default", ], - dist: { - dir: "apistubs/android/test-core", - }, + visibility: ["//frameworks/base/services"], } java_library { name: "android_module_lib_stubs_current.from-source", defaults: [ "android.jar_defaults", - "android_stubs_dists_default", ], static_libs: [ "android-non-updatable.stubs.module_lib", "art.module.public.api.stubs.module_lib", "i18n.module.public.api.stubs", ], + visibility: ["//frameworks/base/services"], +} + +java_library { + name: "android_module_lib_stubs_current_exportable.from-source", + defaults: [ + "android.jar_defaults", + "android_stubs_dists_default", + ], + static_libs: [ + "android-non-updatable.stubs.exportable.module_lib", + "art.module.public.api.stubs.exportable.module_lib", + "i18n.module.public.api.stubs.exportable", + ], dist: { dir: "apistubs/android/module-lib", }, @@ -540,13 +671,26 @@ java_library { name: "android_system_server_stubs_current.from-source", defaults: [ "android.jar_defaults", - "android_stubs_dists_default", ], srcs: [":services-non-updatable-stubs"], installable: false, static_libs: [ "android_module_lib_stubs_current.from-source", ], + visibility: ["//frameworks/base/services"], +} + +java_library { + name: "android_system_server_stubs_current_exportable.from-source", + defaults: [ + "android.jar_defaults", + "android_stubs_dists_default", + ], + srcs: [":services-non-updatable-stubs{.exportable}"], + installable: false, + static_libs: [ + "android_module_lib_stubs_current_exportable.from-source", + ], dist: { dir: "apistubs/android/system-server", }, diff --git a/api/api.go b/api/api.go index 2668999c572e..a632582ce76d 100644 --- a/api/api.go +++ b/api/api.go @@ -64,6 +64,7 @@ type CombinedApisProperties struct { type CombinedApis struct { android.ModuleBase + android.DefaultableModuleBase properties CombinedApisProperties } @@ -74,6 +75,7 @@ func init() { func registerBuildComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("combined_apis", combinedApisModuleFactory) + ctx.RegisterModuleType("combined_apis_defaults", CombinedApisModuleDefaultsFactory) } var PrepareForCombinedApisTest = android.FixtureRegisterWithContext(registerBuildComponents) @@ -203,6 +205,15 @@ func createMergedPublicStubs(ctx android.LoadHookContext, modules []string) { ctx.CreateModule(java.LibraryFactory, &props) } +func createMergedPublicExportableStubs(ctx android.LoadHookContext, modules []string) { + props := libraryProps{} + props.Name = proptools.StringPtr("all-modules-public-stubs-exportable") + props.Static_libs = transformArray(modules, "", ".stubs.exportable") + props.Sdk_version = proptools.StringPtr("module_current") + props.Visibility = []string{"//frameworks/base"} + ctx.CreateModule(java.LibraryFactory, &props) +} + func createMergedSystemStubs(ctx android.LoadHookContext, modules []string) { // First create the all-updatable-modules-system-stubs { @@ -227,6 +238,30 @@ func createMergedSystemStubs(ctx android.LoadHookContext, modules []string) { } } +func createMergedSystemExportableStubs(ctx android.LoadHookContext, modules []string) { + // First create the all-updatable-modules-system-stubs + { + updatable_modules := removeAll(modules, non_updatable_modules) + props := libraryProps{} + props.Name = proptools.StringPtr("all-updatable-modules-system-stubs-exportable") + props.Static_libs = transformArray(updatable_modules, "", ".stubs.exportable.system") + props.Sdk_version = proptools.StringPtr("module_current") + props.Visibility = []string{"//frameworks/base"} + ctx.CreateModule(java.LibraryFactory, &props) + } + // Now merge all-updatable-modules-system-stubs and stubs from non-updatable modules + // into all-modules-system-stubs. + { + props := libraryProps{} + props.Name = proptools.StringPtr("all-modules-system-stubs-exportable") + props.Static_libs = transformArray(non_updatable_modules, "", ".stubs.exportable.system") + props.Static_libs = append(props.Static_libs, "all-updatable-modules-system-stubs-exportable") + props.Sdk_version = proptools.StringPtr("module_current") + props.Visibility = []string{"//frameworks/base"} + ctx.CreateModule(java.LibraryFactory, &props) + } +} + func createMergedTestStubsForNonUpdatableModules(ctx android.LoadHookContext) { props := libraryProps{} props.Name = proptools.StringPtr("all-non-updatable-modules-test-stubs") @@ -236,6 +271,15 @@ func createMergedTestStubsForNonUpdatableModules(ctx android.LoadHookContext) { ctx.CreateModule(java.LibraryFactory, &props) } +func createMergedTestExportableStubsForNonUpdatableModules(ctx android.LoadHookContext) { + props := libraryProps{} + props.Name = proptools.StringPtr("all-non-updatable-modules-test-stubs-exportable") + props.Static_libs = transformArray(non_updatable_modules, "", ".stubs.exportable.test") + props.Sdk_version = proptools.StringPtr("module_current") + props.Visibility = []string{"//frameworks/base"} + ctx.CreateModule(java.LibraryFactory, &props) +} + func createMergedFrameworkImpl(ctx android.LoadHookContext, modules []string) { // This module is for the "framework-all" module, which should not include the core libraries. modules = removeAll(modules, core_libraries_modules) @@ -266,6 +310,19 @@ func createMergedFrameworkImpl(ctx android.LoadHookContext, modules []string) { } } +func createMergedFrameworkModuleLibExportableStubs(ctx android.LoadHookContext, modules []string) { + // The user of this module compiles against the "core" SDK and against non-updatable modules, + // so remove to avoid dupes. + modules = removeAll(modules, core_libraries_modules) + modules = removeAll(modules, non_updatable_modules) + props := libraryProps{} + props.Name = proptools.StringPtr("framework-updatable-stubs-module_libs_api-exportable") + props.Static_libs = transformArray(modules, "", ".stubs.exportable.module_lib") + props.Sdk_version = proptools.StringPtr("module_current") + props.Visibility = []string{"//frameworks/base"} + ctx.CreateModule(java.LibraryFactory, &props) +} + func createMergedFrameworkModuleLibStubs(ctx android.LoadHookContext, modules []string) { // The user of this module compiles against the "core" SDK and against non-updatable modules, // so remove to avoid dupes. @@ -381,6 +438,27 @@ func createFullApiLibraries(ctx android.LoadHookContext) { } } +func createFullExportableApiLibraries(ctx android.LoadHookContext) { + javaLibraryNames := []string{ + "android_stubs_current_exportable", + "android_system_stubs_current_exportable", + "android_test_stubs_current_exportable", + "android_module_lib_stubs_current_exportable", + "android_system_server_stubs_current_exportable", + } + + for _, libraryName := range javaLibraryNames { + props := libraryProps{} + props.Name = proptools.StringPtr(libraryName) + staticLib := libraryName + ".from-source" + props.Static_libs = []string{staticLib} + props.Defaults = []string{"android.jar_defaults"} + props.Visibility = []string{"//visibility:public"} + + ctx.CreateModule(java.LibraryFactory, &props) + } +} + func (a *CombinedApis) createInternalModules(ctx android.LoadHookContext) { bootclasspath := a.properties.Bootclasspath system_server_classpath := a.properties.System_server_classpath @@ -396,6 +474,11 @@ func (a *CombinedApis) createInternalModules(ctx android.LoadHookContext) { createMergedFrameworkModuleLibStubs(ctx, bootclasspath) createMergedFrameworkImpl(ctx, bootclasspath) + createMergedPublicExportableStubs(ctx, bootclasspath) + createMergedSystemExportableStubs(ctx, bootclasspath) + createMergedTestExportableStubsForNonUpdatableModules(ctx) + createMergedFrameworkModuleLibExportableStubs(ctx, bootclasspath) + createMergedAnnotationsFilegroups(ctx, bootclasspath, system_server_classpath) createPublicStubsSourceFilegroup(ctx, bootclasspath) @@ -403,12 +486,15 @@ func (a *CombinedApis) createInternalModules(ctx android.LoadHookContext) { createApiContributionDefaults(ctx, bootclasspath) createFullApiLibraries(ctx) + + createFullExportableApiLibraries(ctx) } func combinedApisModuleFactory() android.Module { module := &CombinedApis{} module.AddProperties(&module.properties) android.InitAndroidModule(module) + android.InitDefaultableModule(module) android.AddLoadHook(module, func(ctx android.LoadHookContext) { module.createInternalModules(ctx) }) return module } @@ -445,3 +531,16 @@ func remove(s []string, v string) []string { } return s2 } + +// Defaults +type CombinedApisModuleDefaults struct { + android.ModuleBase + android.DefaultsModuleBase +} + +func CombinedApisModuleDefaultsFactory() android.Module { + module := &CombinedApisModuleDefaults{} + module.AddProperties(&CombinedApisProperties{}) + android.InitDefaultsModule(module) + return module +} diff --git a/boot/Android.bp b/boot/Android.bp index 8a3d35e2d0eb..4b3ad655d36b 100644 --- a/boot/Android.bp +++ b/boot/Android.bp @@ -29,6 +29,7 @@ soong_config_module_type { config_namespace: "AUTO", bool_variables: [ "car_bootclasspath_fragment", + "release_crashrecovery_module", ], properties: [ "fragments", @@ -155,6 +156,15 @@ custom_platform_bootclasspath { }, ], }, + release_crashrecovery_module: { + fragments: [ + // only used when crashrecovery is enabled + { + apex: "com.android.crashrecovery", + module: "com.android.crashrecovery-bootclasspath-fragment", + }, + ], + }, }, // Additional information needed by hidden api processing. diff --git a/config/preloaded-classes b/config/preloaded-classes index fd4e3dfcaf95..0a77877a9e79 100644 --- a/config/preloaded-classes +++ b/config/preloaded-classes @@ -6265,8 +6265,6 @@ android.os.VibratorInfo$FrequencyProfile$1 android.os.VibratorInfo$FrequencyProfile android.os.VibratorInfo android.os.VibratorManager -android.os.VintfObject -android.os.VintfRuntimeInfo android.os.WorkSource$1 android.os.WorkSource$WorkChain$1 android.os.WorkSource$WorkChain diff --git a/core/api/current.txt b/core/api/current.txt index 48f58c09871d..e26632a72183 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -193,6 +193,7 @@ package android { field public static final String MANAGE_DEVICE_POLICY_SYSTEM_APPS = "android.permission.MANAGE_DEVICE_POLICY_SYSTEM_APPS"; field public static final String MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS = "android.permission.MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS"; field public static final String MANAGE_DEVICE_POLICY_SYSTEM_UPDATES = "android.permission.MANAGE_DEVICE_POLICY_SYSTEM_UPDATES"; + field @FlaggedApi("com.android.net.thread.flags.thread_user_restriction_enabled") public static final String MANAGE_DEVICE_POLICY_THREAD_NETWORK = "android.permission.MANAGE_DEVICE_POLICY_THREAD_NETWORK"; field public static final String MANAGE_DEVICE_POLICY_TIME = "android.permission.MANAGE_DEVICE_POLICY_TIME"; field public static final String MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING = "android.permission.MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING"; field public static final String MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER = "android.permission.MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER"; @@ -16384,7 +16385,6 @@ package android.graphics { } public final class RecordingCanvas extends android.graphics.Canvas { - method public final void drawMesh(@NonNull android.graphics.Mesh, android.graphics.BlendMode, @NonNull android.graphics.Paint); } public final class Rect implements android.os.Parcelable { @@ -23603,6 +23603,8 @@ package android.media { field public static final String KEY_AUDIO_SESSION_ID = "audio-session-id"; field public static final String KEY_BITRATE_MODE = "bitrate-mode"; field public static final String KEY_BIT_RATE = "bitrate"; + field @FlaggedApi("com.android.media.codec.flags.large_audio_frame") public static final String KEY_BUFFER_BATCH_MAX_OUTPUT_SIZE = "buffer-batch-max-output-size"; + field @FlaggedApi("com.android.media.codec.flags.large_audio_frame") public static final String KEY_BUFFER_BATCH_THRESHOLD_OUTPUT_SIZE = "buffer-batch-threshold-output-size"; field public static final String KEY_CAPTION_SERVICE_NUMBER = "caption-service-number"; field public static final String KEY_CAPTURE_RATE = "capture-rate"; field public static final String KEY_CHANNEL_COUNT = "channel-count"; @@ -23631,6 +23633,7 @@ package android.media { field public static final String KEY_HDR10_PLUS_INFO = "hdr10-plus-info"; field public static final String KEY_HDR_STATIC_INFO = "hdr-static-info"; field public static final String KEY_HEIGHT = "height"; + field @FlaggedApi("com.android.media.codec.flags.codec_importance") public static final String KEY_IMPORTANCE = "importance"; field public static final String KEY_INTRA_REFRESH_PERIOD = "intra-refresh-period"; field public static final String KEY_IS_ADTS = "is-adts"; field public static final String KEY_IS_AUTOSELECT = "is-autoselect"; diff --git a/core/api/test-current.txt b/core/api/test-current.txt index d01706d85c59..ab770afb078b 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -154,6 +154,7 @@ package android.app { field public static final int PROCESS_CAPABILITY_ALL_IMPLICIT = 6; // 0x6 field public static final int PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK = 8; // 0x8 field public static final int PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK = 32; // 0x20 + field public static final int PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 5; // 0x5 field public static final int PROCESS_STATE_FOREGROUND_SERVICE = 4; // 0x4 field public static final int PROCESS_STATE_TOP = 2; // 0x2 field public static final int STOP_USER_ON_SWITCH_DEFAULT = -1; // 0xffffffff diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 66d04a3132eb..f72a9dec81f3 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -705,6 +705,8 @@ public class ActivityManager { /** @hide Process is hosting a foreground service due to a system binding. */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @SuppressLint("UnflaggedApi") // @TestApi without associated feature. + @TestApi public static final int PROCESS_STATE_BOUND_FOREGROUND_SERVICE = ProcessStateEnum.BOUND_FOREGROUND_SERVICE; @@ -3541,7 +3543,7 @@ public class ActivityManager { * foreground. This may be running a window that is behind the current * foreground (so paused and with its state saved, not interacting with * the user, but visible to them to some degree); it may also be running - * other services under the system's control that it inconsiders important. + * other services under the system's control that it considers important. */ public static final int IMPORTANCE_VISIBLE = 200; @@ -3613,9 +3615,9 @@ public class ActivityManager { public static final int IMPORTANCE_CANT_SAVE_STATE = 350; /** - * Constant for {@link #importance}: This process process contains - * cached code that is expendable, not actively running any app components - * we care about. + * Constant for {@link #importance}: This process contains cached code + * that is expendable, not actively running any app components we care + * about. */ public static final int IMPORTANCE_CACHED = 400; diff --git a/core/java/android/app/wearable/OWNERS b/core/java/android/app/wearable/OWNERS index 073e2d79850b..497eaf0e40f1 100644 --- a/core/java/android/app/wearable/OWNERS +++ b/core/java/android/app/wearable/OWNERS @@ -1,3 +1,5 @@ charliewang@google.com +hackz@google.com oni@google.com +tomchan@google.com volnov@google.com
\ No newline at end of file diff --git a/core/java/android/content/OWNERS b/core/java/android/content/OWNERS index 90c3d04d62d0..a37408b7d847 100644 --- a/core/java/android/content/OWNERS +++ b/core/java/android/content/OWNERS @@ -4,6 +4,7 @@ per-file ContextWrapper.java = * per-file *Content* = file:/services/core/java/com/android/server/am/OWNERS per-file *Sync* = file:/services/core/java/com/android/server/am/OWNERS per-file IntentFilter.java = file:/PACKAGE_MANAGER_OWNERS +per-file UriRelativeFilter* = file:/PACKAGE_MANAGER_OWNERS per-file IntentFilter.java = file:/services/core/java/com/android/server/am/OWNERS per-file Intent.java = file:/INTENT_OWNERS per-file AutofillOptions* = file:/core/java/android/service/autofill/OWNERS diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 365f9130ddd7..594ec18d9996 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -16,6 +16,7 @@ package android.net; +import static android.app.ActivityManager.PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK; import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN; import static android.app.ActivityManager.procStateToString; import static android.content.pm.PackageManager.GET_SIGNATURES; @@ -170,6 +171,8 @@ public class NetworkPolicyManager { public static final String FIREWALL_CHAIN_NAME_RESTRICTED = "restricted"; /** @hide */ public static final String FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY = "low_power_standby"; + /** @hide */ + public static final String FIREWALL_CHAIN_NAME_BACKGROUND = "background"; private static final boolean ALLOW_PLATFORM_APP_POLICY = true; @@ -180,6 +183,9 @@ public class NetworkPolicyManager { /** @hide */ public static final int TOP_THRESHOLD_STATE = ActivityManager.PROCESS_STATE_BOUND_TOP; + /** @hide */ + public static final int BACKGROUND_THRESHOLD_STATE = ActivityManager.PROCESS_STATE_TOP_SLEEPING; + /** * {@link Intent} extra that indicates which {@link NetworkTemplate} rule it * applies to. @@ -264,6 +270,16 @@ public class NetworkPolicyManager { * @hide */ public static final int ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST = 1 << 6; + + /** + * Flag to indicate that the app is exempt from always-on background network restrictions. + * Note that this is explicitly different to the flag NOT_FOREGROUND which is used to grant + * shared exception to apps from power restrictions like doze, battery saver and app-standby. + * + * @hide + */ + public static final int ALLOWED_REASON_NOT_IN_BACKGROUND = 1 << 7; + /** * Flag to indicate that app is exempt from certain metered network restrictions because user * explicitly exempted it. @@ -822,6 +838,21 @@ public class NetworkPolicyManager { } /** + * This is currently only used as an implementation detail for + * {@link com.android.server.net.NetworkPolicyManagerService}. + * Only put here to be together with other isProcStateAllowed* methods. + * + * @hide + */ + public static boolean isProcStateAllowedNetworkWhileBackground(@Nullable UidState uidState) { + if (uidState == null) { + return false; + } + return uidState.procState < BACKGROUND_THRESHOLD_STATE + || (uidState.capability & PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK) != 0; + } + + /** * Returns true if {@param procState} is considered foreground and as such will be allowed * to access network when the device is in data saver mode. Otherwise, false. * @hide diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java index 70cf97308ffb..83b7edaec72d 100644 --- a/core/java/android/net/vcn/VcnManager.java +++ b/core/java/android/net/vcn/VcnManager.java @@ -80,8 +80,6 @@ public class VcnManager { * <p>The VCN will only migrate to a Carrier WiFi network that has a signal strength greater * than, or equal to this threshold. * - * <p>WARNING: The VCN does not listen for changes to this key made after VCN startup. - * * @hide */ @NonNull @@ -94,14 +92,39 @@ public class VcnManager { * <p>If the VCN's selected Carrier WiFi network has a signal strength less than this threshold, * the VCN will attempt to migrate away from the Carrier WiFi network. * - * <p>WARNING: The VCN does not listen for changes to this key made after VCN startup. - * * @hide */ @NonNull public static final String VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY = "vcn_network_selection_wifi_exit_rssi_threshold"; + /** + * Key for the interval to poll IpSecTransformState for packet loss monitoring + * + * @hide + */ + @NonNull + public static final String VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY = + "vcn_network_selection_poll_ipsec_state_interval_seconds"; + + /** + * Key for the threshold of IPSec packet loss rate + * + * @hide + */ + @NonNull + public static final String VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY = + "vcn_network_selection_ipsec_packet_loss_percent_threshold"; + + /** + * Key for the list of timeouts in minute to stop penalizing an underlying network candidate + * + * @hide + */ + @NonNull + public static final String VCN_NETWORK_SELECTION_PENALTY_TIMEOUT_MINUTES_LIST_KEY = + "vcn_network_selection_penalty_timeout_minutes_list"; + // TODO: Add separate signal strength thresholds for 2.4 GHz and 5GHz /** @@ -115,6 +138,20 @@ public class VcnManager { "vcn_restricted_transports"; /** + * Key for number of seconds to wait before entering safe mode + * + * <p>A VcnGatewayConnection will enter safe mode when it takes over the configured timeout to + * enter {@link ConnectedState}. + * + * <p>Defaults to 30, unless overridden by carrier config + * + * @hide + */ + @NonNull + public static final String VCN_SAFE_MODE_TIMEOUT_SECONDS_KEY = + "vcn_safe_mode_timeout_seconds_key"; + + /** * Key for maximum number of parallel SAs for tunnel aggregation * * <p>If set to a value > 1, multiple tunnels will be set up, and inbound traffic will be @@ -134,7 +171,11 @@ public class VcnManager { new String[] { VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY, VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY, + VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY, + VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY, + VCN_NETWORK_SELECTION_PENALTY_TIMEOUT_MINUTES_LIST_KEY, VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY, + VCN_SAFE_MODE_TIMEOUT_SECONDS_KEY, VCN_TUNNEL_AGGREGATION_SA_COUNT_MAX_KEY, }; diff --git a/core/java/android/net/vcn/flags.aconfig b/core/java/android/net/vcn/flags.aconfig index 6956916af0f1..7afd72195fcb 100644 --- a/core/java/android/net/vcn/flags.aconfig +++ b/core/java/android/net/vcn/flags.aconfig @@ -5,4 +5,18 @@ flag { namespace: "vcn" description: "Feature flag for safe mode configurability" bug: "276358140" +} + +flag { + name: "safe_mode_timeout_config" + namespace: "vcn" + description: "Feature flag for adjustable safe mode timeout" + bug: "317406085" +} + +flag{ + name: "network_metric_monitor" + namespace: "vcn" + description: "Feature flag for enabling network metric monitor" + bug: "282996138" }
\ No newline at end of file diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index 5b24dcacbf53..3de862810a4c 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -25,6 +25,7 @@ import static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY; import static android.system.OsConstants.EINVAL; import static android.system.OsConstants.ENOSYS; import static android.system.OsConstants.F_OK; +import static android.system.OsConstants.EIO; import static android.system.OsConstants.O_ACCMODE; import static android.system.OsConstants.O_APPEND; import static android.system.OsConstants.O_CREAT; @@ -37,6 +38,7 @@ import static android.system.OsConstants.SPLICE_F_MORE; import static android.system.OsConstants.SPLICE_F_MOVE; import static android.system.OsConstants.S_ISFIFO; import static android.system.OsConstants.S_ISREG; +import static android.system.OsConstants.S_ISSOCK; import static android.system.OsConstants.W_OK; import android.annotation.NonNull; @@ -459,6 +461,8 @@ public final class FileUtils { } } else if (S_ISFIFO(st_in.st_mode) || S_ISFIFO(st_out.st_mode)) { return copyInternalSplice(in, out, count, signal, executor, listener); + } else if (S_ISSOCK(st_in.st_mode) || S_ISSOCK(st_out.st_mode)) { + return copyInternalSpliceSocket(in, out, count, signal, executor, listener); } } catch (ErrnoException e) { throw e.rethrowAsIOException(); @@ -509,6 +513,86 @@ public final class FileUtils { } return progress; } + /** + * Requires one of input or output to be a socket file. + * + * @hide + */ + @VisibleForTesting + public static long copyInternalSpliceSocket(FileDescriptor in, FileDescriptor out, long count, + CancellationSignal signal, Executor executor, ProgressListener listener) + throws ErrnoException { + long progress = 0; + long checkpoint = 0; + long countToRead = count; + long countInPipe = 0; + long t; + + FileDescriptor[] pipes = Os.pipe(); + + while (countToRead > 0 || countInPipe > 0) { + if (countToRead > 0) { + t = Os.splice(in, null, pipes[1], null, Math.min(countToRead, COPY_CHECKPOINT_BYTES), + SPLICE_F_MOVE | SPLICE_F_MORE); + if (t < 0) { + // splice error + Slog.e(TAG, "splice error, fdIn --> pipe, copy size:" + count + + ", copied:" + progress + + ", read:" + (count - countToRead) + + ", in pipe:" + countInPipe); + break; + } else if (t == 0) { + // end of input, input count larger than real size + Slog.w(TAG, "Reached the end of the input file. The size to be copied exceeds the actual size, copy size:" + count + + ", copied:" + progress + + ", read:" + (count - countToRead) + + ", in pipe:" + countInPipe); + countToRead = 0; + } else { + countInPipe += t; + countToRead -= t; + } + } + + if (countInPipe > 0) { + t = Os.splice(pipes[0], null, out, null, Math.min(countInPipe, COPY_CHECKPOINT_BYTES), + SPLICE_F_MOVE | SPLICE_F_MORE); + // The data is already in the pipeline, so the return value will not be zero. + // If it is 0, it means an error has occurred. So here use t<=0. + if (t <= 0) { + Slog.e(TAG, "splice error, pipe --> fdOut, copy size:" + count + + ", copied:" + progress + + ", read:" + (count - countToRead) + + ", in pipe: " + countInPipe); + throw new ErrnoException("splice, pipe --> fdOut", EIO); + } else { + progress += t; + checkpoint += t; + countInPipe -= t; + } + } + + if (checkpoint >= COPY_CHECKPOINT_BYTES) { + if (signal != null) { + signal.throwIfCanceled(); + } + if (executor != null && listener != null) { + final long progressSnapshot = progress; + executor.execute(() -> { + listener.onProgress(progressSnapshot); + }); + } + checkpoint = 0; + } + } + if (executor != null && listener != null) { + final long progressSnapshot = progress; + executor.execute(() -> { + listener.onProgress(progressSnapshot); + }); + } + return progress; + } /** * Requires both input and output to be a regular file. diff --git a/core/java/android/os/VintfObject.java b/core/java/android/os/VintfObject.java index 4fc5131617b2..505655775239 100644 --- a/core/java/android/os/VintfObject.java +++ b/core/java/android/os/VintfObject.java @@ -31,6 +31,10 @@ public class VintfObject { private static final String LOG_TAG = "VintfObject"; + static { + System.loadLibrary("vintf_jni"); + } + /** * Slurps all device information (both manifests and both matrices) * and report them. diff --git a/core/java/android/os/VintfRuntimeInfo.java b/core/java/android/os/VintfRuntimeInfo.java index f17039ba9bf4..e729063d6763 100644 --- a/core/java/android/os/VintfRuntimeInfo.java +++ b/core/java/android/os/VintfRuntimeInfo.java @@ -28,6 +28,10 @@ public class VintfRuntimeInfo { private VintfRuntimeInfo() {} + static { + System.loadLibrary("vintf_jni"); + } + /** * @return /sys/fs/selinux/policyvers, via security_policyvers() native call * diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 2cc850d5c11e..7bad9c561f8a 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -12961,6 +12961,16 @@ public final class Settings { @Readable public static final String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update"; + + /** + * Whether to boot with 16K page size compatible kernel + * 1 = Boot with 16K kernel + * 0 = Boot with 4K kernel (default) + * @hide + */ + @Readable + public static final String ENABLE_16K_PAGES = "enable_16k_pages"; + /** Timeout for package verification. * @hide */ @Readable diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig index 30524a1132fa..43163b3b9051 100644 --- a/core/java/android/security/flags.aconfig +++ b/core/java/android/security/flags.aconfig @@ -53,8 +53,15 @@ flag { flag { name: "frp_enforcement" - namespace: "android_hw_security" + namespace: "hardware_backed_security" description: "This flag controls whether PDB enforces FRP" bug: "290312729" is_fixed_read_only: true } + +flag { + name: "report_primary_auth_attempts" + namespace: "biometrics" + description: "Report primary auth attempts from LockSettingsService" + bug: "285053096" +} diff --git a/core/java/android/service/wearable/OWNERS b/core/java/android/service/wearable/OWNERS index 073e2d79850b..eca48b742cef 100644 --- a/core/java/android/service/wearable/OWNERS +++ b/core/java/android/service/wearable/OWNERS @@ -1,3 +1 @@ -charliewang@google.com -oni@google.com -volnov@google.com
\ No newline at end of file +include /core/java/android/app/wearable/OWNERS
\ No newline at end of file diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index cd2d36c60ade..72d4cda8b139 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -2191,7 +2191,8 @@ public final class ViewRootImpl implements ViewParent, } else { mDisplay = preferredDisplay; } - if (mHdrSdrRatioChangedListener != null && mDisplay != null) { + if (mHdrSdrRatioChangedListener != null && mDisplay != null + && mDisplay.isHdrSdrRatioAvailable()) { mDisplay.registerHdrSdrRatioChangedListener(mExecutor, mHdrSdrRatioChangedListener); } mContext.updateDisplay(mDisplay.getDisplayId()); diff --git a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java index 7694754de6ff..c9ffdae890c5 100644 --- a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java +++ b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java @@ -49,7 +49,6 @@ import android.view.KeyEvent; import android.view.View; import android.view.ViewRootImpl; -import com.android.internal.annotations.GuardedBy; import com.android.internal.infra.AndroidFuture; import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection; import com.android.internal.inputmethod.IRemoteInputConnection; @@ -60,6 +59,7 @@ import java.lang.annotation.Retention; import java.lang.ref.WeakReference; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; import java.util.function.Supplier; @@ -164,18 +164,15 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { boolean cancellable(); } - @GuardedBy("mLock") - @Nullable - private InputConnection mInputConnection; + @NonNull + private final AtomicReference<InputConnection> mInputConnectionRef; + @NonNull + private final AtomicBoolean mDeactivateRequested = new AtomicBoolean(false); @NonNull private final Looper mLooper; private final Handler mH; - private final Object mLock = new Object(); - @GuardedBy("mLock") - private boolean mFinished = false; - private final InputMethodManager mParentInputMethodManager; private final WeakReference<View> mServedView; @@ -193,7 +190,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { RemoteInputConnectionImpl(@NonNull Looper looper, @NonNull InputConnection inputConnection, @NonNull InputMethodManager inputMethodManager, @Nullable View servedView) { - mInputConnection = inputConnection; + mInputConnectionRef = new AtomicReference<>(inputConnection); mLooper = looper; mH = new Handler(mLooper); mParentInputMethodManager = inputMethodManager; @@ -211,9 +208,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { */ @Nullable public InputConnection getInputConnection() { - synchronized (mLock) { - return mInputConnection; - } + return mInputConnectionRef.get(); } /** @@ -229,13 +224,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { * {@link InputConnection#closeConnection()} as a result of {@link #deactivate()}. */ private boolean isFinished() { - synchronized (mLock) { - return mFinished; - } - } - - private boolean isActive() { - return mParentInputMethodManager.isActive() && !isFinished(); + return mInputConnectionRef.get() == null; } private View getServedView() { @@ -372,28 +361,18 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { */ @Dispatching(cancellable = false) public void deactivate() { - if (isFinished()) { + if (mDeactivateRequested.getAndSet(true)) { // This is a small performance optimization. Still only the 1st call of - // reportFinish() will take effect. + // deactivate() will take effect. return; } dispatch(() -> { // Deactivate the notifier when finishing typing. notifyTypingHint(false /* isTyping */, true /* deactivate */); - // Note that we do not need to worry about race condition here, because 1) mFinished is - // updated only inside this block, and 2) the code here is running on a Handler hence we - // assume multiple closeConnection() tasks will not be handled at the same time. - if (isFinished()) { - return; - } Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#closeConnection"); try { InputConnection ic = getInputConnection(); - // Note we do NOT check isActive() here, because this is safe - // for an IME to call at any time, and we need to allow it - // through to clean up our state after the IME has switched to - // another client. if (ic == null) { return; } @@ -403,10 +382,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { // TODO(b/199934664): See if we can remove this by providing a default impl. } } finally { - synchronized (mLock) { - mInputConnection = null; - mFinished = true; - } + mInputConnectionRef.set(null); Trace.traceEnd(Trace.TRACE_TAG_INPUT); } @@ -460,8 +436,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { public String toString() { return "RemoteInputConnectionImpl{" + "connection=" + getInputConnection() - + " finished=" + isFinished() - + " mParentInputMethodManager.isActive()=" + mParentInputMethodManager.isActive() + + " mDeactivateRequested=" + mDeactivateRequested.get() + " mServedView=" + mServedView.get() + "}"; } @@ -474,16 +449,14 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { * {@link DumpableInputConnection#dumpDebug(ProtoOutputStream, long)}. */ public void dumpDebug(ProtoOutputStream proto, long fieldId) { - synchronized (mLock) { - // Check that the call is initiated in the target thread of the current InputConnection - // {@link InputConnection#getHandler} since the messages to IInputConnectionWrapper are - // executed on this thread. Otherwise the messages are dispatched to the correct thread - // in IInputConnectionWrapper, but this is not wanted while dumpng, for performance - // reasons. - if ((mInputConnection instanceof DumpableInputConnection) - && mLooper.isCurrentThread()) { - ((DumpableInputConnection) mInputConnection).dumpDebug(proto, fieldId); - } + final InputConnection ic = mInputConnectionRef.get(); + // Check that the call is initiated in the target thread of the current InputConnection + // {@link InputConnection#getHandler} since the messages to IInputConnectionWrapper are + // executed on this thread. Otherwise the messages are dispatched to the correct thread + // in IInputConnectionWrapper, but this is not wanted while dumping, for performance + // reasons. + if ((ic instanceof DumpableInputConnection) && mLooper.isCurrentThread()) { + ((DumpableInputConnection) ic).dumpDebug(proto, fieldId); } } @@ -498,7 +471,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { public void dispatchReportFullscreenMode(boolean enabled) { dispatch(() -> { final InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { return; } ic.reportFullscreenMode(enabled); @@ -514,7 +487,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return null; // cancelled } final InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "getTextAfterCursor on inactive InputConnection"); return null; } @@ -536,7 +509,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return null; // cancelled } final InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "getTextBeforeCursor on inactive InputConnection"); return null; } @@ -558,7 +531,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return null; // cancelled } final InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "getSelectedText on inactive InputConnection"); return null; } @@ -580,7 +553,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return null; // cancelled } final InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "getSurroundingText on inactive InputConnection"); return null; } @@ -608,7 +581,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return 0; // cancelled } final InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "getCursorCapsMode on inactive InputConnection"); return 0; } @@ -625,7 +598,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return null; // cancelled } final InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "getExtractedText on inactive InputConnection"); return null; } @@ -642,7 +615,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return; // cancelled } InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "commitText on inactive InputConnection"); return; } @@ -660,7 +633,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return; // cancelled } InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "commitText on inactive InputConnection"); return; } @@ -676,7 +649,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return; // cancelled } InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "commitCompletion on inactive InputConnection"); return; } @@ -692,7 +665,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return; // cancelled } InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "commitCorrection on inactive InputConnection"); return; } @@ -712,7 +685,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return; // cancelled } InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "setSelection on inactive InputConnection"); return; } @@ -728,7 +701,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return; // cancelled } InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "performEditorAction on inactive InputConnection"); return; } @@ -744,7 +717,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return; // cancelled } InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "performContextMenuAction on inactive InputConnection"); return; } @@ -760,7 +733,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return; // cancelled } InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "setComposingRegion on inactive InputConnection"); return; } @@ -781,7 +754,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return; // cancelled } InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "setComposingRegion on inactive InputConnection"); return; } @@ -798,7 +771,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return; // cancelled } InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "setComposingText on inactive InputConnection"); return; } @@ -816,7 +789,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return; // cancelled } InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "setComposingText on inactive InputConnection"); return; } @@ -845,11 +818,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return; // cancelled } InputConnection ic = getInputConnection(); - // Note we do NOT check isActive() here, because this is safe - // for an IME to call at any time, and we need to allow it - // through to clean up our state after the IME has switched to - // another client. - if (ic == null) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "finishComposingTextFromImm on inactive InputConnection"); return; } @@ -873,11 +842,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return; // cancelled } InputConnection ic = getInputConnection(); - // Note we do NOT check isActive() here, because this is safe - // for an IME to call at any time, and we need to allow it - // through to clean up our state after the IME has switched to - // another client. - if (ic == null) { + if (ic == null && mDeactivateRequested.get()) { Log.w(TAG, "finishComposingText on inactive InputConnection"); return; } @@ -893,7 +858,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return; // cancelled } InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "sendKeyEvent on inactive InputConnection"); return; } @@ -909,7 +874,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return; // cancelled } InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "clearMetaKeyStates on inactive InputConnection"); return; } @@ -926,7 +891,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return; // cancelled } InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "deleteSurroundingText on inactive InputConnection"); return; } @@ -944,7 +909,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return; // cancelled } InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "deleteSurroundingTextInCodePoints on inactive InputConnection"); return; } @@ -964,7 +929,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return; // cancelled } InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "beginBatchEdit on inactive InputConnection"); return; } @@ -980,7 +945,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return; // cancelled } InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "endBatchEdit on inactive InputConnection"); return; } @@ -996,7 +961,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return; // cancelled } InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "performSpellCheck on inactive InputConnection"); return; } @@ -1013,7 +978,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return; // cancelled } InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "performPrivateCommand on inactive InputConnection"); return; } @@ -1051,7 +1016,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return; // cancelled } InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "performHandwritingGesture on inactive InputConnection"); if (resultReceiver != null) { resultReceiver.send( @@ -1091,7 +1056,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return; // cancelled } InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "previewHandwritingGesture on inactive InputConnection"); return; // cancelled } @@ -1139,7 +1104,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { @InputConnection.CursorUpdateMode int cursorUpdateMode, @InputConnection.CursorUpdateFilter int cursorUpdateFilter, int imeDisplayId) { final InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "requestCursorUpdates on inactive InputConnection"); return false; } @@ -1177,7 +1142,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return; // cancelled } InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "requestTextBoundsInfo on inactive InputConnection"); resultReceiver.send(TextBoundsInfoResult.CODE_CANCELLED, null); return; @@ -1221,7 +1186,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return false; // cancelled } final InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "commitContent on inactive InputConnection"); return false; } @@ -1246,7 +1211,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return; // cancelled } InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "setImeConsumesInput on inactive InputConnection"); return; } @@ -1270,7 +1235,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return; // cancelled } InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "replaceText on inactive InputConnection"); return; } @@ -1289,7 +1254,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return; // cancelled } InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "commitText on inactive InputConnection"); return; } @@ -1309,7 +1274,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return; // cancelled } InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "setSelection on inactive InputConnection"); return; } @@ -1326,7 +1291,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return null; // cancelled } final InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "getSurroundingText on inactive InputConnection"); return null; } @@ -1354,7 +1319,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return; // cancelled } InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "deleteSurroundingText on inactive InputConnection"); return; } @@ -1370,7 +1335,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return; // cancelled } InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "sendKeyEvent on inactive InputConnection"); return; } @@ -1386,7 +1351,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return; // cancelled } InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "performEditorAction on inactive InputConnection"); return; } @@ -1402,7 +1367,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return; // cancelled } InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "performContextMenuAction on inactive InputConnection"); return; } @@ -1419,7 +1384,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return 0; // cancelled } final InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "getCursorCapsMode on inactive InputConnection"); return 0; } @@ -1435,7 +1400,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { return; // cancelled } InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { + if (ic == null || mDeactivateRequested.get()) { Log.w(TAG, "clearMetaKeyStates on inactive InputConnection"); return; } diff --git a/core/java/android/window/SplashScreenView.java b/core/java/android/window/SplashScreenView.java index bdaad2b68fc2..473b814fc4a7 100644 --- a/core/java/android/window/SplashScreenView.java +++ b/core/java/android/window/SplashScreenView.java @@ -47,6 +47,7 @@ import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup; import android.view.Window; +import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.ImageView; @@ -337,7 +338,14 @@ public final class SplashScreenView extends FrameLayout { "SplashScreenView"); ImageView imageView = new ImageView(viewContext); imageView.setBackground(mIconDrawable); - viewHost.setView(imageView, mIconSize, mIconSize); + final int windowFlag = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; + final WindowManager.LayoutParams lp = + new WindowManager.LayoutParams(mIconSize, mIconSize, + WindowManager.LayoutParams.TYPE_APPLICATION, windowFlag, + PixelFormat.TRANSPARENT); + viewHost.setView(imageView, lp); SurfaceControlViewHost.SurfacePackage surfacePackage = viewHost.getSurfacePackage(); surfaceView.setChildSurfacePackage(surfacePackage); view.mSurfacePackage = surfacePackage; diff --git a/core/java/com/android/internal/util/RingBuffer.java b/core/java/com/android/internal/util/RingBuffer.java index 8fc4c30e54ab..7f8c8a1cae04 100644 --- a/core/java/com/android/internal/util/RingBuffer.java +++ b/core/java/com/android/internal/util/RingBuffer.java @@ -19,7 +19,10 @@ package com.android.internal.util; import static com.android.internal.util.Preconditions.checkArgumentPositive; import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; import java.util.Arrays; +import java.util.function.IntFunction; +import java.util.function.Supplier; /** * A simple ring buffer structure with bounded capacity backed by an array. @@ -29,16 +32,35 @@ import java.util.Arrays; */ public class RingBuffer<T> { + private final Supplier<T> mNewItem; // Array for storing events. private final T[] mBuffer; // Cursor keeping track of the logical end of the array. This cursor never // wraps and instead keeps track of the total number of append() operations. private long mCursor = 0; + /** + * @deprecated This uses reflection to create new instances. + * Use {@link #RingBuffer(Supplier, IntFunction, int)}} instead. + */ + @Deprecated public RingBuffer(Class<T> c, int capacity) { + this(() -> (T) createNewItem(c), cap -> (T[]) Array.newInstance(c, cap), capacity); + } + + private static Object createNewItem(Class c) { + try { + return c.getDeclaredConstructor().newInstance(); + } catch (IllegalAccessException | InstantiationException | NoSuchMethodException + | InvocationTargetException e) { + return null; + } + } + + public RingBuffer(Supplier<T> newItem, IntFunction<T[]> newBacking, int capacity) { checkArgumentPositive(capacity, "A RingBuffer cannot have 0 capacity"); - // Java cannot create generic arrays without a runtime hint. - mBuffer = (T[]) Array.newInstance(c, capacity); + mBuffer = newBacking.apply(capacity); + mNewItem = newItem; } public int size() { @@ -68,22 +90,11 @@ public class RingBuffer<T> { public T getNextSlot() { final int nextSlotIdx = indexOf(mCursor++); if (mBuffer[nextSlotIdx] == null) { - mBuffer[nextSlotIdx] = createNewItem(); + mBuffer[nextSlotIdx] = mNewItem.get(); } return mBuffer[nextSlotIdx]; } - /** - * @return a new object of type <T> or null if a new object could not be created. - */ - protected T createNewItem() { - try { - return (T) mBuffer.getClass().getComponentType().newInstance(); - } catch (IllegalAccessException | InstantiationException e) { - return null; - } - } - public T[] toArray() { // Only generic way to create a T[] from another T[] T[] out = Arrays.copyOf(mBuffer, size(), (Class<T[]>) mBuffer.getClass()); diff --git a/core/java/com/android/internal/widget/ILockSettingsStateListener.aidl b/core/java/com/android/internal/widget/ILockSettingsStateListener.aidl new file mode 100644 index 000000000000..25e30034fe8f --- /dev/null +++ b/core/java/com/android/internal/widget/ILockSettingsStateListener.aidl @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.widget; + +/** + * Callback interface between LockSettingService and other system services to be notified about the + * state of primary authentication (i.e. PIN/pattern/password). + * @hide + */ +oneway interface ILockSettingsStateListener { + /** + * Defines behavior in response to a successful authentication + * @param userId The user Id for the requested authentication + */ + void onAuthenticationSucceeded(int userId); + + /** + * Defines behavior in response to a failed authentication + * @param userId The user Id for the requested authentication + */ + void onAuthenticationFailed(int userId); +}
\ No newline at end of file diff --git a/core/java/com/android/internal/widget/LockSettingsInternal.java b/core/java/com/android/internal/widget/LockSettingsInternal.java index 8114e1fd3bb0..627e8779f9d0 100644 --- a/core/java/com/android/internal/widget/LockSettingsInternal.java +++ b/core/java/com/android/internal/widget/LockSettingsInternal.java @@ -166,4 +166,16 @@ public abstract class LockSettingsInternal { * Refreshes pending strong auth timeout with the latest admin requirement set by device policy. */ public abstract void refreshStrongAuthTimeout(int userId); + + /** + * Register a LockSettingsStateListener + * @param listener The listener to be registered + */ + public abstract void registerLockSettingsStateListener(ILockSettingsStateListener listener); + + /** + * Unregister a LockSettingsStateListener + * @param listener The listener to be unregistered + */ + public abstract void unregisterLockSettingsStateListener(ILockSettingsStateListener listener); } diff --git a/core/jni/Android.bp b/core/jni/Android.bp index f5b12db97c54..c4ff402a0da7 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -182,8 +182,6 @@ cc_library_shared_for_libandroid_runtime { "android_os_SharedMemory.cpp", "android_os_storage_StorageManager.cpp", "android_os_UEventObserver.cpp", - "android_os_VintfObject.cpp", - "android_os_VintfRuntimeInfo.cpp", "android_os_incremental_IncrementalManager.cpp", "android_net_LocalSocketImpl.cpp", "android_service_DataLoaderService.cpp", @@ -271,6 +269,7 @@ cc_library_shared_for_libandroid_runtime { "libdmabufinfo", "libgif", "libgui_window_info_static", + "libkernelconfigs", "libseccomp_policy", "libgrallocusage", "libscrypt_static", @@ -340,7 +339,6 @@ cc_library_shared_for_libandroid_runtime { "libnativeloader_lazy", "libmemunreachable", "libhidlbase", - "libvintf", "libnativedisplay", "libnativewindow", "libdl", @@ -448,8 +446,25 @@ cc_library_shared_for_libandroid_runtime { // (e.g. gDefaultServiceManager) "libbinder", "libhidlbase", // libhwbinder is in here - "libvintf", ], }, }, } + +cc_library_shared { + name: "libvintf_jni", + + cpp_std: "gnu++20", + + srcs: [ + "android_os_VintfObject.cpp", + "android_os_VintfRuntimeInfo.cpp", + ], + + shared_libs: [ + "libbase", + "liblog", + "libnativehelper", + "libvintf", + ], +} diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 08fb51b8b8e3..a3bc2bb3b17c 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -151,8 +151,6 @@ extern int register_android_os_MessageQueue(JNIEnv* env); extern int register_android_os_Parcel(JNIEnv* env); extern int register_android_os_PerformanceHintManager(JNIEnv* env); extern int register_android_os_SELinux(JNIEnv* env); -extern int register_android_os_VintfObject(JNIEnv *env); -extern int register_android_os_VintfRuntimeInfo(JNIEnv *env); extern int register_android_os_storage_StorageManager(JNIEnv* env); extern int register_android_os_SystemProperties(JNIEnv *env); extern int register_android_os_SystemClock(JNIEnv* env); @@ -1541,8 +1539,6 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_os_NativeHandle), REG_JNI(register_android_os_ServiceManager), REG_JNI(register_android_os_storage_StorageManager), - REG_JNI(register_android_os_VintfObject), - REG_JNI(register_android_os_VintfRuntimeInfo), REG_JNI(register_android_service_DataLoaderService), REG_JNI(register_android_view_DisplayEventReceiver), REG_JNI(register_android_view_Surface), diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index fe957624cf4b..e4b0f1abeff9 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -52,7 +52,7 @@ #include <memunreachable/memunreachable.h> #include <android-base/strings.h> #include "android_os_Debug.h" -#include <vintf/VintfObject.h> +#include <vintf/KernelConfigs.h> namespace android { @@ -959,10 +959,9 @@ static jboolean android_os_Debug_isVmapStack(JNIEnv *env, jobject clazz) } cfg_state = CONFIG_UNKNOWN; if (cfg_state == CONFIG_UNKNOWN) { - auto runtime_info = vintf::VintfObject::GetInstance()->getRuntimeInfo( - vintf::RuntimeInfo::FetchFlag::CONFIG_GZ); - CHECK(runtime_info != nullptr) << "Kernel configs cannot be fetched. b/151092221"; - const std::map<std::string, std::string>& configs = runtime_info->kernelConfigs(); + std::map<std::string, std::string> configs; + const status_t result = android::kernelconfigs::LoadKernelConfigs(&configs); + CHECK(result == OK) << "Kernel configs could not be fetched. b/151092221"; std::map<std::string, std::string>::const_iterator it = configs.find("CONFIG_VMAP_STACK"); cfg_state = (it != configs.end() && it->second == "y") ? CONFIG_SET : CONFIG_UNSET; } diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp index 477bd096b11a..734b5f497e2e 100644 --- a/core/jni/android_os_HwBinder.cpp +++ b/core/jni/android_os_HwBinder.cpp @@ -39,7 +39,6 @@ #include <hwbinder/ProcessState.h> #include <nativehelper/ScopedLocalRef.h> #include <nativehelper/ScopedUtfChars.h> -#include <vintf/parse_string.h> #include <utils/misc.h> #include "core_jni_helpers.h" diff --git a/core/jni/android_os_VintfObject.cpp b/core/jni/android_os_VintfObject.cpp index b6517117ca62..ce4a33735c6d 100644 --- a/core/jni/android_os_VintfObject.cpp +++ b/core/jni/android_os_VintfObject.cpp @@ -17,16 +17,14 @@ #define LOG_TAG "VintfObject" //#define LOG_NDEBUG 0 #include <android-base/logging.h> - -#include <vector> -#include <string> - -#include <nativehelper/JNIHelp.h> #include <vintf/VintfObject.h> #include <vintf/parse_string.h> #include <vintf/parse_xml.h> -#include "core_jni_helpers.h" +#include <vector> +#include <string> + +#include "jni_wrappers.h" static jclass gString; static jclass gHashMapClazz; @@ -94,10 +92,13 @@ static jobjectArray android_os_VintfObject_report(JNIEnv* env, jclass) return toJavaStringArray(env, cStrings); } -static jint android_os_VintfObject_verifyBuildAtBoot(JNIEnv* env, jclass) { +static jint android_os_VintfObject_verifyBuildAtBoot(JNIEnv*, jclass) { std::string error; + // Use temporary VintfObject, not the shared instance, to release memory + // after check. int32_t status = - VintfObject::GetInstance() + VintfObject::Builder() + .build() ->checkCompatibility(&error, ENABLE_ALL_CHECKS.disableAvb().disableKernel()); if (status) LOG(WARNING) << "VintfObject.verifyBuildAtBoot() returns " << status << ": " << error; @@ -201,4 +202,23 @@ int register_android_os_VintfObject(JNIEnv* env) NELEM(gVintfObjectMethods)); } -}; +extern int register_android_os_VintfRuntimeInfo(JNIEnv* env); + +} // namespace android + +jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) { + JNIEnv* env = NULL; + if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6)) { + return JNI_ERR; + } + + if (android::register_android_os_VintfObject(env) < 0) { + return JNI_ERR; + } + + if (android::register_android_os_VintfRuntimeInfo(env) < 0) { + return JNI_ERR; + } + + return JNI_VERSION_1_6; +} diff --git a/core/jni/android_os_VintfRuntimeInfo.cpp b/core/jni/android_os_VintfRuntimeInfo.cpp index b0271b9e92af..7c2f58829446 100644 --- a/core/jni/android_os_VintfRuntimeInfo.cpp +++ b/core/jni/android_os_VintfRuntimeInfo.cpp @@ -17,23 +17,22 @@ #define LOG_TAG "VintfRuntimeInfo" //#define LOG_NDEBUG 0 -#include <nativehelper/JNIHelp.h> #include <vintf/VintfObject.h> #include <vintf/parse_string.h> #include <vintf/parse_xml.h> -#include "core_jni_helpers.h" +#include "jni_wrappers.h" namespace android { using vintf::RuntimeInfo; using vintf::VintfObject; -#define MAP_STRING_METHOD(javaMethod, cppString, flags) \ - static jstring android_os_VintfRuntimeInfo_##javaMethod(JNIEnv* env, jclass clazz) { \ - std::shared_ptr<const RuntimeInfo> info = VintfObject::GetRuntimeInfo(flags); \ - if (info == nullptr) return nullptr; \ - return env->NewStringUTF((cppString).c_str()); \ +#define MAP_STRING_METHOD(javaMethod, cppString, flags) \ + static jstring android_os_VintfRuntimeInfo_##javaMethod(JNIEnv* env, jclass) { \ + std::shared_ptr<const RuntimeInfo> info = VintfObject::GetRuntimeInfo(flags); \ + if (info == nullptr) return nullptr; \ + return env->NewStringUTF((cppString).c_str()); \ } MAP_STRING_METHOD(getCpuInfo, info->cpuInfo(), RuntimeInfo::FetchFlag::CPU_INFO); @@ -49,9 +48,7 @@ MAP_STRING_METHOD(getBootAvbVersion, vintf::to_string(info->bootAvbVersion()), MAP_STRING_METHOD(getBootVbmetaAvbVersion, vintf::to_string(info->bootVbmetaAvbVersion()), RuntimeInfo::FetchFlag::AVB); - -static jlong android_os_VintfRuntimeInfo_getKernelSepolicyVersion(JNIEnv *env, jclass clazz) -{ +static jlong android_os_VintfRuntimeInfo_getKernelSepolicyVersion(JNIEnv*, jclass) { std::shared_ptr<const RuntimeInfo> info = VintfObject::GetRuntimeInfo(RuntimeInfo::FetchFlag::POLICYVERS); if (info == nullptr) return 0; diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp index 7af69f2dff08..6a640a5ab23b 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -28,6 +28,7 @@ #include <meminfo/sysmeminfo.h> #include <processgroup/processgroup.h> #include <processgroup/sched_policy.h> +#include <android-base/logging.h> #include <android-base/unique_fd.h> #include <algorithm> @@ -232,6 +233,31 @@ void android_os_Process_setThreadGroupAndCpuset(JNIEnv* env, jobject clazz, int } } +// Look up the user ID of a process in /proc/${pid}/status. The Uid: line is present in +// /proc/${pid}/status since at least kernel v2.5. +static int uid_from_pid(int pid) +{ + int uid = -1; + std::array<char, 64> path; + int res = snprintf(path.data(), path.size(), "/proc/%d/status", pid); + if (res < 0 || res >= static_cast<int>(path.size())) { + DCHECK(false); + return uid; + } + FILE* f = fopen(path.data(), "r"); + if (!f) { + return uid; + } + char line[256]; + while (fgets(line, sizeof(line), f)) { + if (sscanf(line, "Uid: %d", &uid) == 1) { + break; + } + } + fclose(f); + return uid; +} + void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jint grp) { ALOGV("%s pid=%d grp=%" PRId32, __func__, pid, grp); @@ -275,7 +301,12 @@ void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jin } } - if (!SetProcessProfilesCached(0, pid, {get_cpuset_policy_profile_name((SchedPolicy)grp)})) + const int uid = uid_from_pid(pid); + if (uid < 0) { + signalExceptionForGroupError(env, ESRCH, pid); + return; + } + if (!SetProcessProfilesCached(uid, pid, {get_cpuset_policy_profile_name((SchedPolicy)grp)})) signalExceptionForGroupError(env, errno ? errno : EPERM, pid); } diff --git a/core/jni/core_jni_helpers.h b/core/jni/core_jni_helpers.h index 210dc895d674..769fa723c96e 100644 --- a/core/jni/core_jni_helpers.h +++ b/core/jni/core_jni_helpers.h @@ -22,6 +22,8 @@ #include <nativehelper/scoped_utf_chars.h> #include <android_runtime/AndroidRuntime.h> +#include "jni_wrappers.h" + // Host targets (layoutlib) do not differentiate between regular and critical native methods, // and they need all the JNI methods to have JNIEnv* and jclass/jobject as their first two arguments. // The following macro allows to have those arguments when compiling for host while omitting them when @@ -36,60 +38,6 @@ namespace android { -// Defines some helpful functions. - -static inline jclass FindClassOrDie(JNIEnv* env, const char* class_name) { - jclass clazz = env->FindClass(class_name); - LOG_ALWAYS_FATAL_IF(clazz == NULL, "Unable to find class %s", class_name); - return clazz; -} - -static inline jfieldID GetFieldIDOrDie(JNIEnv* env, jclass clazz, const char* field_name, - const char* field_signature) { - jfieldID res = env->GetFieldID(clazz, field_name, field_signature); - LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find field %s with signature %s", field_name, - field_signature); - return res; -} - -static inline jmethodID GetMethodIDOrDie(JNIEnv* env, jclass clazz, const char* method_name, - const char* method_signature) { - jmethodID res = env->GetMethodID(clazz, method_name, method_signature); - LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find method %s with signature %s", method_name, - method_signature); - return res; -} - -static inline jfieldID GetStaticFieldIDOrDie(JNIEnv* env, jclass clazz, const char* field_name, - const char* field_signature) { - jfieldID res = env->GetStaticFieldID(clazz, field_name, field_signature); - LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static field %s with signature %s", field_name, - field_signature); - return res; -} - -static inline jmethodID GetStaticMethodIDOrDie(JNIEnv* env, jclass clazz, const char* method_name, - const char* method_signature) { - jmethodID res = env->GetStaticMethodID(clazz, method_name, method_signature); - LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static method %s with signature %s", - method_name, method_signature); - return res; -} - -template <typename T> -static inline T MakeGlobalRefOrDie(JNIEnv* env, T in) { - jobject res = env->NewGlobalRef(in); - LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to create global reference."); - return static_cast<T>(res); -} - -static inline int RegisterMethodsOrDie(JNIEnv* env, const char* className, - const JNINativeMethod* gMethods, int numMethods) { - int res = AndroidRuntime::registerNativeMethods(env, className, gMethods, numMethods); - LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods."); - return res; -} - /** * Returns the result of invoking java.lang.ref.Reference.get() on a Reference object. */ diff --git a/core/jni/jni_wrappers.h b/core/jni/jni_wrappers.h new file mode 100644 index 000000000000..3b29e305e410 --- /dev/null +++ b/core/jni/jni_wrappers.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +// JNI wrappers for better logging + +#include <jni.h> +#include <log/log.h> +#include <nativehelper/JNIHelp.h> + +namespace android { + +static inline jclass FindClassOrDie(JNIEnv* env, const char* class_name) { + jclass clazz = env->FindClass(class_name); + LOG_ALWAYS_FATAL_IF(clazz == NULL, "Unable to find class %s", class_name); + return clazz; +} + +static inline jfieldID GetFieldIDOrDie(JNIEnv* env, jclass clazz, const char* field_name, + const char* field_signature) { + jfieldID res = env->GetFieldID(clazz, field_name, field_signature); + LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find field %s with signature %s", field_name, + field_signature); + return res; +} + +static inline jmethodID GetMethodIDOrDie(JNIEnv* env, jclass clazz, const char* method_name, + const char* method_signature) { + jmethodID res = env->GetMethodID(clazz, method_name, method_signature); + LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find method %s with signature %s", method_name, + method_signature); + return res; +} + +static inline jfieldID GetStaticFieldIDOrDie(JNIEnv* env, jclass clazz, const char* field_name, + const char* field_signature) { + jfieldID res = env->GetStaticFieldID(clazz, field_name, field_signature); + LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static field %s with signature %s", field_name, + field_signature); + return res; +} + +static inline jmethodID GetStaticMethodIDOrDie(JNIEnv* env, jclass clazz, const char* method_name, + const char* method_signature) { + jmethodID res = env->GetStaticMethodID(clazz, method_name, method_signature); + LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static method %s with signature %s", + method_name, method_signature); + return res; +} + +template <typename T> +static inline T MakeGlobalRefOrDie(JNIEnv* env, T in) { + jobject res = env->NewGlobalRef(in); + LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to create global reference."); + return static_cast<T>(res); +} + +static inline int RegisterMethodsOrDie(JNIEnv* env, const char* className, + const JNINativeMethod* gMethods, int numMethods) { + int res = jniRegisterNativeMethods(env, className, gMethods, numMethods); + LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods."); + return res; +} + +} // namespace android diff --git a/core/proto/OWNERS b/core/proto/OWNERS index db391f7a8c35..a854e3626e78 100644 --- a/core/proto/OWNERS +++ b/core/proto/OWNERS @@ -18,6 +18,7 @@ per-file usagestatsservice.proto, usagestatsservice_v2.proto = file:/core/java/a per-file apphibernationservice.proto = file:/core/java/android/apphibernation/OWNERS per-file android/hardware/sensorprivacy.proto = ntmyren@google.com,evanseverson@google.com per-file background_install_control.proto = wenhaowang@google.com,georgechan@google.com,billylau@google.com +per-file android/content/intent.proto = file:/PACKAGE_MANAGER_OWNERS # Biometrics jaggies@google.com @@ -31,5 +32,3 @@ jreck@google.com # Accessibility pweaver@google.com -hongmingjin@google.com -cbrower@google.com diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index bc74f397fa35..f5635f450268 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3522,6 +3522,13 @@ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION" android:protectionLevel="internal|role" /> + <!-- Allows an application to set policy related to <a + href="https://www.threadgroup.org">Thread</a> network. + @FlaggedApi("com.android.net.thread.flags.thread_user_restriction_enabled") + --> + <permission android:name="android.permission.MANAGE_DEVICE_POLICY_THREAD_NETWORK" + android:protectionLevel="internal|role" /> + <!-- Allows an application to set policy related to windows. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java index a0d8183b8da7..d2d745291c5f 100644 --- a/core/tests/coretests/src/android/os/FileUtilsTest.java +++ b/core/tests/coretests/src/android/os/FileUtilsTest.java @@ -29,6 +29,7 @@ import static android.os.ParcelFileDescriptor.MODE_READ_ONLY; import static android.os.ParcelFileDescriptor.MODE_READ_WRITE; import static android.os.ParcelFileDescriptor.MODE_TRUNCATE; import static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY; +import static android.system.OsConstants.AF_INET; import static android.system.OsConstants.F_OK; import static android.system.OsConstants.O_APPEND; import static android.system.OsConstants.O_CREAT; @@ -37,6 +38,7 @@ import static android.system.OsConstants.O_RDWR; import static android.system.OsConstants.O_TRUNC; import static android.system.OsConstants.O_WRONLY; import static android.system.OsConstants.R_OK; +import static android.system.OsConstants.SOCK_STREAM; import static android.system.OsConstants.W_OK; import static android.system.OsConstants.X_OK; import static android.text.format.DateUtils.DAY_IN_MILLIS; @@ -54,6 +56,7 @@ import static org.junit.Assert.fail; import android.content.Context; import android.os.FileUtils.MemoryPipe; import android.provider.DocumentsContract.Document; +import android.system.Os; import android.util.DataUnit; import androidx.test.InstrumentationRegistry; @@ -70,6 +73,8 @@ import org.junit.runner.RunWith; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; @@ -77,6 +82,7 @@ import java.io.FileOutputStream; import java.util.Arrays; import java.util.HashSet; import java.util.Random; +import java.net.InetSocketAddress; @RunWith(AndroidJUnit4.class) public class FileUtilsTest { @@ -249,6 +255,84 @@ public class FileUtilsTest { assertArrayEquals(expected, actual); } + //TODO(ravenwood) Remove the _$noRavenwood suffix and add @RavenwoodIgnore instead + @Test + public void testCopy_SocketToFile_FileToSocket$noRavenwood() throws Exception { + for (int size : DATA_SIZES ) { + final File src = new File(mTarget, "src"); + final File dest = new File(mTarget, "dest"); + byte[] expected = new byte[size]; + byte[] actual = new byte[size]; + new Random().nextBytes(expected); + + // write test data in to src file + writeFile(src, expected); + + // start server, get data from client and save to dest file (socket --> file) + FileDescriptor srvSocketFd = Os.socket(AF_INET, SOCK_STREAM, 0); + Os.bind(srvSocketFd, new InetSocketAddress("localhost", 0)); + Os.listen(srvSocketFd, 5); + InetSocketAddress localSocketAddress = (InetSocketAddress) Os.getsockname(srvSocketFd); + + final Thread srv = new Thread(new Runnable() { + public void run() { + try { + InetSocketAddress peerAddress = new InetSocketAddress(); + FileDescriptor srvConnFd = Os.accept(srvSocketFd, peerAddress); + + // read file size + byte[] rcvFileSizeByteArray = new byte[8]; + Os.read(srvConnFd, rcvFileSizeByteArray, 0, rcvFileSizeByteArray.length); + long rcvFileSize = 0; + for (int i = 0; i < 8; i++) { + rcvFileSize <<= 8; + rcvFileSize |= (rcvFileSizeByteArray[i] & 0xFF); + } + + FileOutputStream fileOutputStream = new FileOutputStream(dest); + // copy data from socket to file + FileUtils.copy(srvConnFd, fileOutputStream.getFD(), rcvFileSize, null, null, null); + + fileOutputStream.close(); + Os.close(srvConnFd); + Os.close(srvSocketFd); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + + srv.start(); + + + // start client, get data from dest file and send to server (file --> socket) + FileDescriptor clientFd = Os.socket(AF_INET, SOCK_STREAM, 0); + Os.connect(clientFd, localSocketAddress.getAddress(), localSocketAddress.getPort()); + + FileInputStream fileInputStream = new FileInputStream(src); + long sndFileSize = src.length(); + // send the file size to server + byte[] sndFileSizeByteArray = new byte[8]; + for (int i = 7; i >= 0; i--) { + sndFileSizeByteArray[i] = (byte)(sndFileSize & 0xFF); + sndFileSize >>= 8; + } + Os.write(clientFd, sndFileSizeByteArray, 0, sndFileSizeByteArray.length); + + // copy data from file to socket + FileUtils.copy(fileInputStream.getFD(), clientFd, src.length(), null, null, null); + + fileInputStream.close(); + Os.close(clientFd); + + srv.join(); + + // read test data from dest file + actual = readFile(dest); + assertArrayEquals(expected, actual); + } + } + @Test public void testIsFilenameSafe() throws Exception { assertTrue(FileUtils.isFilenameSafe(new File("foobar"))); diff --git a/data/etc/com.android.settings.xml b/data/etc/com.android.settings.xml index dcc96861bc31..fbe1b8e65171 100644 --- a/data/etc/com.android.settings.xml +++ b/data/etc/com.android.settings.xml @@ -48,6 +48,7 @@ <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> <permission name="android.permission.READ_SEARCH_INDEXABLES"/> <permission name="android.permission.REBOOT"/> + <permission name="android.permission.RECOVERY"/> <permission name="android.permission.STATUS_BAR"/> <permission name="android.permission.SUGGEST_MANUAL_TIME_AND_ZONE"/> <permission name="android.permission.TETHER_PRIVILEGED"/> diff --git a/data/fonts/Android.bp b/data/fonts/Android.bp index 3dd9ba9db1d9..f403b4f5353a 100644 --- a/data/fonts/Android.bp +++ b/data/fonts/Android.bp @@ -57,3 +57,9 @@ prebuilt_etc { name: "font_fallback.xml", src: "font_fallback.xml", } + +///////////////////////////////// +// Move `fontchain_lint` to `core/tasks/fontchain_lint.mk`. +// Because `system.img` is a dependency of `fontchain_lint`, it cannot be +// converted to Android.bp for now. +// After system.img can be generated by Soong, then it can be converted to Android.bp. diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk deleted file mode 100644 index a322b829932b..000000000000 --- a/data/fonts/Android.mk +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright (C) 2011 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH := $(call my-dir) - -# Run sanity tests on fonts on checkbuild -checkbuild: fontchain_lint - -FONTCHAIN_LINTER := $(HOST_OUT_EXECUTABLES)/fontchain_linter -ifeq ($(MINIMAL_FONT_FOOTPRINT),true) -CHECK_EMOJI := false -else -CHECK_EMOJI := true -endif - -fontchain_lint_timestamp := $(call intermediates-dir-for,PACKAGING,fontchain_lint)/stamp - -.PHONY: fontchain_lint -fontchain_lint: $(fontchain_lint_timestamp) - -fontchain_lint_deps := \ - external/unicode/DerivedAge.txt \ - external/unicode/emoji-data.txt \ - external/unicode/emoji-sequences.txt \ - external/unicode/emoji-variation-sequences.txt \ - external/unicode/emoji-zwj-sequences.txt \ - external/unicode/additions/emoji-data.txt \ - external/unicode/additions/emoji-sequences.txt \ - external/unicode/additions/emoji-zwj-sequences.txt \ - -$(fontchain_lint_timestamp): $(FONTCHAIN_LINTER) $(TARGET_OUT)/etc/fonts.xml $(PRODUCT_OUT)/system.img $(fontchain_lint_deps) - @echo Running fontchain lint - $(FONTCHAIN_LINTER) $(TARGET_OUT) $(CHECK_EMOJI) external/unicode - touch $@ diff --git a/keystore/java/android/security/Authorization.java b/keystore/java/android/security/Authorization.java index 4ec5e1b67c5d..6404c4bc33d6 100644 --- a/keystore/java/android/security/Authorization.java +++ b/keystore/java/android/security/Authorization.java @@ -100,12 +100,14 @@ public class Authorization { * * @param userId - the user's Android user ID * @param unlockingSids - list of biometric SIDs with which the device may be unlocked again + * @param weakUnlockEnabled - true if non-strong biometric or trust agent unlock is enabled * @return 0 if successful or a {@code ResponseCode}. */ - public static int onDeviceLocked(int userId, @NonNull long[] unlockingSids) { + public static int onDeviceLocked(int userId, @NonNull long[] unlockingSids, + boolean weakUnlockEnabled) { StrictMode.noteDiskWrite(); try { - getService().onDeviceLocked(userId, unlockingSids); + getService().onDeviceLocked(userId, unlockingSids, weakUnlockEnabled); return 0; } catch (RemoteException | NullPointerException e) { Log.w(TAG, "Can not connect to keystore", e); diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 1dd22cf43c5c..a733d1772757 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -218,7 +218,7 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) { * stencil buffer may be needed. Views that use a functor to draw will be forced onto a layer. */ void RenderNode::prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer) { - if (mDamageGenerationId == info.damageGenerationId) { + if (mDamageGenerationId == info.damageGenerationId && mDamageGenerationId != 0) { // We hit the same node a second time in the same tree. We don't know the minimal // damage rect anymore, so just push the biggest we can onto our parent's transform // We push directly onto parent in case we are clipped to bounds but have moved position. diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index 572635a9bd45..4d03bf189a84 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -263,7 +263,7 @@ private: DisplayList mDisplayList; DisplayList mStagingDisplayList; - int64_t mDamageGenerationId; + int64_t mDamageGenerationId = 0; friend class AnimatorManager; AnimatorManager mAnimatorManager; diff --git a/libs/hwui/jni/PathMeasure.cpp b/libs/hwui/jni/PathMeasure.cpp index acf893e9544c..79acb6cc35e5 100644 --- a/libs/hwui/jni/PathMeasure.cpp +++ b/libs/hwui/jni/PathMeasure.cpp @@ -17,7 +17,11 @@ #include "GraphicsJNI.h" +#include "SkMatrix.h" +#include "SkPath.h" #include "SkPathMeasure.h" +#include "SkPoint.h" +#include "SkScalar.h" /* We declare an explicit pair, so that we don't have to rely on the java client to be sure not to edit the path while we have an active measure diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index cf31173d266e..56f1e64396af 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -115,7 +115,7 @@ IRenderPipeline::DrawResult SkiaOpenGLPipeline::draw( const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo, const std::vector<sp<RenderNode>>& renderNodes, FrameInfoVisualizer* profiler, - const HardwareBufferRenderParams& bufferParams) { + const HardwareBufferRenderParams& bufferParams, std::mutex& profilerLock) { if (!isCapturingSkp() && !mHardwareBuffer) { mEglManager.damageFrame(frame, dirty); } @@ -167,6 +167,7 @@ IRenderPipeline::DrawResult SkiaOpenGLPipeline::draw( // Draw visual debugging features if (CC_UNLIKELY(Properties::showDirtyRegions || ProfileType::None != Properties::getProfileType())) { + std::scoped_lock lock(profilerLock); SkCanvas* profileCanvas = surface->getCanvas(); SkiaProfileRenderer profileRenderer(profileCanvas, frame.width(), frame.height()); profiler->draw(profileRenderer); diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h index f0461bef170c..0325593f7170 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h @@ -42,7 +42,8 @@ public: const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo, const std::vector<sp<RenderNode> >& renderNodes, FrameInfoVisualizer* profiler, - const renderthread::HardwareBufferRenderParams& bufferParams) override; + const renderthread::HardwareBufferRenderParams& bufferParams, + std::mutex& profilerLock) override; GrSurfaceOrigin getSurfaceOrigin() override { return kBottomLeft_GrSurfaceOrigin; } bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) override; diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index 86096d5bd01c..7d192321f13e 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -75,7 +75,7 @@ IRenderPipeline::DrawResult SkiaVulkanPipeline::draw( const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo, const std::vector<sp<RenderNode>>& renderNodes, FrameInfoVisualizer* profiler, - const HardwareBufferRenderParams& bufferParams) { + const HardwareBufferRenderParams& bufferParams, std::mutex& profilerLock) { sk_sp<SkSurface> backBuffer; SkMatrix preTransform; if (mHardwareBuffer) { @@ -103,6 +103,7 @@ IRenderPipeline::DrawResult SkiaVulkanPipeline::draw( // Draw visual debugging features if (CC_UNLIKELY(Properties::showDirtyRegions || ProfileType::None != Properties::getProfileType())) { + std::scoped_lock lock(profilerLock); SkCanvas* profileCanvas = backBuffer->getCanvas(); SkAutoCanvasRestore saver(profileCanvas, true); profileCanvas->concat(mVkSurface->getCurrentPreTransform()); diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h index 284cde537ec0..37b86f136fbb 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h @@ -42,7 +42,8 @@ public: const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo, const std::vector<sp<RenderNode> >& renderNodes, FrameInfoVisualizer* profiler, - const renderthread::HardwareBufferRenderParams& bufferParams) override; + const renderthread::HardwareBufferRenderParams& bufferParams, + std::mutex& profilerLock) override; GrSurfaceOrigin getSurfaceOrigin() override { return kTopLeft_GrSurfaceOrigin; } bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) override; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 618c896afa96..f6907831e11f 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -592,14 +592,10 @@ void CanvasContext::draw(bool solelyTextureViewUpdates) { { // FrameInfoVisualizer accesses the frame events, which cannot be mutated mid-draw // or it can lead to memory corruption. - // This lock is overly broad, but it's the quickest fix since this mutex is otherwise - // not visible to IRenderPipeline much less FrameInfoVisualizer. And since this is - // the thread we're primarily concerned about being responsive, this being too broad - // shouldn't pose a performance issue. - std::scoped_lock lock(mFrameMetricsReporterMutex); drawResult = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue, mContentDrawBounds, mOpaque, - mLightInfo, mRenderNodes, &(profiler()), mBufferParams); + mLightInfo, mRenderNodes, &(profiler()), mBufferParams, + profilerLock()); } uint64_t frameCompleteNr = getFrameNumber(); @@ -729,7 +725,7 @@ void CanvasContext::draw(bool solelyTextureViewUpdates) { mCurrentFrameInfo->markFrameCompleted(); mCurrentFrameInfo->set(FrameInfoIndex::GpuCompleted) = mCurrentFrameInfo->get(FrameInfoIndex::FrameCompleted); - std::scoped_lock lock(mFrameMetricsReporterMutex); + std::scoped_lock lock(mFrameInfoMutex); mJankTracker.finishFrame(*mCurrentFrameInfo, mFrameMetricsReporter, frameCompleteNr, mSurfaceControlGenerationId); } @@ -758,7 +754,7 @@ void CanvasContext::draw(bool solelyTextureViewUpdates) { void CanvasContext::reportMetricsWithPresentTime() { { // acquire lock - std::scoped_lock lock(mFrameMetricsReporterMutex); + std::scoped_lock lock(mFrameInfoMutex); if (mFrameMetricsReporter == nullptr) { return; } @@ -793,7 +789,7 @@ void CanvasContext::reportMetricsWithPresentTime() { forthBehind->set(FrameInfoIndex::DisplayPresentTime) = presentTime; { // acquire lock - std::scoped_lock lock(mFrameMetricsReporterMutex); + std::scoped_lock lock(mFrameInfoMutex); if (mFrameMetricsReporter != nullptr) { mFrameMetricsReporter->reportFrameMetrics(forthBehind->data(), true /*hasPresentTime*/, frameNumber, surfaceControlId); @@ -802,7 +798,7 @@ void CanvasContext::reportMetricsWithPresentTime() { } void CanvasContext::addFrameMetricsObserver(FrameMetricsObserver* observer) { - std::scoped_lock lock(mFrameMetricsReporterMutex); + std::scoped_lock lock(mFrameInfoMutex); if (mFrameMetricsReporter.get() == nullptr) { mFrameMetricsReporter.reset(new FrameMetricsReporter()); } @@ -816,7 +812,7 @@ void CanvasContext::addFrameMetricsObserver(FrameMetricsObserver* observer) { } void CanvasContext::removeFrameMetricsObserver(FrameMetricsObserver* observer) { - std::scoped_lock lock(mFrameMetricsReporterMutex); + std::scoped_lock lock(mFrameInfoMutex); if (mFrameMetricsReporter.get() != nullptr) { mFrameMetricsReporter->removeObserver(observer); if (!mFrameMetricsReporter->hasObservers()) { @@ -853,7 +849,7 @@ void CanvasContext::onSurfaceStatsAvailable(void* context, int32_t surfaceContro FrameInfo* frameInfo = instance->getFrameInfoFromLast4(frameNumber, surfaceControlId); if (frameInfo != nullptr) { - std::scoped_lock lock(instance->mFrameMetricsReporterMutex); + std::scoped_lock lock(instance->mFrameInfoMutex); frameInfo->set(FrameInfoIndex::FrameCompleted) = std::max(gpuCompleteTime, frameInfo->get(FrameInfoIndex::SwapBuffersCompleted)); frameInfo->set(FrameInfoIndex::GpuCompleted) = std::max( diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 32ac5af94c14..3978fbcc3a21 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -161,6 +161,7 @@ public: void notifyFramePending(); FrameInfoVisualizer& profiler() { return mProfiler; } + std::mutex& profilerLock() { return mFrameInfoMutex; } void dumpFrames(int fd); void resetFrameStats(); @@ -340,8 +341,8 @@ private: JankTracker mJankTracker; FrameInfoVisualizer mProfiler; std::unique_ptr<FrameMetricsReporter> mFrameMetricsReporter - GUARDED_BY(mFrameMetricsReporterMutex); - std::mutex mFrameMetricsReporterMutex; + GUARDED_BY(mFrameInfoMutex); + std::mutex mFrameInfoMutex; std::set<RenderNode*> mPrefetchedLayers; diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h index 6c2cb9d71c55..023c29a3ab85 100644 --- a/libs/hwui/renderthread/IRenderPipeline.h +++ b/libs/hwui/renderthread/IRenderPipeline.h @@ -67,7 +67,8 @@ public: const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo, const std::vector<sp<RenderNode>>& renderNodes, FrameInfoVisualizer* profiler, - const HardwareBufferRenderParams& bufferParams) = 0; + const HardwareBufferRenderParams& bufferParams, + std::mutex& profilerLock) = 0; virtual bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) = 0; virtual DeferredLayerUpdater* createTextureLayer() = 0; diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java index 46db77708521..5e40eee26886 100644 --- a/media/java/android/media/MediaFormat.java +++ b/media/java/android/media/MediaFormat.java @@ -16,6 +16,10 @@ package android.media; +import static com.android.media.codec.flags.Flags.FLAG_CODEC_IMPORTANCE; +import static com.android.media.codec.flags.Flags.FLAG_LARGE_AUDIO_FRAME; + +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -118,6 +122,10 @@ import java.util.stream.Collectors; * <tr><td>{@link #KEY_MPEGH_REFERENCE_CHANNEL_LAYOUT}</td> * <td>Integer</td><td><b>decoder-only</b>, optional, if content is MPEG-H audio, * specifies the preferred reference channel layout of the stream.</td></tr> + * <tr><td>{@link #KEY_MAX_BUFFER_BATCH_OUTPUT_SIZE}</td><td>Integer</td><td>optional, used with + * large audio frame support, specifies max size of output buffer in bytes.</td></tr> + * <tr><td>{@link #KEY_BUFFER_BATCH_THRESHOLD_OUTPUT_SIZE}</td><td>Integer</td><td>optional, + * used with large audio frame support, specifies threshold output size in bytes.</td></tr> * </table> * * Subtitle formats have the following keys: @@ -456,6 +464,50 @@ public final class MediaFormat { public static final String KEY_MAX_INPUT_SIZE = "max-input-size"; /** + * A key describing the maximum output buffer size in bytes when using + * large buffer mode containing multiple access units. + * + * When not-set - codec functions with one access-unit per frame. + * When set less than the size of two access-units - will make codec + * operate in single access-unit per output frame. + * When set to a value too big - The component or the framework will + * override this value to a reasonable max size not exceeding typical + * 10 seconds of data (device dependent) when set to a value larger than + * that. The value final value used will be returned in the output format. + * + * The associated value is an integer + * + * @see FEATURE_MultipleFrames + */ + @FlaggedApi(FLAG_LARGE_AUDIO_FRAME) + public static final String KEY_BUFFER_BATCH_MAX_OUTPUT_SIZE = "buffer-batch-max-output-size"; + + /** + * A key describing the threshold output size in bytes when using large buffer + * mode containing multiple access units. + * + * This is an optional parameter. + * + * If not set - the component can set this to a reasonable value. + * If set larger than max size, the components will + * clip this setting to maximum buffer batching output size. + * + * The component will return a partial output buffer if the output buffer reaches or + * surpass this limit. + * + * Threshold size should be always less or equal to KEY_MAX_BUFFER_BATCH_OUTPUT_SIZE. + * The final setting of this value as determined by the component will be returned + * in the output format + * + * The associated value is an integer + * + * @see FEATURE_MultipleFrames + */ + @FlaggedApi(FLAG_LARGE_AUDIO_FRAME) + public static final String KEY_BUFFER_BATCH_THRESHOLD_OUTPUT_SIZE = + "buffer-batch-threshold-output-size"; + + /** * A key describing the pixel aspect ratio width. * The associated value is an integer */ @@ -1635,6 +1687,34 @@ public final class MediaFormat { */ public static final String KEY_ALLOW_FRAME_DROP = "allow-frame-drop"; + /** + * A key describing the desired codec importance for the application. + * <p> + * The associated value is a positive integer including zero. + * Higher value means lesser importance. + * <p> + * The resource manager may use the codec importance, along with other factors + * when reclaiming codecs from an application. + * The specifics of reclaim policy is device dependent, but specifying the codec importance, + * will allow the resource manager to prioritize reclaiming less important codecs + * (assigned higher values) from the (reclaim) requesting application first. + * So, the codec importance is only relevant within the context of that application. + * <p> + * The codec importance can be set: + * <ul> + * <li>through {@link MediaCodec#configure}. </li> + * <li>through {@link MediaCodec#setParameters} if the codec has been configured already, + * which allows the users to change the codec importance multiple times. + * </ul> + * Any change/update in codec importance is guaranteed upon the completion of the function call + * that sets the codec importance. So, in case of concurrent codec operations, + * make sure to wait for the change in codec importance, before using another codec. + * Note that unless specified, by default the codecs will have highest importance (of value 0). + * + */ + @FlaggedApi(FLAG_CODEC_IMPORTANCE) + public static final String KEY_IMPORTANCE = "importance"; + /* package private */ MediaFormat(@NonNull Map<String, Object> map) { mMap = map; } diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 1ee5aa36740c..0d9acf22071b 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -1156,6 +1156,7 @@ public class MediaPlayer extends PlayerBase setDataSource(afd); return true; } catch (NullPointerException | SecurityException | IOException ex) { + Log.w(TAG, "Error setting data source via ContentResolver", ex); return false; } } diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java index 1d433e767e5b..943e3fc27ebb 100644 --- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java +++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java @@ -59,7 +59,7 @@ public abstract class AbstractWifiMacAddressPreferenceController @Override public boolean isAvailable() { - return true; + return mWifiManager != null; } @Override @@ -70,10 +70,8 @@ public abstract class AbstractWifiMacAddressPreferenceController @Override public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); - if (isAvailable()) { - mWifiMacAddress = screen.findPreference(KEY_WIFI_MAC_ADDRESS); - updateConnectivity(); - } + mWifiMacAddress = screen.findPreference(KEY_WIFI_MAC_ADDRESS); + updateConnectivity(); } @Override @@ -84,16 +82,16 @@ public abstract class AbstractWifiMacAddressPreferenceController @SuppressLint("HardwareIds") @Override protected void updateConnectivity() { + if (mWifiManager == null || mWifiMacAddress == null) { + return; + } + final String[] macAddresses = mWifiManager.getFactoryMacAddresses(); String macAddress = null; if (macAddresses != null && macAddresses.length > 0) { macAddress = macAddresses[0]; } - if (mWifiMacAddress == null) { - return; - } - if (TextUtils.isEmpty(macAddress) || macAddress.equals(WifiInfo.DEFAULT_MAC_ADDRESS)) { mWifiMacAddress.setSummary(R.string.status_unavailable); } else { diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 91c72b543cc4..bc93c5b70374 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -280,6 +280,7 @@ public class SettingsBackupTest { Settings.Global.ENABLE_ADB_INCREMENTAL_INSTALL_DEFAULT, Settings.Global.ENABLE_MULTI_SLOT_TIMEOUT_MILLIS, Settings.Global.ENHANCED_4G_MODE_ENABLED, + Settings.Global.ENABLE_16K_PAGES, // Added for 16K developer option Settings.Global.EPHEMERAL_COOKIE_MAX_SIZE_BYTES, Settings.Global.ERROR_LOGCAT_PREFIX, Settings.Global.EUICC_PROVISIONED, diff --git a/services/core/Android.bp b/services/core/Android.bp index 21cfd242cf45..2dda76e06c56 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -190,6 +190,7 @@ java_library_static { "com.android.sysprop.watchdog", "ImmutabilityAnnotation", "securebox", + "net_flags_lib", ], javac_shard_size: 50, javacflags: [ diff --git a/services/core/java/com/android/server/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java index 5cdfca7392e3..926d7a4d3ea6 100644 --- a/services/core/java/com/android/server/BootReceiver.java +++ b/services/core/java/com/android/server/BootReceiver.java @@ -48,8 +48,6 @@ import com.android.internal.util.XmlUtils; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; import com.android.server.am.DropboxRateLimiter; -import com.android.server.os.TombstoneProtos; -import com.android.server.os.TombstoneProtos.Tombstone; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -62,14 +60,11 @@ import java.io.FileOutputStream; import java.io.IOException; import java.nio.file.Files; import java.nio.file.attribute.PosixFilePermissions; -import java.util.AbstractMap; import java.util.HashMap; import java.util.Iterator; -import java.util.Map; import java.util.concurrent.locks.ReentrantLock; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; /** * Performs a number of miscellaneous, non-system-critical actions @@ -332,12 +327,12 @@ public class BootReceiver extends BroadcastReceiver { * * @param ctx Context * @param tombstone path to the tombstone - * @param tombstoneProto the parsed proto tombstone + * @param proto whether the tombstone is stored as proto * @param processName the name of the process corresponding to the tombstone * @param tmpFileLock the lock for reading/writing tmp files */ public static void addTombstoneToDropBox( - Context ctx, File tombstone, Tombstone tombstoneProto, String processName, + Context ctx, File tombstone, boolean proto, String processName, ReentrantLock tmpFileLock) { final DropBoxManager db = ctx.getSystemService(DropBoxManager.class); if (db == null) { @@ -347,33 +342,31 @@ public class BootReceiver extends BroadcastReceiver { // Check if we should rate limit and abort early if needed. DropboxRateLimiter.RateLimitResult rateLimitResult = - sDropboxRateLimiter.shouldRateLimit(TAG_TOMBSTONE_PROTO_WITH_HEADERS, processName); + sDropboxRateLimiter.shouldRateLimit( + proto ? TAG_TOMBSTONE_PROTO_WITH_HEADERS : TAG_TOMBSTONE, processName); if (rateLimitResult.shouldRateLimit()) return; HashMap<String, Long> timestamps = readTimestamps(); try { - // Remove the memory data from the proto. - Tombstone tombstoneProtoWithoutMemory = removeMemoryFromTombstone(tombstoneProto); - - final byte[] tombstoneBytes = tombstoneProtoWithoutMemory.toByteArray(); - - // Use JNI to call the c++ proto to text converter and add the headers to the tombstone. - String tombstoneWithoutMemory = new StringBuilder(getBootHeadersToLogAndUpdate()) - .append(rateLimitResult.createHeader()) - .append(getTombstoneText(tombstoneBytes)) - .toString(); - - // Add the tombstone without memory data to dropbox. - db.addText(TAG_TOMBSTONE, tombstoneWithoutMemory); - - // Add the tombstone proto to dropbox. - if (recordFileTimestamp(tombstone, timestamps)) { - tmpFileLock.lock(); - try { - addAugmentedProtoToDropbox(tombstone, tombstoneBytes, db, rateLimitResult); - } finally { - tmpFileLock.unlock(); + if (proto) { + if (recordFileTimestamp(tombstone, timestamps)) { + // We need to attach the count indicating the number of dropped dropbox entries + // due to rate limiting. Do this by enclosing the proto tombsstone in a + // container proto that has the dropped entry count and the proto tombstone as + // bytes (to avoid the complexity of reading and writing nested protos). + tmpFileLock.lock(); + try { + addAugmentedProtoToDropbox(tombstone, db, rateLimitResult); + } finally { + tmpFileLock.unlock(); + } } + } else { + // Add the header indicating how many events have been dropped due to rate limiting. + final String headers = getBootHeadersToLogAndUpdate() + + rateLimitResult.createHeader(); + addFileToDropBox(db, timestamps, headers, tombstone.getPath(), LOG_SIZE, + TAG_TOMBSTONE); } } catch (IOException e) { Slog.e(TAG, "Can't log tombstone", e); @@ -382,8 +375,11 @@ public class BootReceiver extends BroadcastReceiver { } private static void addAugmentedProtoToDropbox( - File tombstone, byte[] tombstoneBytes, DropBoxManager db, + File tombstone, DropBoxManager db, DropboxRateLimiter.RateLimitResult rateLimitResult) throws IOException { + // Read the proto tombstone file as bytes. + final byte[] tombstoneBytes = Files.readAllBytes(tombstone.toPath()); + final File tombstoneProtoWithHeaders = File.createTempFile( tombstone.getName(), ".tmp", TOMBSTONE_TMP_DIR); Files.setPosixFilePermissions( @@ -416,8 +412,6 @@ public class BootReceiver extends BroadcastReceiver { } } - private static native String getTombstoneText(byte[] tombstoneBytes); - private static void addLastkToDropBox( DropBoxManager db, HashMap<String, Long> timestamps, String headers, String footers, String filename, int maxSize, @@ -435,31 +429,6 @@ public class BootReceiver extends BroadcastReceiver { addFileWithFootersToDropBox(db, timestamps, headers, footers, filename, maxSize, tag); } - /** Removes memory information from the Tombstone proto. */ - @VisibleForTesting - public static Tombstone removeMemoryFromTombstone(Tombstone tombstoneProto) { - Tombstone.Builder tombstoneBuilder = tombstoneProto.toBuilder() - .clearMemoryMappings() - .clearThreads() - .putAllThreads(tombstoneProto.getThreadsMap().entrySet() - .stream() - .map(BootReceiver::clearMemoryDump) - .collect(Collectors.toMap(e->e.getKey(), e->e.getValue()))); - - if (tombstoneProto.hasSignalInfo()) { - tombstoneBuilder.setSignalInfo( - tombstoneProto.getSignalInfo().toBuilder().clearFaultAdjacentMetadata()); - } - - return tombstoneBuilder.build(); - } - - private static AbstractMap.SimpleEntry<Integer, TombstoneProtos.Thread> clearMemoryDump( - Map.Entry<Integer, TombstoneProtos.Thread> e) { - return new AbstractMap.SimpleEntry<Integer, TombstoneProtos.Thread>( - e.getKey(), e.getValue().toBuilder().clearMemoryDump().build()); - } - private static void addFileToDropBox( DropBoxManager db, HashMap<String, Long> timestamps, String headers, String filename, int maxSize, String tag) throws IOException { diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index f95d6ea117ef..a564b7d740e5 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -164,6 +164,7 @@ public class Watchdog implements Dumpable { public static final String[] AIDL_INTERFACE_PREFIXES_OF_INTEREST = new String[] { "android.hardware.audio.core.IModule/", "android.hardware.audio.core.IConfig/", + "android.hardware.audio.effect.IFactory/", "android.hardware.biometrics.face.IFace/", "android.hardware.biometrics.fingerprint.IFingerprint/", "android.hardware.bluetooth.IBluetoothHci/", diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 9c82a8d8e45a..b8d2284275ea 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -2861,7 +2861,11 @@ public final class ProcessList { return true; } - private static void freezeBinderAndPackageCgroup(ArrayList<Pair<ProcessRecord, Boolean>> procs, + private static boolean unfreezePackageCgroup(int packageUID) { + return freezePackageCgroup(packageUID, false); + } + + private static void freezeBinderAndPackageCgroup(List<Pair<ProcessRecord, Boolean>> procs, int packageUID) { // Freeze all binder processes under the target UID (whose cgroup is about to be frozen). // Since we're going to kill these, we don't need to unfreze them later. @@ -2869,12 +2873,9 @@ public final class ProcessList { // processes (forks) should not be Binder users. int N = procs.size(); for (int i = 0; i < N; i++) { - final int uid = procs.get(i).first.uid; final int pid = procs.get(i).first.getPid(); int nRetries = 0; - // We only freeze the cgroup of the target package, so we do not need to freeze the - // Binder interfaces of dependant processes in other UIDs. - if (pid > 0 && uid == packageUID) { + if (pid > 0) { try { int rc; do { @@ -2888,12 +2889,19 @@ public final class ProcessList { } // We freeze the entire UID (parent) cgroup so that newly-specialized processes also freeze - // despite being added to a new child cgroup. The cgroups of package dependant processes are - // not frozen, since it's possible this would freeze processes with no dependency on the - // package being killed here. + // despite being added to a child cgroup created after this call that would otherwise be + // unfrozen. freezePackageCgroup(packageUID, true); } + private static List<Pair<ProcessRecord, Boolean>> getUIDSublist( + List<Pair<ProcessRecord, Boolean>> procs, int startIdx) { + final int uid = procs.get(startIdx).first.uid; + int endIdx = startIdx + 1; + while (endIdx < procs.size() && procs.get(endIdx).first.uid == uid) ++endIdx; + return procs.subList(startIdx, endIdx); + } + @GuardedBy({"mService", "mProcLock"}) boolean killPackageProcessesLSP(String packageName, int appId, int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart, @@ -2989,25 +2997,36 @@ public final class ProcessList { } } - final int packageUID = UserHandle.getUid(userId, appId); - final boolean doFreeze = appId >= Process.FIRST_APPLICATION_UID - && appId <= Process.LAST_APPLICATION_UID; - if (doFreeze) { - freezeBinderAndPackageCgroup(procs, packageUID); + final boolean killingUserApp = appId >= Process.FIRST_APPLICATION_UID + && appId <= Process.LAST_APPLICATION_UID; + + if (killingUserApp) { + procs.sort((o1, o2) -> Integer.compare(o1.first.uid, o2.first.uid)); } - int N = procs.size(); - for (int i=0; i<N; i++) { - final Pair<ProcessRecord, Boolean> proc = procs.get(i); - removeProcessLocked(proc.first, callerWillRestart, allowRestart || proc.second, - reasonCode, subReason, reason, !doFreeze /* async */); + int idx = 0; + while (idx < procs.size()) { + final List<Pair<ProcessRecord, Boolean>> uidProcs = getUIDSublist(procs, idx); + final int packageUID = uidProcs.get(0).first.uid; + + // Do not freeze for system apps or for dependencies of the targeted package, but + // make sure to freeze the targeted package for all users if called with USER_ALL. + final boolean doFreeze = killingUserApp && UserHandle.getAppId(packageUID) == appId; + + if (doFreeze) freezeBinderAndPackageCgroup(uidProcs, packageUID); + + for (Pair<ProcessRecord, Boolean> proc : uidProcs) { + removeProcessLocked(proc.first, callerWillRestart, allowRestart || proc.second, + reasonCode, subReason, reason, !doFreeze /* async */); + } + killAppZygotesLocked(packageName, appId, userId, false /* force */); + + if (doFreeze) unfreezePackageCgroup(packageUID); + + idx += uidProcs.size(); } - killAppZygotesLocked(packageName, appId, userId, false /* force */); mService.updateOomAdjLocked(OOM_ADJ_REASON_PROCESS_END); - if (doFreeze) { - freezePackageCgroup(packageUID, false); - } - return N > 0; + return procs.size() > 0; } @GuardedBy("mService") diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index b9535d6afc6c..43ecec4105aa 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -1024,11 +1024,9 @@ public class AudioDeviceBroker { private void initAudioHalBluetoothState() { synchronized (mBluetoothAudioStateLock) { mBluetoothScoOnApplied = false; - AudioSystem.setParameters("BT_SCO=off"); mBluetoothA2dpSuspendedApplied = false; - AudioSystem.setParameters("A2dpSuspended=false"); mBluetoothLeSuspendedApplied = false; - AudioSystem.setParameters("LeAudioSuspended=false"); + reapplyAudioHalBluetoothState(); } } @@ -1091,6 +1089,34 @@ public class AudioDeviceBroker { } } + @GuardedBy("mBluetoothAudioStateLock") + private void reapplyAudioHalBluetoothState() { + if (AudioService.DEBUG_COMM_RTE) { + Log.v(TAG, "reapplyAudioHalBluetoothState() mBluetoothScoOnApplied: " + + mBluetoothScoOnApplied + ", mBluetoothA2dpSuspendedApplied: " + + mBluetoothA2dpSuspendedApplied + ", mBluetoothLeSuspendedApplied: " + + mBluetoothLeSuspendedApplied); + } + // Note: the order of parameters is important. + if (mBluetoothScoOnApplied) { + AudioSystem.setParameters("A2dpSuspended=true"); + AudioSystem.setParameters("LeAudioSuspended=true"); + AudioSystem.setParameters("BT_SCO=on"); + } else { + AudioSystem.setParameters("BT_SCO=off"); + if (mBluetoothA2dpSuspendedApplied) { + AudioSystem.setParameters("A2dpSuspended=true"); + } else { + AudioSystem.setParameters("A2dpSuspended=false"); + } + if (mBluetoothLeSuspendedApplied) { + AudioSystem.setParameters("LeAudioSuspended=true"); + } else { + AudioSystem.setParameters("LeAudioSuspended=false"); + } + } + } + /*package*/ void setBluetoothScoOn(boolean on, String eventSource) { if (AudioService.DEBUG_COMM_RTE) { Log.v(TAG, "setBluetoothScoOn: " + on + " " + eventSource); @@ -1727,6 +1753,9 @@ public class AudioDeviceBroker { initRoutingStrategyIds(); updateActiveCommunicationDevice(); mDeviceInventory.onRestoreDevices(); + synchronized (mBluetoothAudioStateLock) { + reapplyAudioHalBluetoothState(); + } mBtHelper.onAudioServerDiedRestoreA2dp(); updateCommunicationRoute("MSG_RESTORE_DEVICES"); } diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index ad090829a2f6..c81ce26b6403 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -16,6 +16,7 @@ package com.android.server.locksettings; +import static android.security.Flags.reportPrimaryAuthAttempts; import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE; import static android.Manifest.permission.MANAGE_BIOMETRIC; import static android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS; @@ -90,6 +91,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.IProgressListener; import android.os.Process; +import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; @@ -135,6 +137,7 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import com.android.internal.widget.ICheckCredentialProgressCallback; import com.android.internal.widget.ILockSettings; +import com.android.internal.widget.ILockSettingsStateListener; import com.android.internal.widget.IWeakEscrowTokenActivatedListener; import com.android.internal.widget.IWeakEscrowTokenRemovedListener; import com.android.internal.widget.LockPatternUtils; @@ -327,6 +330,9 @@ public class LockSettingsService extends ILockSettings.Stub { private HashMap<UserHandle, UserManager> mUserManagerCache = new HashMap<>(); + private final RemoteCallbackList<ILockSettingsStateListener> mLockSettingsStateListeners = + new RemoteCallbackList<>(); + // This class manages life cycle events for encrypted users on File Based Encryption (FBE) // devices. The most basic of these is to show/hide notifications about missing features until // the user unlocks the account and credential-encrypted storage is available. @@ -2342,9 +2348,37 @@ public class LockSettingsService extends ILockSettings.Stub { requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_LOCKOUT, userId); } } + if (reportPrimaryAuthAttempts()) { + final boolean success = + response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK; + notifyLockSettingsStateListeners(success, userId); + } return response; } + private void notifyLockSettingsStateListeners(boolean success, int userId) { + int i = mLockSettingsStateListeners.beginBroadcast(); + try { + while (i > 0) { + i--; + try { + if (success) { + mLockSettingsStateListeners.getBroadcastItem(i) + .onAuthenticationSucceeded(userId); + } else { + mLockSettingsStateListeners.getBroadcastItem(i) + .onAuthenticationFailed(userId); + } + } catch (RemoteException e) { + Slog.e(TAG, "Exception while notifying LockSettingsStateListener:" + + " success = " + success + ", userId = " + userId, e); + } + } + } finally { + mLockSettingsStateListeners.finishBroadcast(); + } + } + @Override public VerifyCredentialResponse verifyTiedProfileChallenge(LockscreenCredential credential, int userId, @LockPatternUtils.VerifyFlag int flags) { @@ -3662,6 +3696,18 @@ public class LockSettingsService extends ILockSettings.Stub { public void refreshStrongAuthTimeout(int userId) { mStrongAuth.refreshStrongAuthTimeout(userId); } + + @Override + public void registerLockSettingsStateListener( + @NonNull ILockSettingsStateListener listener) { + mLockSettingsStateListeners.register(listener); + } + + @Override + public void unregisterLockSettingsStateListener( + @NonNull ILockSettingsStateListener listener) { + mLockSettingsStateListeners.unregister(listener); + } } private class RebootEscrowCallbacks implements RebootEscrowManager.Callbacks { diff --git a/services/core/java/com/android/server/net/Android.bp b/services/core/java/com/android/server/net/Android.bp new file mode 100644 index 000000000000..71d8e6ba367e --- /dev/null +++ b/services/core/java/com/android/server/net/Android.bp @@ -0,0 +1,10 @@ +aconfig_declarations { + name: "net_flags", + package: "com.android.server.net", + srcs: ["*.aconfig"], +} + +java_aconfig_library { + name: "net_flags_lib", + aconfig_declarations: "net_flags", +} diff --git a/services/core/java/com/android/server/net/NetworkManagementService.java b/services/core/java/com/android/server/net/NetworkManagementService.java index 681d1a0ee10a..d25f52973085 100644 --- a/services/core/java/com/android/server/net/NetworkManagementService.java +++ b/services/core/java/com/android/server/net/NetworkManagementService.java @@ -17,6 +17,7 @@ package com.android.server.net; import static android.Manifest.permission.CONNECTIVITY_INTERNAL; +import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND; import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE; import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY; import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE; @@ -27,6 +28,7 @@ import static android.net.INetd.FIREWALL_CHAIN_NONE; import static android.net.INetd.FIREWALL_DENYLIST; import static android.net.INetd.FIREWALL_RULE_ALLOW; import static android.net.INetd.FIREWALL_RULE_DENY; +import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_BACKGROUND; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE; @@ -187,6 +189,13 @@ public class NetworkManagementService extends INetworkManagementService.Stub { */ @GuardedBy("mRulesLock") private final SparseIntArray mUidFirewallLowPowerStandbyRules = new SparseIntArray(); + + /** + * Contains the per-UID firewall rules that are used when Background chain is enabled. + */ + @GuardedBy("mRulesLock") + private final SparseIntArray mUidFirewallBackgroundRules = new SparseIntArray(); + /** Set of states for the child firewall chains. True if the chain is active. */ @GuardedBy("mRulesLock") final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray(); @@ -449,13 +458,15 @@ public class NetworkManagementService extends INetworkManagementService.Stub { syncFirewallChainLocked(FIREWALL_CHAIN_POWERSAVE, "powersave "); syncFirewallChainLocked(FIREWALL_CHAIN_RESTRICTED, "restricted "); syncFirewallChainLocked(FIREWALL_CHAIN_LOW_POWER_STANDBY, "low power standby "); + syncFirewallChainLocked(FIREWALL_CHAIN_BACKGROUND, FIREWALL_CHAIN_NAME_BACKGROUND); final int[] chains = { FIREWALL_CHAIN_STANDBY, FIREWALL_CHAIN_DOZABLE, FIREWALL_CHAIN_POWERSAVE, FIREWALL_CHAIN_RESTRICTED, - FIREWALL_CHAIN_LOW_POWER_STANDBY + FIREWALL_CHAIN_LOW_POWER_STANDBY, + FIREWALL_CHAIN_BACKGROUND, }; for (int chain : chains) { @@ -1206,6 +1217,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub { return FIREWALL_CHAIN_NAME_RESTRICTED; case FIREWALL_CHAIN_LOW_POWER_STANDBY: return FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY; + case FIREWALL_CHAIN_BACKGROUND: + return FIREWALL_CHAIN_NAME_BACKGROUND; default: throw new IllegalArgumentException("Bad child chain: " + chain); } @@ -1223,6 +1236,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub { return FIREWALL_ALLOWLIST; case FIREWALL_CHAIN_LOW_POWER_STANDBY: return FIREWALL_ALLOWLIST; + case FIREWALL_CHAIN_BACKGROUND: + return FIREWALL_ALLOWLIST; default: return isFirewallEnabled() ? FIREWALL_ALLOWLIST : FIREWALL_DENYLIST; } @@ -1343,6 +1358,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub { return mUidFirewallRestrictedRules; case FIREWALL_CHAIN_LOW_POWER_STANDBY: return mUidFirewallLowPowerStandbyRules; + case FIREWALL_CHAIN_BACKGROUND: + return mUidFirewallBackgroundRules; case FIREWALL_CHAIN_NONE: return mUidFirewallRules; default: @@ -1395,6 +1412,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub { pw.println(getFirewallChainState(FIREWALL_CHAIN_LOW_POWER_STANDBY)); dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY, mUidFirewallLowPowerStandbyRules); + + pw.print("UID firewall background chain enabled: "); + pw.println(getFirewallChainState(FIREWALL_CHAIN_BACKGROUND)); + dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_BACKGROUND, mUidFirewallBackgroundRules); } pw.print("Firewall enabled: "); pw.println(mFirewallEnabled); @@ -1494,6 +1515,11 @@ public class NetworkManagementService extends INetworkManagementService.Stub { if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because of low power standby"); return true; } + if (getFirewallChainState(FIREWALL_CHAIN_BACKGROUND) + && mUidFirewallBackgroundRules.get(uid) != FIREWALL_RULE_ALLOW) { + if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because it is in background"); + return true; + } if (mUidRejectOnMetered.get(uid)) { if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because of no metered data" + " in the background"); diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java index 4d19eade5a05..8e2d7780204a 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java +++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java @@ -16,6 +16,7 @@ package com.android.server.net; import static android.net.ConnectivityManager.BLOCKED_REASON_NONE; +import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND; import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE; import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY; import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE; @@ -24,6 +25,7 @@ import static android.net.ConnectivityManager.FIREWALL_CHAIN_STANDBY; import static android.net.INetd.FIREWALL_RULE_ALLOW; import static android.net.INetd.FIREWALL_RULE_DENY; import static android.net.NetworkPolicyManager.ALLOWED_REASON_NONE; +import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_BACKGROUND; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE; @@ -42,7 +44,6 @@ import android.util.ArraySet; import android.util.Log; import android.util.Slog; -import com.android.internal.annotations.Keep; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.RingBuffer; import com.android.server.am.ProcessList; @@ -390,6 +391,8 @@ public class NetworkPolicyLogger { return FIREWALL_CHAIN_NAME_RESTRICTED; case FIREWALL_CHAIN_LOW_POWER_STANDBY: return FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY; + case FIREWALL_CHAIN_BACKGROUND: + return FIREWALL_CHAIN_NAME_BACKGROUND; default: return String.valueOf(chain); } @@ -414,7 +417,7 @@ public class NetworkPolicyLogger { private static final Date sDate = new Date(); public LogBuffer(int capacity) { - super(Data.class, capacity); + super(Data::new, Data[]::new, capacity); } public void uidStateChanged(int uid, int procState, long procStateSeq, @@ -690,12 +693,8 @@ public class NetworkPolicyLogger { /** * Container class for all networkpolicy events data. - * - * Note: This class needs to be public for RingBuffer class to be able to create - * new instances of this. */ - @Keep - public static final class Data { + private static final class Data { public int type; public long timeStamp; diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 52734a4ad3b0..b47458b88f40 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -47,6 +47,7 @@ import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_ADMIN_DISAB import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_DATA_SAVER; import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_MASK; import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_USER_RESTRICTED; +import static android.net.ConnectivityManager.BLOCKED_REASON_APP_BACKGROUND; import static android.net.ConnectivityManager.BLOCKED_REASON_APP_STANDBY; import static android.net.ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER; import static android.net.ConnectivityManager.BLOCKED_REASON_DOZE; @@ -54,6 +55,7 @@ import static android.net.ConnectivityManager.BLOCKED_REASON_LOW_POWER_STANDBY; import static android.net.ConnectivityManager.BLOCKED_REASON_NONE; import static android.net.ConnectivityManager.BLOCKED_REASON_RESTRICTED_MODE; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; +import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND; import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE; import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY; import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE; @@ -77,6 +79,7 @@ import static android.net.NetworkPolicyManager.ALLOWED_METERED_REASON_USER_EXEMP import static android.net.NetworkPolicyManager.ALLOWED_REASON_FOREGROUND; import static android.net.NetworkPolicyManager.ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST; import static android.net.NetworkPolicyManager.ALLOWED_REASON_NONE; +import static android.net.NetworkPolicyManager.ALLOWED_REASON_NOT_IN_BACKGROUND; import static android.net.NetworkPolicyManager.ALLOWED_REASON_POWER_SAVE_ALLOWLIST; import static android.net.NetworkPolicyManager.ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST; import static android.net.NetworkPolicyManager.ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS; @@ -96,6 +99,7 @@ import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED; import static android.net.NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_UNMETERED; import static android.net.NetworkPolicyManager.allowedReasonsToString; import static android.net.NetworkPolicyManager.blockedReasonsToString; +import static android.net.NetworkPolicyManager.isProcStateAllowedNetworkWhileBackground; import static android.net.NetworkPolicyManager.isProcStateAllowedWhileIdleOrPowerSaveMode; import static android.net.NetworkPolicyManager.isProcStateAllowedWhileInLowPowerStandby; import static android.net.NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground; @@ -201,12 +205,12 @@ import android.os.Message; import android.os.MessageQueue.IdleHandler; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; +import android.os.PowerExemptionManager; import android.os.PowerExemptionManager.ReasonCode; import android.os.PowerManager; import android.os.PowerManager.ServiceType; import android.os.PowerManagerInternal; import android.os.PowerSaveState; -import android.os.PowerWhitelistManager; import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; @@ -242,6 +246,7 @@ import android.util.SparseBooleanArray; import android.util.SparseIntArray; import android.util.SparseLongArray; import android.util.SparseSetArray; +import android.util.TimeUtils; import android.util.Xml; import com.android.internal.R; @@ -457,6 +462,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { */ private static final int MSG_UIDS_BLOCKED_REASONS_CHANGED = 23; + /** + * Message to update background restriction rules for uids that should lose network access + * due to being in the background. + */ + private static final int MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS = 24; + private static final int UID_MSG_STATE_CHANGED = 100; private static final int UID_MSG_GONE = 101; @@ -475,7 +486,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private ConnectivityManager mConnManager; private PowerManagerInternal mPowerManagerInternal; - private PowerWhitelistManager mPowerWhitelistManager; + private PowerExemptionManager mPowerExemptionManager; @NonNull private final Dependencies mDeps; @@ -490,6 +501,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // Denotes the status of restrict background read from disk. private boolean mLoadedRestrictBackground; + /** + * Whether or not network for apps in proc-states greater than + * {@link NetworkPolicyManager#BACKGROUND_THRESHOLD_STATE} is always blocked. + */ + private boolean mBackgroundNetworkRestricted; + // See main javadoc for instructions on how to use these locks. final Object mUidRulesFirstLock = new Object(); final Object mNetworkPoliciesSecondLock = new Object(); @@ -514,6 +531,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private volatile boolean mNetworkManagerReady; + /** + * Delay after which a uid going into a process state greater than or equal to + * {@link NetworkPolicyManager#BACKGROUND_THRESHOLD_STATE} will lose network access. + * The delay is meant to prevent churn due to quick process-state changes. + * Note that there is no delay while granting network access. + */ + @VisibleForTesting + long mBackgroundRestrictionDelayMs = TimeUnit.SECONDS.toMillis(5); + /** Defined network policies. */ @GuardedBy("mNetworkPoliciesSecondLock") final ArrayMap<NetworkTemplate, NetworkPolicy> mNetworkPolicy = new ArrayMap<>(); @@ -545,6 +571,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @GuardedBy("mUidRulesFirstLock") final SparseIntArray mUidFirewallPowerSaveRules = new SparseIntArray(); @GuardedBy("mUidRulesFirstLock") + final SparseIntArray mUidFirewallBackgroundRules = new SparseIntArray(); + @GuardedBy("mUidRulesFirstLock") final SparseIntArray mUidFirewallRestrictedModeRules = new SparseIntArray(); @GuardedBy("mUidRulesFirstLock") final SparseIntArray mUidFirewallLowPowerStandbyModeRules = new SparseIntArray(); @@ -624,6 +652,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @GuardedBy("mUidRulesFirstLock") private final SparseArray<UidBlockedState> mTmpUidBlockedState = new SparseArray<>(); + /** + * Stores a map of uids to the time their transition to background is considered complete. They + * will lose network access after this time. This is used to prevent churn in rules due to quick + * process-state transitions. + */ + @GuardedBy("mUidRulesFirstLock") + private final SparseLongArray mBackgroundTransitioningUids = new SparseLongArray(); + /** Map from network ID to last observed meteredness state */ @GuardedBy("mNetworkPoliciesSecondLock") private final SparseBooleanArray mNetworkMetered = new SparseBooleanArray(); @@ -823,7 +859,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mContext = Objects.requireNonNull(context, "missing context"); mActivityManager = Objects.requireNonNull(activityManager, "missing activityManager"); mNetworkManager = Objects.requireNonNull(networkManagement, "missing networkManagement"); - mPowerWhitelistManager = mContext.getSystemService(PowerWhitelistManager.class); + mPowerExemptionManager = mContext.getSystemService(PowerExemptionManager.class); mClock = Objects.requireNonNull(clock, "missing Clock"); mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class); @@ -859,15 +895,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @GuardedBy("mUidRulesFirstLock") private void updatePowerSaveAllowlistUL() { - int[] whitelist = mPowerWhitelistManager.getWhitelistedAppIds(/* includingIdle */ false); + int[] allowlist = mPowerExemptionManager.getAllowListedAppIds(/* includingIdle */ false); mPowerSaveWhitelistExceptIdleAppIds.clear(); - for (int uid : whitelist) { + for (int uid : allowlist) { mPowerSaveWhitelistExceptIdleAppIds.put(uid, true); } - whitelist = mPowerWhitelistManager.getWhitelistedAppIds(/* includingIdle */ true); + allowlist = mPowerExemptionManager.getAllowListedAppIds(/* includingIdle */ true); mPowerSaveWhitelistAppIds.clear(); - for (int uid : whitelist) { + for (int uid : allowlist) { mPowerSaveWhitelistAppIds.put(uid, true); } } @@ -1017,6 +1053,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { writePolicyAL(); } + // The flag is boot-stable. + mBackgroundNetworkRestricted = Flags.networkBlockedForTopSleepingAndAbove(); + if (mBackgroundNetworkRestricted) { + // Firewall rules and UidBlockedState will get updated in + // updateRulesForGlobalChangeAL below. + enableFirewallChainUL(FIREWALL_CHAIN_BACKGROUND, true); + } + setRestrictBackgroundUL(mLoadedRestrictBackground, "init_service"); updateRulesForGlobalChangeAL(false); updateNotificationsNL(); @@ -1027,17 +1071,22 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final int changes = ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_CAPABILITY; + + final int cutpoint = mBackgroundNetworkRestricted ? PROCESS_STATE_UNKNOWN + : NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE; + // TODO (b/319728914): Filter out the unnecessary changes when using no cutpoint. + mActivityManagerInternal.registerNetworkPolicyUidObserver(mUidObserver, changes, - NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE, "android"); + cutpoint, "android"); mNetworkManager.registerObserver(mAlertObserver); } catch (RemoteException e) { // ignored; both services live in system_server } // listen for changes to power save allowlist - final IntentFilter whitelistFilter = new IntentFilter( + final IntentFilter allowlistFilter = new IntentFilter( PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED); - mContext.registerReceiver(mPowerSaveWhitelistReceiver, whitelistFilter, null, mHandler); + mContext.registerReceiver(mPowerSaveAllowlistReceiver, allowlistFilter, null, mHandler); // watch for network interfaces to be claimed final IntentFilter connFilter = new IntentFilter(CONNECTIVITY_ACTION); @@ -1188,12 +1237,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } - final private BroadcastReceiver mPowerSaveWhitelistReceiver = new BroadcastReceiver() { + private final BroadcastReceiver mPowerSaveAllowlistReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // on background handler thread, and POWER_SAVE_WHITELIST_CHANGED is protected synchronized (mUidRulesFirstLock) { updatePowerSaveAllowlistUL(); + if (mBackgroundNetworkRestricted) { + updateRulesForBackgroundChainUL(); + } updateRulesForRestrictPowerUL(); updateRulesForAppIdleUL(); } @@ -3901,6 +3953,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } fout.println(); + fout.println("Flags:"); + fout.println("Network blocked for TOP_SLEEPING and above: " + + mBackgroundNetworkRestricted); + + fout.println(); fout.println("mRestrictBackgroundLowPowerMode: " + mRestrictBackgroundLowPowerMode); fout.println("mRestrictBackgroundBeforeBsm: " + mRestrictBackgroundBeforeBsm); fout.println("mLoadedRestrictBackground: " + mLoadedRestrictBackground); @@ -4042,6 +4099,22 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { fout.decreaseIndent(); } + size = mBackgroundTransitioningUids.size(); + if (size > 0) { + final long nowUptime = SystemClock.uptimeMillis(); + fout.println("Uids transitioning to background:"); + fout.increaseIndent(); + for (int i = 0; i < size; i++) { + fout.print("UID="); + fout.print(mBackgroundTransitioningUids.keyAt(i)); + fout.print(", "); + TimeUtils.formatDuration(mBackgroundTransitioningUids.valueAt(i), nowUptime, + fout); + fout.println(); + } + fout.decreaseIndent(); + } + final SparseBooleanArray knownUids = new SparseBooleanArray(); collectKeys(mUidState, knownUids); synchronized (mUidBlockedState) { @@ -4163,6 +4236,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return isProcStateAllowedWhileInLowPowerStandby(uidState); } + @GuardedBy("mUidRulesFirstLock") + private boolean isUidExemptFromBackgroundRestrictions(int uid) { + return mBackgroundTransitioningUids.indexOfKey(uid) >= 0 + || isProcStateAllowedNetworkWhileBackground(mUidState.get(uid)); + } + /** * Process state of UID changed; if needed, will trigger * {@link #updateRulesForDataUsageRestrictionsUL(int)} and @@ -4188,6 +4267,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // state changed, push updated rules mUidState.put(uid, newUidState); updateRestrictBackgroundRulesOnUidStatusChangedUL(uid, oldUidState, newUidState); + + boolean updatePowerRestrictionRules = false; boolean allowedWhileIdleOrPowerSaveModeChanged = isProcStateAllowedWhileIdleOrPowerSaveMode(oldUidState) != isProcStateAllowedWhileIdleOrPowerSaveMode(newUidState); @@ -4199,19 +4280,44 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (mRestrictPower) { updateRuleForRestrictPowerUL(uid); } - updateRulesForPowerRestrictionsUL(uid, procState); + updatePowerRestrictionRules = true; + } + if (mBackgroundNetworkRestricted) { + final boolean wasAllowed = isProcStateAllowedNetworkWhileBackground( + oldUidState); + final boolean isAllowed = isProcStateAllowedNetworkWhileBackground(newUidState); + if (!wasAllowed && isAllowed) { + mBackgroundTransitioningUids.delete(uid); + updateRuleForBackgroundUL(uid); + updatePowerRestrictionRules = true; + } else if (wasAllowed && !isAllowed) { + final long completionTimeMs = SystemClock.uptimeMillis() + + mBackgroundRestrictionDelayMs; + if (mBackgroundTransitioningUids.indexOfKey(uid) < 0) { + // This is just a defensive check in case the upstream code ever makes + // multiple calls for the same process state change. + mBackgroundTransitioningUids.put(uid, completionTimeMs); + } + if (!mHandler.hasMessages(MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS)) { + // Many uids may be in this "transitioning" state at the same time, so + // using one message at a time to avoid congestion in the MessageQueue. + mHandler.sendEmptyMessageAtTime( + MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS, completionTimeMs); + } + } } if (mLowPowerStandbyActive) { boolean allowedInLpsChanged = isProcStateAllowedWhileInLowPowerStandby(oldUidState) != isProcStateAllowedWhileInLowPowerStandby(newUidState); if (allowedInLpsChanged) { - if (!allowedWhileIdleOrPowerSaveModeChanged) { - updateRulesForPowerRestrictionsUL(uid, procState); - } updateRuleForLowPowerStandbyUL(uid); + updatePowerRestrictionRules = true; } } + if (updatePowerRestrictionRules) { + updateRulesForPowerRestrictionsUL(uid, procState); + } return true; } } finally { @@ -4234,6 +4340,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (mRestrictPower) { updateRuleForRestrictPowerUL(uid); } + if (mBackgroundNetworkRestricted) { + // Uid is no longer running, there is no point in any grace period of network + // access during transitions to lower importance proc-states. + mBackgroundTransitioningUids.delete(uid); + updateRuleForBackgroundUL(uid); + } updateRulesForPowerRestrictionsUL(uid); if (mLowPowerStandbyActive) { updateRuleForLowPowerStandbyUL(uid); @@ -4441,11 +4553,41 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } + /** + * Updates the rules for apps allowlisted to use network while in the background. + */ + @GuardedBy("mUidRulesFirstLock") + private void updateRulesForBackgroundChainUL() { + Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForBackgroundChainUL"); + try { + final SparseIntArray uidRules = mUidFirewallBackgroundRules; + uidRules.clear(); + + final List<UserInfo> users = mUserManager.getUsers(); + for (int ui = users.size() - 1; ui >= 0; ui--) { + final UserInfo user = users.get(ui); + updateRulesForAllowlistedAppIds(uidRules, mPowerSaveTempWhitelistAppIds, user.id); + updateRulesForAllowlistedAppIds(uidRules, mPowerSaveWhitelistAppIds, user.id); + updateRulesForAllowlistedAppIds(uidRules, mPowerSaveWhitelistExceptIdleAppIds, + user.id); + } + for (int i = mUidState.size() - 1; i >= 0; i--) { + if (mBackgroundTransitioningUids.indexOfKey(mUidState.keyAt(i)) >= 0 + || isProcStateAllowedNetworkWhileBackground(mUidState.valueAt(i))) { + uidRules.put(mUidState.keyAt(i), FIREWALL_RULE_ALLOW); + } + } + setUidFirewallRulesUL(FIREWALL_CHAIN_BACKGROUND, uidRules); + } finally { + Trace.traceEnd(TRACE_TAG_NETWORK); + } + } + private void updateRulesForAllowlistedAppIds(final SparseIntArray uidRules, - final SparseBooleanArray whitelistedAppIds, int userId) { - for (int i = whitelistedAppIds.size() - 1; i >= 0; --i) { - if (whitelistedAppIds.valueAt(i)) { - final int appId = whitelistedAppIds.keyAt(i); + final SparseBooleanArray allowlistedAppIds, int userId) { + for (int i = allowlistedAppIds.size() - 1; i >= 0; --i) { + if (allowlistedAppIds.valueAt(i)) { + final int appId = allowlistedAppIds.keyAt(i); final int uid = UserHandle.getUid(userId, appId); uidRules.put(uid, FIREWALL_RULE_ALLOW); } @@ -4504,12 +4646,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @GuardedBy("mUidRulesFirstLock") private boolean isAllowlistedFromPowerSaveUL(int uid, boolean deviceIdleMode) { final int appId = UserHandle.getAppId(uid); - boolean isWhitelisted = mPowerSaveTempWhitelistAppIds.get(appId) + boolean allowlisted = mPowerSaveTempWhitelistAppIds.get(appId) || mPowerSaveWhitelistAppIds.get(appId); if (!deviceIdleMode) { - isWhitelisted = isWhitelisted || isAllowlistedFromPowerSaveExceptIdleUL(uid); + allowlisted = allowlisted || isAllowlistedFromPowerSaveExceptIdleUL(uid); } - return isWhitelisted; + return allowlisted; } /** @@ -4598,6 +4740,38 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } /** + * Update firewall rule for a single uid whenever there are any interesting changes in the uid. + * Currently, it is called when: + * - The uid is added to or removed from power allowlists + * - The uid undergoes a process-state change + * - A package belonging to this uid is added + * - The uid is evicted from memory + */ + @GuardedBy("mUidRulesFirstLock") + void updateRuleForBackgroundUL(int uid) { + if (!isUidValidForAllowlistRulesUL(uid)) { + return; + } + + Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRuleForBackgroundUL: " + uid); + try { + // The uid should be absent from mUidState and mBackgroundTransitioningUids if it is + // not running when this method is called. Then, the firewall state will depend on the + // allowlist alone. This is the desired behavior. + if (isAllowlistedFromPowerSaveUL(uid, false) + || isUidExemptFromBackgroundRestrictions(uid)) { + setUidFirewallRuleUL(FIREWALL_CHAIN_BACKGROUND, uid, FIREWALL_RULE_ALLOW); + if (LOGD) Log.d(TAG, "updateRuleForBackgroundUL ALLOW " + uid); + } else { + setUidFirewallRuleUL(FIREWALL_CHAIN_BACKGROUND, uid, FIREWALL_RULE_DEFAULT); + if (LOGD) Log.d(TAG, "updateRuleForBackgroundUL " + uid + " to DEFAULT"); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_NETWORK); + } + } + + /** * Toggle the firewall standby chain and inform listeners if the uid rules have effectively * changed. */ @@ -4644,6 +4818,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { "updateRulesForGlobalChangeAL: " + (restrictedNetworksChanged ? "R" : "-")); } try { + if (mBackgroundNetworkRestricted) { + updateRulesForBackgroundChainUL(); + } updateRulesForAppIdleUL(); updateRulesForRestrictPowerUL(); updateRulesForRestrictBackgroundUL(); @@ -4803,6 +4980,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { updateRuleForAppIdleUL(uid, PROCESS_STATE_UNKNOWN); updateRuleForDeviceIdleUL(uid); updateRuleForRestrictPowerUL(uid); + if (mBackgroundNetworkRestricted) { + updateRuleForBackgroundUL(uid); + } // Update internal rules. updateRulesForPowerRestrictionsUL(uid); } @@ -4940,6 +5120,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mUidFirewallStandbyRules.delete(uid); mUidFirewallDozableRules.delete(uid); mUidFirewallPowerSaveRules.delete(uid); + mUidFirewallBackgroundRules.delete(uid); + mBackgroundTransitioningUids.delete(uid); mPowerSaveWhitelistExceptIdleAppIds.delete(uid); mPowerSaveWhitelistAppIds.delete(uid); mPowerSaveTempWhitelistAppIds.delete(uid); @@ -4973,6 +5155,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { updateRuleForDeviceIdleUL(uid); updateRuleForAppIdleUL(uid, PROCESS_STATE_UNKNOWN); updateRuleForRestrictPowerUL(uid); + if (mBackgroundNetworkRestricted) { + updateRuleForBackgroundUL(uid); + } // If the uid has the necessary permissions, then it should be added to the restricted mode // firewall allowlist. @@ -5157,7 +5342,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * Similar to above but ignores idle state if app standby is currently disabled by parole. * * @param uid the uid of the app to update rules for - * @param oldUidRules the current rules for the uid, in order to determine if there's a change * @param isUidIdle whether uid is idle or not */ @GuardedBy("mUidRulesFirstLock") @@ -5203,6 +5387,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { newBlockedReasons |= (mLowPowerStandbyActive ? BLOCKED_REASON_LOW_POWER_STANDBY : 0); newBlockedReasons |= (isUidIdle ? BLOCKED_REASON_APP_STANDBY : 0); newBlockedReasons |= (uidBlockedState.blockedReasons & BLOCKED_REASON_RESTRICTED_MODE); + newBlockedReasons |= mBackgroundNetworkRestricted ? BLOCKED_REASON_APP_BACKGROUND : 0; newAllowedReasons |= (isSystem(uid) ? ALLOWED_REASON_SYSTEM : 0); newAllowedReasons |= (isForeground ? ALLOWED_REASON_FOREGROUND : 0); @@ -5215,6 +5400,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { & ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS); newAllowedReasons |= (isAllowlistedFromLowPowerStandbyUL(uid)) ? ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST : 0; + newAllowedReasons |= (mBackgroundNetworkRestricted + && isUidExemptFromBackgroundRestrictions(uid)) + ? ALLOWED_REASON_NOT_IN_BACKGROUND : 0; uidBlockedState.blockedReasons = (uidBlockedState.blockedReasons & BLOCKED_METERED_REASON_MASK) | newBlockedReasons; @@ -5236,7 +5424,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { oldEffectiveBlockedReasons = previousUidBlockedState.effectiveBlockedReasons; newEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons; - uidRules = oldEffectiveBlockedReasons == newEffectiveBlockedReasons + uidRules = (oldEffectiveBlockedReasons == newEffectiveBlockedReasons) ? RULE_NONE : uidBlockedState.deriveUidRules(); } @@ -5429,6 +5617,28 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mContext.sendBroadcastAsUser(intent, UserHandle.ALL); return true; } + case MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS: { + final long now = SystemClock.uptimeMillis(); + long nextCheckTime = Long.MAX_VALUE; + synchronized (mUidRulesFirstLock) { + for (int i = mBackgroundTransitioningUids.size() - 1; i >= 0; i--) { + final long completionTimeMs = mBackgroundTransitioningUids.valueAt(i); + if (completionTimeMs > now) { + nextCheckTime = Math.min(nextCheckTime, completionTimeMs); + continue; + } + final int uid = mBackgroundTransitioningUids.keyAt(i); + mBackgroundTransitioningUids.removeAt(i); + updateRuleForBackgroundUL(uid); + updateRulesForPowerRestrictionsUL(uid, false); + } + } + if (nextCheckTime < Long.MAX_VALUE) { + mHandler.sendEmptyMessageAtTime(MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS, + nextCheckTime); + } + return true; + } case MSG_POLICIES_CHANGED: { final int uid = msg.arg1; final int policy = msg.arg2; @@ -5840,6 +6050,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mUidFirewallRestrictedModeRules.put(uid, rule); } else if (chain == FIREWALL_CHAIN_LOW_POWER_STANDBY) { mUidFirewallLowPowerStandbyModeRules.put(uid, rule); + } else if (chain == FIREWALL_CHAIN_BACKGROUND) { + mUidFirewallBackgroundRules.put(uid, rule); } try { @@ -5896,6 +6108,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { FIREWALL_RULE_DEFAULT); mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_LOW_POWER_STANDBY, uid, FIREWALL_RULE_DEFAULT); + mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, uid, + FIREWALL_RULE_DEFAULT); mNetworkManager.setUidOnMeteredNetworkAllowlist(uid, false); mLogger.meteredAllowlistChanged(uid, false); mNetworkManager.setUidOnMeteredNetworkDenylist(uid, false); @@ -6420,10 +6634,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { effectiveBlockedReasons &= ~BLOCKED_REASON_BATTERY_SAVER; effectiveBlockedReasons &= ~BLOCKED_REASON_DOZE; effectiveBlockedReasons &= ~BLOCKED_REASON_APP_STANDBY; + effectiveBlockedReasons &= ~BLOCKED_REASON_APP_BACKGROUND; } if ((allowedReasons & ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST) != 0) { effectiveBlockedReasons &= ~BLOCKED_REASON_BATTERY_SAVER; effectiveBlockedReasons &= ~BLOCKED_REASON_APP_STANDBY; + effectiveBlockedReasons &= ~BLOCKED_REASON_APP_BACKGROUND; } if ((allowedReasons & ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS) != 0) { effectiveBlockedReasons &= ~BLOCKED_REASON_RESTRICTED_MODE; @@ -6434,19 +6650,24 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if ((allowedReasons & ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST) != 0) { effectiveBlockedReasons &= ~BLOCKED_REASON_LOW_POWER_STANDBY; } + if ((allowedReasons & ALLOWED_REASON_NOT_IN_BACKGROUND) != 0) { + effectiveBlockedReasons &= ~BLOCKED_REASON_APP_BACKGROUND; + } return effectiveBlockedReasons; } static int getAllowedReasonsForProcState(int procState) { - if (procState > NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE) { - return ALLOWED_REASON_NONE; - } else if (procState <= NetworkPolicyManager.TOP_THRESHOLD_STATE) { + if (procState <= NetworkPolicyManager.TOP_THRESHOLD_STATE) { return ALLOWED_REASON_TOP | ALLOWED_REASON_FOREGROUND - | ALLOWED_METERED_REASON_FOREGROUND; - } else { - return ALLOWED_REASON_FOREGROUND | ALLOWED_METERED_REASON_FOREGROUND; + | ALLOWED_METERED_REASON_FOREGROUND | ALLOWED_REASON_NOT_IN_BACKGROUND; + } else if (procState <= NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE) { + return ALLOWED_REASON_FOREGROUND | ALLOWED_METERED_REASON_FOREGROUND + | ALLOWED_REASON_NOT_IN_BACKGROUND; + } else if (procState < NetworkPolicyManager.BACKGROUND_THRESHOLD_STATE) { + return ALLOWED_REASON_NOT_IN_BACKGROUND; } + return ALLOWED_REASON_NONE; } @Override @@ -6471,6 +6692,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { BLOCKED_REASON_APP_STANDBY, BLOCKED_REASON_RESTRICTED_MODE, BLOCKED_REASON_LOW_POWER_STANDBY, + BLOCKED_REASON_APP_BACKGROUND, BLOCKED_METERED_REASON_DATA_SAVER, BLOCKED_METERED_REASON_USER_RESTRICTED, BLOCKED_METERED_REASON_ADMIN_DISABLED, @@ -6484,6 +6706,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST, ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS, ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST, + ALLOWED_REASON_NOT_IN_BACKGROUND, ALLOWED_METERED_REASON_USER_EXEMPTED, ALLOWED_METERED_REASON_SYSTEM, ALLOWED_METERED_REASON_FOREGROUND, @@ -6503,6 +6726,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return "RESTRICTED_MODE"; case BLOCKED_REASON_LOW_POWER_STANDBY: return "LOW_POWER_STANDBY"; + case BLOCKED_REASON_APP_BACKGROUND: + return "APP_BACKGROUND"; case BLOCKED_METERED_REASON_DATA_SAVER: return "DATA_SAVER"; case BLOCKED_METERED_REASON_USER_RESTRICTED: @@ -6533,6 +6758,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return "RESTRICTED_MODE_PERMISSIONS"; case ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST: return "LOW_POWER_STANDBY_ALLOWLIST"; + case ALLOWED_REASON_NOT_IN_BACKGROUND: + return "NOT_IN_BACKGROUND"; case ALLOWED_METERED_REASON_USER_EXEMPTED: return "METERED_USER_EXEMPTED"; case ALLOWED_METERED_REASON_SYSTEM: @@ -6600,7 +6827,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { int powerBlockedReasons = BLOCKED_REASON_APP_STANDBY | BLOCKED_REASON_DOZE | BLOCKED_REASON_BATTERY_SAVER - | BLOCKED_REASON_LOW_POWER_STANDBY; + | BLOCKED_REASON_LOW_POWER_STANDBY + | BLOCKED_REASON_APP_BACKGROUND; if ((effectiveBlockedReasons & powerBlockedReasons) != 0) { uidRule |= RULE_REJECT_ALL; } else if ((blockedReasons & powerBlockedReasons) != 0) { diff --git a/services/core/java/com/android/server/net/flags.aconfig b/services/core/java/com/android/server/net/flags.aconfig new file mode 100644 index 000000000000..419665a0a5ab --- /dev/null +++ b/services/core/java/com/android/server/net/flags.aconfig @@ -0,0 +1,8 @@ +package: "com.android.server.net" + +flag { + name: "network_blocked_for_top_sleeping_and_above" + namespace: "backstage_power" + description: "Block network access for apps in a low importance background state" + bug: "304347838" +} diff --git a/services/core/java/com/android/server/os/NativeTombstoneManager.java b/services/core/java/com/android/server/os/NativeTombstoneManager.java index b7e737448d2d..ab0d0d2626db 100644 --- a/services/core/java/com/android/server/os/NativeTombstoneManager.java +++ b/services/core/java/com/android/server/os/NativeTombstoneManager.java @@ -41,13 +41,14 @@ import android.system.Os; import android.system.StructStat; import android.util.Slog; import android.util.SparseArray; +import android.util.proto.ProtoInputStream; +import android.util.proto.ProtoParseException; import com.android.internal.annotations.GuardedBy; import com.android.server.BootReceiver; import com.android.server.ServiceThread; import com.android.server.os.TombstoneProtos.Cause; import com.android.server.os.TombstoneProtos.Tombstone; -import com.android.server.os.protobuf.CodedInputStream; import libcore.io.IoUtils; @@ -127,21 +128,18 @@ public final class NativeTombstoneManager { return; } + String processName = "UNKNOWN"; final boolean isProtoFile = filename.endsWith(".pb"); - if (!isProtoFile) { - return; - } + File protoPath = isProtoFile ? path : new File(path.getAbsolutePath() + ".pb"); - Optional<ParsedTombstone> parsedTombstone = handleProtoTombstone(path, true); + Optional<TombstoneFile> parsedTombstone = handleProtoTombstone(protoPath, isProtoFile); if (parsedTombstone.isPresent()) { - BootReceiver.addTombstoneToDropBox( - mContext, path, parsedTombstone.get().getTombstone(), - parsedTombstone.get().getProcessName(), mTmpFileLock); + processName = parsedTombstone.get().getProcessName(); } + BootReceiver.addTombstoneToDropBox(mContext, path, isProtoFile, processName, mTmpFileLock); } - private Optional<ParsedTombstone> handleProtoTombstone( - File path, boolean addToList) { + private Optional<TombstoneFile> handleProtoTombstone(File path, boolean addToList) { final String filename = path.getName(); if (!filename.endsWith(".pb")) { Slog.w(TAG, "unexpected tombstone name: " + path); @@ -171,7 +169,7 @@ public final class NativeTombstoneManager { return Optional.empty(); } - final Optional<ParsedTombstone> parsedTombstone = TombstoneFile.parse(pfd); + final Optional<TombstoneFile> parsedTombstone = TombstoneFile.parse(pfd); if (!parsedTombstone.isPresent()) { IoUtils.closeQuietly(pfd); return Optional.empty(); @@ -184,7 +182,7 @@ public final class NativeTombstoneManager { previous.dispose(); } - mTombstones.put(number, parsedTombstone.get().getTombstoneFile()); + mTombstones.put(number, parsedTombstone.get()); } } @@ -332,27 +330,6 @@ public final class NativeTombstoneManager { } } - static class ParsedTombstone { - TombstoneFile mTombstoneFile; - Tombstone mTombstone; - ParsedTombstone(TombstoneFile tombstoneFile, Tombstone tombstone) { - mTombstoneFile = tombstoneFile; - mTombstone = tombstone; - } - - public String getProcessName() { - return mTombstoneFile.getProcessName(); - } - - public TombstoneFile getTombstoneFile() { - return mTombstoneFile; - } - - public Tombstone getTombstone() { - return mTombstone; - } - } - static class TombstoneFile { final ParcelFileDescriptor mPfd; @@ -435,21 +412,67 @@ public final class NativeTombstoneManager { } } - static Optional<ParsedTombstone> parse(ParcelFileDescriptor pfd) { - Tombstone tombstoneProto; - try (FileInputStream is = new FileInputStream(pfd.getFileDescriptor())) { - final byte[] tombstoneBytes = is.readAllBytes(); + static Optional<TombstoneFile> parse(ParcelFileDescriptor pfd) { + final FileInputStream is = new FileInputStream(pfd.getFileDescriptor()); + final ProtoInputStream stream = new ProtoInputStream(is); + + int pid = 0; + int uid = 0; + String processName = null; + String crashReason = ""; + String selinuxLabel = ""; + + try { + while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (stream.getFieldNumber()) { + case (int) Tombstone.PID: + pid = stream.readInt(Tombstone.PID); + break; + + case (int) Tombstone.UID: + uid = stream.readInt(Tombstone.UID); + break; + + case (int) Tombstone.COMMAND_LINE: + if (processName == null) { + processName = stream.readString(Tombstone.COMMAND_LINE); + } + break; - tombstoneProto = Tombstone.parseFrom( - CodedInputStream.newInstance(tombstoneBytes)); - } catch (IOException ex) { + case (int) Tombstone.CAUSES: + if (!crashReason.equals("")) { + // Causes appear in decreasing order of likelihood. For now we only + // want the most likely crash reason here, so ignore all others. + break; + } + long token = stream.start(Tombstone.CAUSES); + cause: + while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (stream.getFieldNumber()) { + case (int) Cause.HUMAN_READABLE: + crashReason = stream.readString(Cause.HUMAN_READABLE); + break cause; + + default: + break; + } + } + stream.end(token); + break; + + case (int) Tombstone.SELINUX_LABEL: + selinuxLabel = stream.readString(Tombstone.SELINUX_LABEL); + break; + + default: + break; + } + } + } catch (IOException | ProtoParseException ex) { Slog.e(TAG, "Failed to parse tombstone", ex); return Optional.empty(); } - int pid = tombstoneProto.getPid(); - int uid = tombstoneProto.getUid(); - if (!UserHandle.isApp(uid)) { Slog.e(TAG, "Tombstone's UID (" + uid + ") not an app, ignoring"); return Optional.empty(); @@ -466,7 +489,6 @@ public final class NativeTombstoneManager { final int userId = UserHandle.getUserId(uid); final int appId = UserHandle.getAppId(uid); - String selinuxLabel = tombstoneProto.getSelinuxLabel(); if (!selinuxLabel.startsWith("u:r:untrusted_app")) { Slog.e(TAG, "Tombstone has invalid selinux label (" + selinuxLabel + "), ignoring"); return Optional.empty(); @@ -478,30 +500,11 @@ public final class NativeTombstoneManager { result.mAppId = appId; result.mPid = pid; result.mUid = uid; - result.mProcessName = getCmdLineProcessName(tombstoneProto); + result.mProcessName = processName == null ? "" : processName; result.mTimestampMs = timestampMs; - result.mCrashReason = getCrashReason(tombstoneProto); + result.mCrashReason = crashReason; - return Optional.of(new ParsedTombstone(result, tombstoneProto)); - } - - private static String getCmdLineProcessName(Tombstone tombstoneProto) { - for (String cmdline : tombstoneProto.getCommandLineList()) { - if (cmdline != null) { - return cmdline; - } - } - return ""; - } - - private static String getCrashReason(Tombstone tombstoneProto) { - for (Cause cause : tombstoneProto.getCausesList()) { - if (cause.getHumanReadable() != null - && !cause.getHumanReadable().equals("")) { - return cause.getHumanReadable(); - } - } - return ""; + return Optional.of(result); } public IParcelFileDescriptorRetriever getPfdRetriever() { diff --git a/services/core/java/com/android/server/pm/UserDataPreparer.java b/services/core/java/com/android/server/pm/UserDataPreparer.java index 4c42c2dd0850..1d414011cff3 100644 --- a/services/core/java/com/android/server/pm/UserDataPreparer.java +++ b/services/core/java/com/android/server/pm/UserDataPreparer.java @@ -141,7 +141,7 @@ class UserDataPreparer { // If internal storage of the system user fails to prepare on first boot, then // things are *really* broken, so we might as well reboot to recovery right away. try { - Log.wtf(TAG, "prepareUserData failed for user " + userId, e); + Log.e(TAG, "prepareUserData failed for user " + userId, e); if (isNewUser && userId == UserHandle.USER_SYSTEM && volumeUuid == null) { RecoverySystem.rebootPromptAndWipeUserData(mContext, "failed to prepare internal storage for system user"); diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index ed9445c26740..ba8a76c9ddde 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -44,6 +44,9 @@ import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricSourceType; +import android.hardware.biometrics.SensorProperties; +import android.hardware.face.FaceManager; +import android.hardware.fingerprint.FingerprintManager; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -157,6 +160,8 @@ public class TrustManagerService extends SystemService { private final LockPatternUtils mLockPatternUtils; private final UserManager mUserManager; private final ActivityManager mActivityManager; + private FingerprintManager mFingerprintManager; + private FaceManager mFaceManager; private VirtualDeviceManagerInternal mVirtualDeviceManager; private enum TrustState { @@ -294,6 +299,8 @@ public class TrustManagerService extends SystemService { mPackageMonitor.register(mContext, mHandler.getLooper(), UserHandle.ALL, true); mReceiver.register(mContext); mLockPatternUtils.registerStrongAuthTracker(mStrongAuthTracker); + mFingerprintManager = mContext.getSystemService(FingerprintManager.class); + mFaceManager = mContext.getSystemService(FaceManager.class); } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { mTrustAgentsCanRun = true; refreshAgentList(UserHandle.USER_ALL); @@ -895,7 +902,19 @@ public class TrustManagerService extends SystemService { private void notifyKeystoreOfDeviceLockState(int userId, boolean isLocked) { if (isLocked) { - Authorization.onDeviceLocked(userId, getBiometricSids(userId)); + if (android.security.Flags.fixUnlockedDeviceRequiredKeysV2()) { + // A profile with unified challenge is unlockable not by its own biometrics and + // trust agents, but rather by those of the parent user. Therefore, when protecting + // the profile's UnlockedDeviceRequired keys, we must use the parent's list of + // biometric SIDs and weak unlock methods, not the profile's. + int authUserId = mLockPatternUtils.isProfileWithUnifiedChallenge(userId) + ? resolveProfileParent(userId) : userId; + + Authorization.onDeviceLocked(userId, getBiometricSids(authUserId), + isWeakUnlockMethodEnabled(authUserId)); + } else { + Authorization.onDeviceLocked(userId, getBiometricSids(userId), false); + } } else { // Notify Keystore that the device is now unlocked for the user. Note that for unlocks // with LSKF, this is redundant with the call from LockSettingsService which provides @@ -1442,16 +1461,59 @@ public class TrustManagerService extends SystemService { if (biometricManager == null) { return new long[0]; } - if (android.security.Flags.fixUnlockedDeviceRequiredKeysV2() - && mLockPatternUtils.isProfileWithUnifiedChallenge(userId)) { - // Profiles with unified challenge have their own set of biometrics, but the device - // unlock happens via the parent user. In this case Keystore needs to be given the list - // of biometric SIDs from the parent user, not the profile. - userId = resolveProfileParent(userId); - } return biometricManager.getAuthenticatorIds(userId); } + // Returns whether the device can become unlocked for the specified user via one of that user's + // non-strong biometrics or trust agents. This assumes that the device is currently locked, or + // is becoming locked, for the user. + private boolean isWeakUnlockMethodEnabled(int userId) { + + // Check whether the system currently allows the use of non-strong biometrics for the user, + // *and* the user actually has a non-strong biometric enrolled. + // + // The biometrics framework ostensibly supports multiple sensors per modality. However, + // that feature is unused and untested. So, we simply consider one sensor per modality. + // + // Also, currently we just consider fingerprint and face, matching Keyguard. If Keyguard + // starts supporting other biometric modalities, this will need to be updated. + if (mStrongAuthTracker.isBiometricAllowedForUser(/* isStrongBiometric= */ false, userId)) { + DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager(); + int disabledFeatures = dpm.getKeyguardDisabledFeatures(null, userId); + + if (mFingerprintManager != null + && (disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT) == 0 + && mFingerprintManager.hasEnrolledTemplates(userId) + && isWeakOrConvenienceSensor( + mFingerprintManager.getSensorProperties().get(0))) { + Slog.i(TAG, "User is unlockable by non-strong fingerprint auth"); + return true; + } + + if (mFaceManager != null + && (disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_FACE) == 0 + && mFaceManager.hasEnrolledTemplates(userId) + && isWeakOrConvenienceSensor(mFaceManager.getSensorProperties().get(0))) { + Slog.i(TAG, "User is unlockable by non-strong face auth"); + return true; + } + } + + // Check whether it's possible for the device to be actively unlocked by a trust agent. + if (getUserTrustStateInner(userId) == TrustState.TRUSTABLE + || (isAutomotive() && isTrustUsuallyManagedInternal(userId))) { + Slog.i(TAG, "User is unlockable by trust agent"); + return true; + } + + return false; + } + + private static boolean isWeakOrConvenienceSensor(SensorProperties sensor) { + return sensor.getSensorStrength() == SensorProperties.STRENGTH_WEAK + || sensor.getSensorStrength() == SensorProperties.STRENGTH_CONVENIENCE; + } + // User lifecycle @Override diff --git a/services/core/java/com/android/server/vcn/VcnContext.java b/services/core/java/com/android/server/vcn/VcnContext.java index 9213d96ad4ca..ed04e5fde024 100644 --- a/services/core/java/com/android/server/vcn/VcnContext.java +++ b/services/core/java/com/android/server/vcn/VcnContext.java @@ -34,6 +34,7 @@ public class VcnContext { @NonNull private final Looper mLooper; @NonNull private final VcnNetworkProvider mVcnNetworkProvider; @NonNull private final FeatureFlags mFeatureFlags; + @NonNull private final com.android.net.flags.FeatureFlags mCoreNetFeatureFlags; private final boolean mIsInTestMode; public VcnContext( @@ -48,6 +49,7 @@ public class VcnContext { // Auto-generated class mFeatureFlags = new FeatureFlagsImpl(); + mCoreNetFeatureFlags = new com.android.net.flags.FeatureFlagsImpl(); } @NonNull @@ -69,11 +71,23 @@ public class VcnContext { return mIsInTestMode; } + public boolean isFlagNetworkMetricMonitorEnabled() { + return mFeatureFlags.networkMetricMonitor(); + } + + public boolean isFlagIpSecTransformStateEnabled() { + return mCoreNetFeatureFlags.ipsecTransformState(); + } + @NonNull public FeatureFlags getFeatureFlags() { return mFeatureFlags; } + public boolean isFlagSafeModeTimeoutConfigEnabled() { + return mFeatureFlags.safeModeTimeoutConfig(); + } + /** * Verifies that the caller is running on the VcnContext Thread. * diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java index 54c97dd37941..3094b182093b 100644 --- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java +++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java @@ -915,9 +915,11 @@ public class VcnGatewayConnection extends StateMachine { // TODO(b/180132994): explore safely removing this Thread check mVcnContext.ensureRunningOnLooperThread(); - logInfo( - "Selected underlying network changed: " - + (underlying == null ? null : underlying.network)); + if (!UnderlyingNetworkRecord.isSameNetwork(mUnderlying, underlying)) { + logInfo( + "Selected underlying network changed: " + + (underlying == null ? null : underlying.network)); + } // TODO(b/179091925): Move the delayed-message handling to BaseState @@ -1242,9 +1244,28 @@ public class VcnGatewayConnection extends StateMachine { createScheduledAlarm( SAFEMODE_TIMEOUT_ALARM, delayedMessage, - mVcnContext.isInTestMode() - ? TimeUnit.SECONDS.toMillis(SAFEMODE_TIMEOUT_SECONDS_TEST_MODE) - : TimeUnit.SECONDS.toMillis(SAFEMODE_TIMEOUT_SECONDS)); + getSafeModeTimeoutMs(mVcnContext, mLastSnapshot, mSubscriptionGroup)); + } + + /** Gets the safe mode timeout */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + public static long getSafeModeTimeoutMs( + VcnContext vcnContext, TelephonySubscriptionSnapshot snapshot, ParcelUuid subGrp) { + final int defaultSeconds = + vcnContext.isInTestMode() + ? SAFEMODE_TIMEOUT_SECONDS_TEST_MODE + : SAFEMODE_TIMEOUT_SECONDS; + + final PersistableBundleWrapper carrierConfig = snapshot.getCarrierConfigForSubGrp(subGrp); + int resultSeconds = defaultSeconds; + + if (vcnContext.isFlagSafeModeTimeoutConfigEnabled() && carrierConfig != null) { + resultSeconds = + carrierConfig.getInt( + VcnManager.VCN_SAFE_MODE_TIMEOUT_SECONDS_KEY, defaultSeconds); + } + + return TimeUnit.SECONDS.toMillis(resultSeconds); } private void cancelSafeModeAlarm() { @@ -1889,6 +1910,12 @@ public class VcnGatewayConnection extends StateMachine { // Transforms do not need to be persisted; the IkeSession will keep them alive mIpSecManager.applyTunnelModeTransform(tunnelIface, direction, transform); + if (direction == IpSecManager.DIRECTION_IN + && mVcnContext.isFlagNetworkMetricMonitorEnabled() + && mVcnContext.isFlagIpSecTransformStateEnabled()) { + mUnderlyingNetworkController.updateInboundTransform(mUnderlying, transform); + } + // For inbound transforms, additionally allow forwarded traffic to bridge to DUN (as // needed) final Set<Integer> exposedCaps = mConnectionConfig.getAllExposedCapabilities(); diff --git a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java new file mode 100644 index 000000000000..5f4852f77727 --- /dev/null +++ b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java @@ -0,0 +1,387 @@ +/* + * Copyright (C) 2023 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.routeselection; + +import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.IpSecTransformState; +import android.net.Network; +import android.net.vcn.VcnManager; +import android.os.Handler; +import android.os.HandlerExecutor; +import android.os.OutcomeReceiver; +import android.os.PowerManager; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.annotations.VisibleForTesting.Visibility; +import com.android.server.vcn.VcnContext; + +import java.util.BitSet; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +/** + * IpSecPacketLossDetector is responsible for continuously monitoring IPsec packet loss + * + * <p>When the packet loss rate surpass the threshold, IpSecPacketLossDetector will report it to the + * caller + * + * <p>IpSecPacketLossDetector will start monitoring when the network being monitored is selected AND + * an inbound IpSecTransform has been applied to this network. + * + * <p>This class is flag gated by "network_metric_monitor" and "ipsec_tramsform_state" + */ +public class IpSecPacketLossDetector extends NetworkMetricMonitor { + private static final String TAG = IpSecPacketLossDetector.class.getSimpleName(); + + @VisibleForTesting(visibility = Visibility.PRIVATE) + static final int PACKET_LOSS_UNAVALAIBLE = -1; + + // For VoIP, losses between 5% and 10% of the total packet stream will affect the quality + // significantly (as per "Computer Networking for LANS to WANS: Hardware, Software and + // Security"). For audio and video streaming, above 10-12% packet loss is unacceptable (as per + // "ICTP-SDU: About PingER"). Thus choose 12% as a conservative default threshold to declare a + // validation failure. + private static final int IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_DEFAULT = 12; + + private static final int POLL_IPSEC_STATE_INTERVAL_SECONDS_DEFAULT = 20; + + private long mPollIpSecStateIntervalMs; + private final int mPacketLossRatePercentThreshold; + + @NonNull private final Handler mHandler; + @NonNull private final PowerManager mPowerManager; + @NonNull private final Object mCancellationToken = new Object(); + @NonNull private final PacketLossCalculator mPacketLossCalculator; + + @Nullable private IpSecTransformWrapper mInboundTransform; + @Nullable private IpSecTransformState mLastIpSecTransformState; + + @VisibleForTesting(visibility = Visibility.PRIVATE) + public IpSecPacketLossDetector( + @NonNull VcnContext vcnContext, + @NonNull Network network, + @Nullable PersistableBundleWrapper carrierConfig, + @NonNull NetworkMetricMonitorCallback callback, + @NonNull Dependencies deps) + throws IllegalAccessException { + super(vcnContext, network, carrierConfig, callback); + + Objects.requireNonNull(deps, "Missing deps"); + + if (!vcnContext.isFlagIpSecTransformStateEnabled()) { + // Caller error + logWtf("ipsecTransformState flag disabled"); + throw new IllegalAccessException("ipsecTransformState flag disabled"); + } + + mHandler = new Handler(getVcnContext().getLooper()); + + mPowerManager = getVcnContext().getContext().getSystemService(PowerManager.class); + + mPacketLossCalculator = deps.getPacketLossCalculator(); + + mPollIpSecStateIntervalMs = getPollIpSecStateIntervalMs(carrierConfig); + mPacketLossRatePercentThreshold = getPacketLossRatePercentThreshold(carrierConfig); + + // Register for system broadcasts to monitor idle mode change + final IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); + getVcnContext() + .getContext() + .registerReceiver( + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals( + intent.getAction()) + && mPowerManager.isDeviceIdleMode()) { + mLastIpSecTransformState = null; + } + } + }, + intentFilter, + null /* broadcastPermission not required */, + mHandler); + } + + public IpSecPacketLossDetector( + @NonNull VcnContext vcnContext, + @NonNull Network network, + @Nullable PersistableBundleWrapper carrierConfig, + @NonNull NetworkMetricMonitorCallback callback) + throws IllegalAccessException { + this(vcnContext, network, carrierConfig, callback, new Dependencies()); + } + + @VisibleForTesting(visibility = Visibility.PRIVATE) + public static class Dependencies { + public PacketLossCalculator getPacketLossCalculator() { + return new PacketLossCalculator(); + } + } + + private static long getPollIpSecStateIntervalMs( + @Nullable PersistableBundleWrapper carrierConfig) { + final int seconds; + + if (carrierConfig != null) { + seconds = + carrierConfig.getInt( + VcnManager.VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY, + POLL_IPSEC_STATE_INTERVAL_SECONDS_DEFAULT); + } else { + seconds = POLL_IPSEC_STATE_INTERVAL_SECONDS_DEFAULT; + } + + return TimeUnit.SECONDS.toMillis(seconds); + } + + private static int getPacketLossRatePercentThreshold( + @Nullable PersistableBundleWrapper carrierConfig) { + if (carrierConfig != null) { + return carrierConfig.getInt( + VcnManager.VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY, + IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_DEFAULT); + } + return IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_DEFAULT; + } + + @Override + protected void onSelectedUnderlyingNetworkChanged() { + if (!isSelectedUnderlyingNetwork()) { + mInboundTransform = null; + stop(); + } + + // No action when the underlying network got selected. Wait for the inbound transform to + // start the monitor + } + + @Override + public void setInboundTransformInternal(@NonNull IpSecTransformWrapper inboundTransform) { + Objects.requireNonNull(inboundTransform, "inboundTransform is null"); + + if (Objects.equals(inboundTransform, mInboundTransform)) { + return; + } + + if (!isSelectedUnderlyingNetwork()) { + logWtf("setInboundTransform called but network not selected"); + return; + } + + // When multiple parallel inbound transforms are created, NetworkMetricMonitor will be + // enabled on the last one as a sample + mInboundTransform = inboundTransform; + start(); + } + + @Override + public void setCarrierConfig(@Nullable PersistableBundleWrapper carrierConfig) { + // The already scheduled event will not be affected. The followup events will be scheduled + // with the new interval + mPollIpSecStateIntervalMs = getPollIpSecStateIntervalMs(carrierConfig); + } + + @Override + protected void start() { + super.start(); + clearTransformStateAndPollingEvents(); + mHandler.postDelayed(new PollIpSecStateRunnable(), mCancellationToken, 0L); + } + + @Override + public void stop() { + super.stop(); + clearTransformStateAndPollingEvents(); + } + + private void clearTransformStateAndPollingEvents() { + mHandler.removeCallbacksAndEqualMessages(mCancellationToken); + mLastIpSecTransformState = null; + } + + @Override + public void close() { + super.close(); + + if (mInboundTransform != null) { + mInboundTransform.close(); + } + } + + @VisibleForTesting(visibility = Visibility.PRIVATE) + @Nullable + public IpSecTransformState getLastTransformState() { + return mLastIpSecTransformState; + } + + @VisibleForTesting(visibility = Visibility.PROTECTED) + @Nullable + public IpSecTransformWrapper getInboundTransformInternal() { + return mInboundTransform; + } + + private class PollIpSecStateRunnable implements Runnable { + @Override + public void run() { + if (!isStarted()) { + logWtf("Monitor stopped but PollIpSecStateRunnable not removed from Handler"); + return; + } + + getInboundTransformInternal() + .getIpSecTransformState( + new HandlerExecutor(mHandler), new IpSecTransformStateReceiver()); + + // Schedule for next poll + mHandler.postDelayed( + new PollIpSecStateRunnable(), mCancellationToken, mPollIpSecStateIntervalMs); + } + } + + private class IpSecTransformStateReceiver + implements OutcomeReceiver<IpSecTransformState, RuntimeException> { + @Override + public void onResult(@NonNull IpSecTransformState state) { + getVcnContext().ensureRunningOnLooperThread(); + + if (!isStarted()) { + return; + } + + onIpSecTransformStateReceived(state); + } + + @Override + public void onError(@NonNull RuntimeException error) { + getVcnContext().ensureRunningOnLooperThread(); + + // Nothing we can do here + logW("TransformStateReceiver#onError " + error.toString()); + } + } + + private void onIpSecTransformStateReceived(@NonNull IpSecTransformState state) { + if (mLastIpSecTransformState == null) { + // This is first time to poll the state + mLastIpSecTransformState = state; + return; + } + + final int packetLossRate = + mPacketLossCalculator.getPacketLossRatePercentage( + mLastIpSecTransformState, state, getLogPrefix()); + + if (packetLossRate == PACKET_LOSS_UNAVALAIBLE) { + return; + } + + final String logMsg = + "packetLossRate: " + + packetLossRate + + "% in the past " + + (state.getTimestamp() - mLastIpSecTransformState.getTimestamp()) + + "ms"; + + mLastIpSecTransformState = state; + if (packetLossRate < mPacketLossRatePercentThreshold) { + logV(logMsg); + onValidationResultReceivedInternal(false /* isFailed */); + } else { + logInfo(logMsg); + onValidationResultReceivedInternal(true /* isFailed */); + } + } + + @VisibleForTesting(visibility = Visibility.PRIVATE) + public static class PacketLossCalculator { + /** Calculate the packet loss rate between two timestamps */ + public int getPacketLossRatePercentage( + @NonNull IpSecTransformState oldState, + @NonNull IpSecTransformState newState, + String logPrefix) { + logVIpSecTransform("oldState", oldState, logPrefix); + logVIpSecTransform("newState", newState, logPrefix); + + final int replayWindowSize = oldState.getReplayBitmap().length * 8; + final long oldSeqHi = oldState.getRxHighestSequenceNumber(); + final long oldSeqLow = Math.max(0L, oldSeqHi - replayWindowSize + 1); + final long newSeqHi = newState.getRxHighestSequenceNumber(); + final long newSeqLow = Math.max(0L, newSeqHi - replayWindowSize + 1); + + if (oldSeqHi == newSeqHi || newSeqHi < replayWindowSize) { + // The replay window did not proceed and all packets might have been delivered out + // of order + return PACKET_LOSS_UNAVALAIBLE; + } + + // Get the expected packet count by assuming there is no packet loss. In this case, SA + // should receive all packets whose sequence numbers are smaller than the lower bound of + // the replay window AND the packets received within the window. + // When the lower bound is 0, it's not possible to tell whether packet with seqNo 0 is + // received or not. For simplicity just assume that packet is received. + final long newExpectedPktCnt = newSeqLow + getPacketCntInReplayWindow(newState); + final long oldExpectedPktCnt = oldSeqLow + getPacketCntInReplayWindow(oldState); + + final long expectedPktCntDiff = newExpectedPktCnt - oldExpectedPktCnt; + final long actualPktCntDiff = newState.getPacketCount() - oldState.getPacketCount(); + + logV( + TAG, + logPrefix + + " expectedPktCntDiff: " + + expectedPktCntDiff + + " actualPktCntDiff: " + + actualPktCntDiff); + + if (expectedPktCntDiff < 0 + || expectedPktCntDiff == 0 + || actualPktCntDiff < 0 + || actualPktCntDiff > expectedPktCntDiff) { + logWtf(TAG, "Impossible values for expectedPktCntDiff or" + " actualPktCntDiff"); + return PACKET_LOSS_UNAVALAIBLE; + } + + return 100 - (int) (actualPktCntDiff * 100 / expectedPktCntDiff); + } + } + + private static void logVIpSecTransform( + String transformTag, IpSecTransformState state, String logPrefix) { + final String stateString = + " seqNo: " + + state.getRxHighestSequenceNumber() + + " | pktCnt: " + + state.getPacketCount() + + " | pktCntInWindow: " + + getPacketCntInReplayWindow(state); + logV(TAG, logPrefix + " " + transformTag + stateString); + } + + /** Get the number of received packets within the replay window */ + private static long getPacketCntInReplayWindow(@NonNull IpSecTransformState state) { + return BitSet.valueOf(state.getReplayBitmap()).cardinality(); + } +} diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java b/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java new file mode 100644 index 000000000000..a79f188713e1 --- /dev/null +++ b/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2023 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.routeselection; + +import static com.android.server.VcnManagementService.LOCAL_LOG; +import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.IpSecTransform; +import android.net.IpSecTransformState; +import android.net.Network; +import android.os.OutcomeReceiver; +import android.util.CloseGuard; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.annotations.VisibleForTesting.Visibility; +import com.android.server.vcn.VcnContext; + +import java.util.Objects; +import java.util.concurrent.Executor; + +/** + * NetworkMetricMonitor is responsible for managing metric monitoring and tracking validation + * results. + * + * <p>This class is flag gated by "network_metric_monitor" + */ +public abstract class NetworkMetricMonitor implements AutoCloseable { + private static final String TAG = NetworkMetricMonitor.class.getSimpleName(); + + private static final boolean VDBG = false; // STOPSHIP: if true + + @NonNull private final CloseGuard mCloseGuard = new CloseGuard(); + + @NonNull private final VcnContext mVcnContext; + @NonNull private final Network mNetwork; + @NonNull private final NetworkMetricMonitorCallback mCallback; + + private boolean mIsSelectedUnderlyingNetwork; + private boolean mIsStarted; + private boolean mIsValidationFailed; + + protected NetworkMetricMonitor( + @NonNull VcnContext vcnContext, + @NonNull Network network, + @Nullable PersistableBundleWrapper carrierConfig, + @NonNull NetworkMetricMonitorCallback callback) + throws IllegalAccessException { + if (!vcnContext.isFlagNetworkMetricMonitorEnabled()) { + // Caller error + logWtf("networkMetricMonitor flag disabled"); + throw new IllegalAccessException("networkMetricMonitor flag disabled"); + } + + mVcnContext = Objects.requireNonNull(vcnContext, "Missing vcnContext"); + mNetwork = Objects.requireNonNull(network, "Missing network"); + mCallback = Objects.requireNonNull(callback, "Missing callback"); + + mIsSelectedUnderlyingNetwork = false; + mIsStarted = false; + mIsValidationFailed = false; + } + + /** Callback to notify caller of the validation result */ + public interface NetworkMetricMonitorCallback { + /** Called when there is a validation result is ready */ + void onValidationResultReceived(); + } + + /** + * Start monitoring + * + * <p>This method might be called on a an already started monitor for updating monitor + * properties (e.g. IpSecTransform, carrier config) + * + * <p>Subclasses MUST call super.start() when overriding this method + */ + protected void start() { + mIsStarted = true; + } + + /** + * Stop monitoring + * + * <p>Subclasses MUST call super.stop() when overriding this method + */ + public void stop() { + mIsValidationFailed = false; + mIsStarted = false; + } + + /** Called by the subclasses when the validation result is ready */ + protected void onValidationResultReceivedInternal(boolean isFailed) { + mIsValidationFailed = isFailed; + mCallback.onValidationResultReceived(); + } + + /** Called when the underlying network changes to selected or unselected */ + protected abstract void onSelectedUnderlyingNetworkChanged(); + + /** + * Mark the network being monitored selected or unselected + * + * <p>Subclasses MUST call super when overriding this method + */ + public void setIsSelectedUnderlyingNetwork(boolean isSelectedUnderlyingNetwork) { + if (mIsSelectedUnderlyingNetwork == isSelectedUnderlyingNetwork) { + return; + } + + mIsSelectedUnderlyingNetwork = isSelectedUnderlyingNetwork; + onSelectedUnderlyingNetworkChanged(); + } + + /** Wrapper that allows injection for testing purposes */ + @VisibleForTesting(visibility = Visibility.PROTECTED) + public static class IpSecTransformWrapper { + @NonNull public final IpSecTransform ipSecTransform; + + public IpSecTransformWrapper(@NonNull IpSecTransform ipSecTransform) { + this.ipSecTransform = ipSecTransform; + } + + /** Poll an IpSecTransformState */ + public void getIpSecTransformState( + @NonNull Executor executor, + @NonNull OutcomeReceiver<IpSecTransformState, RuntimeException> callback) { + ipSecTransform.getIpSecTransformState(executor, callback); + } + + /** Close this instance and release the underlying resources */ + public void close() { + ipSecTransform.close(); + } + + @Override + public int hashCode() { + return Objects.hash(ipSecTransform); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof IpSecTransformWrapper)) { + return false; + } + + final IpSecTransformWrapper other = (IpSecTransformWrapper) o; + + return Objects.equals(ipSecTransform, other.ipSecTransform); + } + } + + /** Set the IpSecTransform that applied to the Network being monitored */ + public void setInboundTransform(@NonNull IpSecTransform inTransform) { + setInboundTransformInternal(new IpSecTransformWrapper(inTransform)); + } + + /** + * Set the IpSecTransform that applied to the Network being monitored * + * + * <p>Subclasses MUST call super when overriding this method + */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + public void setInboundTransformInternal(@NonNull IpSecTransformWrapper inTransform) { + // Subclasses MUST override it if they care + } + + /** Update the carrierconfig */ + public void setCarrierConfig(@Nullable PersistableBundleWrapper carrierConfig) { + // Subclasses MUST override it if they care + } + + public boolean isValidationFailed() { + return mIsValidationFailed; + } + + public boolean isSelectedUnderlyingNetwork() { + return mIsSelectedUnderlyingNetwork; + } + + public boolean isStarted() { + return mIsStarted; + } + + @NonNull + public VcnContext getVcnContext() { + return mVcnContext; + } + + // Override methods for AutoCloseable. Subclasses MUST call super when overriding this method + @Override + public void close() { + mCloseGuard.close(); + + stop(); + } + + // Override #finalize() to use closeGuard for flagging that #close() was not called + @SuppressWarnings("Finalize") + @Override + protected void finalize() throws Throwable { + try { + if (mCloseGuard != null) { + mCloseGuard.warnIfOpen(); + } + close(); + } finally { + super.finalize(); + } + } + + private String getClassName() { + return this.getClass().getSimpleName(); + } + + protected String getLogPrefix() { + return " [Network " + mNetwork + "] "; + } + + protected void logV(String msg) { + if (VDBG) { + Slog.v(getClassName(), getLogPrefix() + msg); + LOCAL_LOG.log("[VERBOSE ] " + getClassName() + getLogPrefix() + msg); + } + } + + protected void logInfo(String msg) { + Slog.i(getClassName(), getLogPrefix() + msg); + LOCAL_LOG.log("[INFO ] " + getClassName() + getLogPrefix() + msg); + } + + protected void logW(String msg) { + Slog.w(getClassName(), getLogPrefix() + msg); + LOCAL_LOG.log("[WARN ] " + getClassName() + getLogPrefix() + msg); + } + + protected void logWtf(String msg) { + Slog.wtf(getClassName(), getLogPrefix() + msg); + LOCAL_LOG.log("[WTF ] " + getClassName() + getLogPrefix() + msg); + } + + protected static void logV(String className, String msgWithPrefix) { + if (VDBG) { + Slog.wtf(className, msgWithPrefix); + LOCAL_LOG.log("[VERBOSE ] " + className + msgWithPrefix); + } + } + + protected static void logWtf(String className, String msgWithPrefix) { + Slog.wtf(className, msgWithPrefix); + LOCAL_LOG.log("[WTF ] " + className + msgWithPrefix); + } +} diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java index 7f129ea3801c..d32e5cc8ef80 100644 --- a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java +++ b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java @@ -47,7 +47,6 @@ import com.android.server.vcn.VcnContext; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; /** @hide */ @@ -86,7 +85,6 @@ class NetworkPriorityClassifier { * <p>VCN MUST never select a non-INTERNET network that are unvalidated or fail to match any * template as the underlying network. */ - @VisibleForTesting(visibility = Visibility.PRIVATE) static final int PRIORITY_INVALID = -1; /** Gives networks a priority class, based on configured VcnGatewayConnectionConfig */ @@ -96,7 +94,7 @@ class NetworkPriorityClassifier { List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, ParcelUuid subscriptionGroup, TelephonySubscriptionSnapshot snapshot, - UnderlyingNetworkRecord currentlySelected, + boolean isSelected, PersistableBundleWrapper carrierConfig) { // mRouteSelectionNetworkRequest requires a network be both VALIDATED and NOT_SUSPENDED @@ -118,7 +116,7 @@ class NetworkPriorityClassifier { networkRecord, subscriptionGroup, snapshot, - currentlySelected, + isSelected, carrierConfig)) { return priorityIndex; } @@ -140,12 +138,9 @@ class NetworkPriorityClassifier { UnderlyingNetworkRecord networkRecord, ParcelUuid subscriptionGroup, TelephonySubscriptionSnapshot snapshot, - UnderlyingNetworkRecord currentlySelected, + boolean isSelected, PersistableBundleWrapper carrierConfig) { final NetworkCapabilities caps = networkRecord.networkCapabilities; - final boolean isSelectedUnderlyingNetwork = - currentlySelected != null - && Objects.equals(currentlySelected.network, networkRecord.network); final int meteredMatch = networkPriority.getMetered(); final boolean isMetered = !caps.hasCapability(NET_CAPABILITY_NOT_METERED); @@ -159,7 +154,7 @@ class NetworkPriorityClassifier { if (caps.getLinkUpstreamBandwidthKbps() < networkPriority.getMinExitUpstreamBandwidthKbps() || (caps.getLinkUpstreamBandwidthKbps() < networkPriority.getMinEntryUpstreamBandwidthKbps() - && !isSelectedUnderlyingNetwork)) { + && !isSelected)) { return false; } @@ -167,7 +162,7 @@ class NetworkPriorityClassifier { < networkPriority.getMinExitDownstreamBandwidthKbps() || (caps.getLinkDownstreamBandwidthKbps() < networkPriority.getMinEntryDownstreamBandwidthKbps() - && !isSelectedUnderlyingNetwork)) { + && !isSelected)) { return false; } @@ -191,7 +186,7 @@ class NetworkPriorityClassifier { return checkMatchesWifiPriorityRule( (VcnWifiUnderlyingNetworkTemplate) networkPriority, networkRecord, - currentlySelected, + isSelected, carrierConfig); } @@ -214,7 +209,7 @@ class NetworkPriorityClassifier { public static boolean checkMatchesWifiPriorityRule( VcnWifiUnderlyingNetworkTemplate networkPriority, UnderlyingNetworkRecord networkRecord, - UnderlyingNetworkRecord currentlySelected, + boolean isSelected, PersistableBundleWrapper carrierConfig) { final NetworkCapabilities caps = networkRecord.networkCapabilities; @@ -223,7 +218,7 @@ class NetworkPriorityClassifier { } // TODO: Move the Network Quality check to the network metric monitor framework. - if (!isWifiRssiAcceptable(networkRecord, currentlySelected, carrierConfig)) { + if (!isWifiRssiAcceptable(networkRecord, isSelected, carrierConfig)) { return false; } @@ -237,15 +232,11 @@ class NetworkPriorityClassifier { private static boolean isWifiRssiAcceptable( UnderlyingNetworkRecord networkRecord, - UnderlyingNetworkRecord currentlySelected, + boolean isSelected, PersistableBundleWrapper carrierConfig) { final NetworkCapabilities caps = networkRecord.networkCapabilities; - final boolean isSelectedNetwork = - currentlySelected != null - && networkRecord.network.equals(currentlySelected.network); - if (isSelectedNetwork - && caps.getSignalStrength() >= getWifiExitRssiThreshold(carrierConfig)) { + if (isSelected && caps.getSignalStrength() >= getWifiExitRssiThreshold(carrierConfig)) { return true; } diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java index 6afa795e96fa..3f8d39e72e89 100644 --- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java +++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java @@ -30,6 +30,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; +import android.net.IpSecTransform; import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; @@ -48,9 +49,11 @@ 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.IndentingPrintWriter; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import com.android.server.vcn.VcnContext; +import com.android.server.vcn.routeselection.UnderlyingNetworkEvaluator.NetworkEvaluatorCallback; import com.android.server.vcn.util.LogUtils; import java.util.ArrayList; @@ -83,6 +86,9 @@ public class UnderlyingNetworkController { @NonNull private final TelephonyCallback mActiveDataSubIdListener = new VcnActiveDataSubscriptionIdListener(); + private final Map<Network, UnderlyingNetworkEvaluator> mUnderlyingNetworkRecords = + new ArrayMap<>(); + @NonNull private final List<NetworkCallback> mCellBringupCallbacks = new ArrayList<>(); @Nullable private NetworkCallback mWifiBringupCallback; @Nullable private NetworkCallback mWifiEntryRssiThresholdCallback; @@ -105,7 +111,8 @@ public class UnderlyingNetworkController { this(vcnContext, connectionConfig, subscriptionGroup, snapshot, cb, new Dependencies()); } - private UnderlyingNetworkController( + @VisibleForTesting(visibility = Visibility.PRIVATE) + UnderlyingNetworkController( @NonNull VcnContext vcnContext, @NonNull VcnGatewayConnectionConfig connectionConfig, @NonNull ParcelUuid subscriptionGroup, @@ -197,6 +204,15 @@ public class UnderlyingNetworkController { List<NetworkCallback> oldCellCallbacks = new ArrayList<>(mCellBringupCallbacks); mCellBringupCallbacks.clear(); + if (mVcnContext.isFlagNetworkMetricMonitorEnabled() + && mVcnContext.isFlagIpSecTransformStateEnabled()) { + for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) { + evaluator.close(); + } + } + + mUnderlyingNetworkRecords.clear(); + // Register new callbacks. Make-before-break; always register new callbacks before removal // of old callbacks if (!mIsQuitting) { @@ -395,15 +411,58 @@ public class UnderlyingNetworkController { // Update carrier config mCarrierConfig = mLastSnapshot.getCarrierConfigForSubGrp(mSubscriptionGroup); + // Make sure all evaluators use the same updated TelephonySubscriptionSnapshot and carrier + // config to calculate their cached priority classes. For simplicity, the + // UnderlyingNetworkController does not listen for changes in VCN-related carrier config + // keys, and changes are applied at restart of the VcnGatewayConnection + for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) { + evaluator.reevaluate( + mConnectionConfig.getVcnUnderlyingNetworkPriorities(), + mSubscriptionGroup, + mLastSnapshot, + mCarrierConfig); + } + // Only trigger re-registration if subIds in this group have changed if (oldSnapshot .getAllSubIdsInGroup(mSubscriptionGroup) .equals(newSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))) { + + if (mVcnContext.isFlagNetworkMetricMonitorEnabled() + && mVcnContext.isFlagIpSecTransformStateEnabled()) { + reevaluateNetworks(); + } return; } registerOrUpdateNetworkRequests(); } + /** + * Pass the IpSecTransform of the VCN to UnderlyingNetworkController for metric monitoring + * + * <p>Caller MUST call it when IpSecTransforms have been created for VCN creation or migration + */ + public void updateInboundTransform( + @NonNull UnderlyingNetworkRecord currentNetwork, @NonNull IpSecTransform transform) { + if (!mVcnContext.isFlagNetworkMetricMonitorEnabled() + || !mVcnContext.isFlagIpSecTransformStateEnabled()) { + logWtf("#updateInboundTransform: unexpected call; flags missing"); + return; + } + + Objects.requireNonNull(currentNetwork, "currentNetwork is null"); + Objects.requireNonNull(transform, "transform is null"); + + if (mCurrentRecord == null + || mRouteSelectionCallback == null + || !Objects.equals(currentNetwork.network, mCurrentRecord.network)) { + // The caller (VcnGatewayConnection) is out-of-dated. Ignore this call. + return; + } + + mUnderlyingNetworkRecords.get(mCurrentRecord.network).setInboundTransform(transform); + } + /** Tears down this Tracker, and releases all underlying network requests. */ public void teardown() { mVcnContext.ensureRunningOnLooperThread(); @@ -418,32 +477,62 @@ public class UnderlyingNetworkController { .unregisterTelephonyCallback(mActiveDataSubIdListener); } + private TreeSet<UnderlyingNetworkEvaluator> getSortedUnderlyingNetworks() { + TreeSet<UnderlyingNetworkEvaluator> sorted = + new TreeSet<>(UnderlyingNetworkEvaluator.getComparator(mVcnContext)); + + for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) { + if (evaluator.getPriorityClass() != NetworkPriorityClassifier.PRIORITY_INVALID) { + sorted.add(evaluator); + } + } + + return sorted; + } + private void reevaluateNetworks() { if (mIsQuitting || mRouteSelectionCallback == null) { return; // UnderlyingNetworkController has quit. } - TreeSet<UnderlyingNetworkRecord> sorted = - mRouteSelectionCallback.getSortedUnderlyingNetworks(); - UnderlyingNetworkRecord candidate = sorted.isEmpty() ? null : sorted.first(); + TreeSet<UnderlyingNetworkEvaluator> sorted = getSortedUnderlyingNetworks(); + + UnderlyingNetworkEvaluator candidateEvaluator = sorted.isEmpty() ? null : sorted.first(); + UnderlyingNetworkRecord candidate = + candidateEvaluator == null ? null : candidateEvaluator.getNetworkRecord(); if (Objects.equals(mCurrentRecord, candidate)) { return; } String allNetworkPriorities = ""; - for (UnderlyingNetworkRecord record : sorted) { + for (UnderlyingNetworkEvaluator recordEvaluator : sorted) { if (!allNetworkPriorities.isEmpty()) { allNetworkPriorities += ", "; } - allNetworkPriorities += record.network + ": " + record.priorityClass; + allNetworkPriorities += + recordEvaluator.getNetwork() + ": " + recordEvaluator.getPriorityClass(); } - logInfo( - "Selected network changed to " - + (candidate == null ? null : candidate.network) - + ", selected from list: " - + allNetworkPriorities); + + if (!UnderlyingNetworkRecord.isSameNetwork(mCurrentRecord, candidate)) { + logInfo( + "Selected network changed to " + + (candidate == null ? null : candidate.network) + + ", selected from list: " + + allNetworkPriorities); + } + mCurrentRecord = candidate; mCb.onSelectedUnderlyingNetworkChanged(mCurrentRecord); + + // Need to update all evaluators to ensure the previously selected one is unselected + for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) { + evaluator.setIsSelected( + candidateEvaluator == evaluator, + mConnectionConfig.getVcnUnderlyingNetworkPriorities(), + mSubscriptionGroup, + mLastSnapshot, + mCarrierConfig); + } } /** @@ -463,46 +552,32 @@ public class UnderlyingNetworkController { */ @VisibleForTesting class UnderlyingNetworkListener extends NetworkCallback { - private final Map<Network, UnderlyingNetworkRecord.Builder> - mUnderlyingNetworkRecordBuilders = new ArrayMap<>(); - UnderlyingNetworkListener() { super(NetworkCallback.FLAG_INCLUDE_LOCATION_INFO); } - private TreeSet<UnderlyingNetworkRecord> getSortedUnderlyingNetworks() { - TreeSet<UnderlyingNetworkRecord> sorted = - new TreeSet<>(UnderlyingNetworkRecord.getComparator()); - - for (UnderlyingNetworkRecord.Builder builder : - mUnderlyingNetworkRecordBuilders.values()) { - if (builder.isValid()) { - final UnderlyingNetworkRecord record = - builder.build( - mVcnContext, - mConnectionConfig.getVcnUnderlyingNetworkPriorities(), - mSubscriptionGroup, - mLastSnapshot, - mCurrentRecord, - mCarrierConfig); - if (record.priorityClass != NetworkPriorityClassifier.PRIORITY_INVALID) { - sorted.add(record); - } - } - } - - return sorted; - } - @Override public void onAvailable(@NonNull Network network) { - mUnderlyingNetworkRecordBuilders.put( - network, new UnderlyingNetworkRecord.Builder(network)); + mUnderlyingNetworkRecords.put( + network, + mDeps.newUnderlyingNetworkEvaluator( + mVcnContext, + network, + mConnectionConfig.getVcnUnderlyingNetworkPriorities(), + mSubscriptionGroup, + mLastSnapshot, + mCarrierConfig, + new NetworkEvaluatorCallbackImpl())); } @Override public void onLost(@NonNull Network network) { - mUnderlyingNetworkRecordBuilders.remove(network); + if (mVcnContext.isFlagNetworkMetricMonitorEnabled() + && mVcnContext.isFlagIpSecTransformStateEnabled()) { + mUnderlyingNetworkRecords.get(network).close(); + } + + mUnderlyingNetworkRecords.remove(network); reevaluateNetworks(); } @@ -510,15 +585,20 @@ public class UnderlyingNetworkController { @Override public void onCapabilitiesChanged( @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) { - final UnderlyingNetworkRecord.Builder builder = - mUnderlyingNetworkRecordBuilders.get(network); - if (builder == null) { + final UnderlyingNetworkEvaluator evaluator = mUnderlyingNetworkRecords.get(network); + if (evaluator == null) { logWtf("Got capabilities change for unknown key: " + network); return; } - builder.setNetworkCapabilities(networkCapabilities); - if (builder.isValid()) { + evaluator.setNetworkCapabilities( + networkCapabilities, + mConnectionConfig.getVcnUnderlyingNetworkPriorities(), + mSubscriptionGroup, + mLastSnapshot, + mCarrierConfig); + + if (evaluator.isValid()) { reevaluateNetworks(); } } @@ -526,35 +606,60 @@ public class UnderlyingNetworkController { @Override public void onLinkPropertiesChanged( @NonNull Network network, @NonNull LinkProperties linkProperties) { - final UnderlyingNetworkRecord.Builder builder = - mUnderlyingNetworkRecordBuilders.get(network); - if (builder == null) { + final UnderlyingNetworkEvaluator evaluator = mUnderlyingNetworkRecords.get(network); + if (evaluator == null) { logWtf("Got link properties change for unknown key: " + network); return; } - builder.setLinkProperties(linkProperties); - if (builder.isValid()) { + evaluator.setLinkProperties( + linkProperties, + mConnectionConfig.getVcnUnderlyingNetworkPriorities(), + mSubscriptionGroup, + mLastSnapshot, + mCarrierConfig); + + if (evaluator.isValid()) { reevaluateNetworks(); } } @Override public void onBlockedStatusChanged(@NonNull Network network, boolean isBlocked) { - final UnderlyingNetworkRecord.Builder builder = - mUnderlyingNetworkRecordBuilders.get(network); - if (builder == null) { + final UnderlyingNetworkEvaluator evaluator = mUnderlyingNetworkRecords.get(network); + if (evaluator == null) { logWtf("Got blocked status change for unknown key: " + network); return; } - builder.setIsBlocked(isBlocked); - if (builder.isValid()) { + evaluator.setIsBlocked( + isBlocked, + mConnectionConfig.getVcnUnderlyingNetworkPriorities(), + mSubscriptionGroup, + mLastSnapshot, + mCarrierConfig); + + if (evaluator.isValid()) { reevaluateNetworks(); } } } + @VisibleForTesting + class NetworkEvaluatorCallbackImpl implements NetworkEvaluatorCallback { + @Override + public void onEvaluationResultChanged() { + if (!mVcnContext.isFlagNetworkMetricMonitorEnabled() + || !mVcnContext.isFlagIpSecTransformStateEnabled()) { + logWtf("#onEvaluationResultChanged: unexpected call; flags missing"); + return; + } + + mVcnContext.ensureRunningOnLooperThread(); + reevaluateNetworks(); + } + } + private String getLogPrefix() { return "(" + LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup) @@ -614,16 +719,8 @@ public class UnderlyingNetworkController { pw.println("Underlying networks:"); pw.increaseIndent(); if (mRouteSelectionCallback != null) { - for (UnderlyingNetworkRecord record : - mRouteSelectionCallback.getSortedUnderlyingNetworks()) { - record.dump( - mVcnContext, - pw, - mConnectionConfig.getVcnUnderlyingNetworkPriorities(), - mSubscriptionGroup, - mLastSnapshot, - mCurrentRecord, - mCarrierConfig); + for (UnderlyingNetworkEvaluator recordEvaluator : getSortedUnderlyingNetworks()) { + recordEvaluator.dump(pw); } } pw.decreaseIndent(); @@ -653,5 +750,24 @@ public class UnderlyingNetworkController { @Nullable UnderlyingNetworkRecord underlyingNetworkRecord); } - private static class Dependencies {} + @VisibleForTesting(visibility = Visibility.PRIVATE) + public static class Dependencies { + public UnderlyingNetworkEvaluator newUnderlyingNetworkEvaluator( + @NonNull VcnContext vcnContext, + @NonNull Network network, + @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, + @NonNull ParcelUuid subscriptionGroup, + @NonNull TelephonySubscriptionSnapshot lastSnapshot, + @Nullable PersistableBundleWrapper carrierConfig, + @NonNull NetworkEvaluatorCallback evaluatorCallback) { + return new UnderlyingNetworkEvaluator( + vcnContext, + network, + underlyingNetworkTemplates, + subscriptionGroup, + lastSnapshot, + carrierConfig, + evaluatorCallback); + } + } } diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java new file mode 100644 index 000000000000..2f4cf5e5d8c7 --- /dev/null +++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java @@ -0,0 +1,442 @@ +/* + * Copyright (C) 2023 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.routeselection; + +import static com.android.server.VcnManagementService.LOCAL_LOG; +import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.IpSecTransform; +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.vcn.VcnManager; +import android.net.vcn.VcnUnderlyingNetworkTemplate; +import android.os.Handler; +import android.os.ParcelUuid; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.annotations.VisibleForTesting.Visibility; +import com.android.internal.util.IndentingPrintWriter; +import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; +import com.android.server.vcn.VcnContext; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +/** + * UnderlyingNetworkEvaluator evaluates the quality and priority class of a network candidate for + * route selection. + * + * @hide + */ +public class UnderlyingNetworkEvaluator { + private static final String TAG = UnderlyingNetworkEvaluator.class.getSimpleName(); + + private static final int[] PENALTY_TIMEOUT_MINUTES_DEFAULT = new int[] {5}; + + @NonNull private final VcnContext mVcnContext; + @NonNull private final Handler mHandler; + @NonNull private final Object mCancellationToken = new Object(); + + @NonNull private final UnderlyingNetworkRecord.Builder mNetworkRecordBuilder; + + @NonNull private final NetworkEvaluatorCallback mEvaluatorCallback; + @NonNull private final List<NetworkMetricMonitor> mMetricMonitors = new ArrayList<>(); + + @NonNull private final Dependencies mDependencies; + + // TODO: Support back-off timeouts + private long mPenalizedTimeoutMs; + + private boolean mIsSelected; + private boolean mIsPenalized; + private int mPriorityClass = NetworkPriorityClassifier.PRIORITY_INVALID; + + @VisibleForTesting(visibility = Visibility.PRIVATE) + public UnderlyingNetworkEvaluator( + @NonNull VcnContext vcnContext, + @NonNull Network network, + @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, + @NonNull ParcelUuid subscriptionGroup, + @NonNull TelephonySubscriptionSnapshot lastSnapshot, + @Nullable PersistableBundleWrapper carrierConfig, + @NonNull NetworkEvaluatorCallback evaluatorCallback, + @NonNull Dependencies dependencies) { + mVcnContext = Objects.requireNonNull(vcnContext, "Missing vcnContext"); + mHandler = new Handler(mVcnContext.getLooper()); + + mDependencies = Objects.requireNonNull(dependencies, "Missing dependencies"); + mEvaluatorCallback = Objects.requireNonNull(evaluatorCallback, "Missing deps"); + + Objects.requireNonNull(underlyingNetworkTemplates, "Missing underlyingNetworkTemplates"); + Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup"); + Objects.requireNonNull(lastSnapshot, "Missing lastSnapshot"); + + mNetworkRecordBuilder = + new UnderlyingNetworkRecord.Builder( + Objects.requireNonNull(network, "Missing network")); + mIsSelected = false; + mIsPenalized = false; + mPenalizedTimeoutMs = getPenaltyTimeoutMs(carrierConfig); + + updatePriorityClass( + underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig); + + if (isIpSecPacketLossDetectorEnabled()) { + try { + mMetricMonitors.add( + mDependencies.newIpSecPacketLossDetector( + mVcnContext, + mNetworkRecordBuilder.getNetwork(), + carrierConfig, + new MetricMonitorCallbackImpl())); + } catch (IllegalAccessException e) { + // No action. Do not add anything to mMetricMonitors + } + } + } + + public UnderlyingNetworkEvaluator( + @NonNull VcnContext vcnContext, + @NonNull Network network, + @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, + @NonNull ParcelUuid subscriptionGroup, + @NonNull TelephonySubscriptionSnapshot lastSnapshot, + @Nullable PersistableBundleWrapper carrierConfig, + @NonNull NetworkEvaluatorCallback evaluatorCallback) { + this( + vcnContext, + network, + underlyingNetworkTemplates, + subscriptionGroup, + lastSnapshot, + carrierConfig, + evaluatorCallback, + new Dependencies()); + } + + @VisibleForTesting(visibility = Visibility.PRIVATE) + public static class Dependencies { + /** Get an IpSecPacketLossDetector instance */ + public IpSecPacketLossDetector newIpSecPacketLossDetector( + @NonNull VcnContext vcnContext, + @NonNull Network network, + @Nullable PersistableBundleWrapper carrierConfig, + @NonNull NetworkMetricMonitor.NetworkMetricMonitorCallback callback) + throws IllegalAccessException { + return new IpSecPacketLossDetector(vcnContext, network, carrierConfig, callback); + } + } + + /** Callback to notify caller to reevaluate network selection */ + public interface NetworkEvaluatorCallback { + /** + * Called when mIsPenalized changed + * + * <p>When receiving this call, UnderlyingNetworkController should reevaluate all network + * candidates for VCN underlying network selection + */ + void onEvaluationResultChanged(); + } + + private class MetricMonitorCallbackImpl + implements NetworkMetricMonitor.NetworkMetricMonitorCallback { + public void onValidationResultReceived() { + mVcnContext.ensureRunningOnLooperThread(); + + handleValidationResult(); + } + } + + private void updatePriorityClass( + @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, + @NonNull ParcelUuid subscriptionGroup, + @NonNull TelephonySubscriptionSnapshot lastSnapshot, + @Nullable PersistableBundleWrapper carrierConfig) { + if (mNetworkRecordBuilder.isValid()) { + mPriorityClass = + NetworkPriorityClassifier.calculatePriorityClass( + mVcnContext, + mNetworkRecordBuilder.build(), + underlyingNetworkTemplates, + subscriptionGroup, + lastSnapshot, + mIsSelected, + carrierConfig); + } else { + mPriorityClass = NetworkPriorityClassifier.PRIORITY_INVALID; + } + } + + private boolean isIpSecPacketLossDetectorEnabled() { + return isIpSecPacketLossDetectorEnabled(mVcnContext); + } + + private static boolean isIpSecPacketLossDetectorEnabled(VcnContext vcnContext) { + return vcnContext.isFlagIpSecTransformStateEnabled() + && vcnContext.isFlagNetworkMetricMonitorEnabled(); + } + + /** Get the comparator for UnderlyingNetworkEvaluator */ + public static Comparator<UnderlyingNetworkEvaluator> getComparator(VcnContext vcnContext) { + return (left, right) -> { + if (isIpSecPacketLossDetectorEnabled(vcnContext)) { + if (left.mIsPenalized != right.mIsPenalized) { + // A penalized network should have lower priority which means a larger index + return left.mIsPenalized ? 1 : -1; + } + } + + final int leftIndex = left.mPriorityClass; + final int rightIndex = right.mPriorityClass; + + // In the case of networks in the same priority class, prioritize based on other + // criteria (eg. actively selected network, link metrics, etc) + if (leftIndex == rightIndex) { + // TODO: Improve the strategy of network selection when both UnderlyingNetworkRecord + // fall into the same priority class. + if (left.mIsSelected) { + return -1; + } + if (right.mIsSelected) { + return 1; + } + } + return Integer.compare(leftIndex, rightIndex); + }; + } + + private static long getPenaltyTimeoutMs(@Nullable PersistableBundleWrapper carrierConfig) { + final int[] timeoutMinuteList; + + if (carrierConfig != null) { + timeoutMinuteList = + carrierConfig.getIntArray( + VcnManager.VCN_NETWORK_SELECTION_PENALTY_TIMEOUT_MINUTES_LIST_KEY, + PENALTY_TIMEOUT_MINUTES_DEFAULT); + } else { + timeoutMinuteList = PENALTY_TIMEOUT_MINUTES_DEFAULT; + } + + // TODO: Add the support of back-off timeouts and return the full list + return TimeUnit.MINUTES.toMillis(timeoutMinuteList[0]); + } + + private void handleValidationResult() { + final boolean wasPenalized = mIsPenalized; + mIsPenalized = false; + for (NetworkMetricMonitor monitor : mMetricMonitors) { + mIsPenalized |= monitor.isValidationFailed(); + } + + if (wasPenalized == mIsPenalized) { + return; + } + + logInfo( + "#handleValidationResult: wasPenalized " + + wasPenalized + + " mIsPenalized " + + mIsPenalized); + + if (mIsPenalized) { + mHandler.postDelayed( + new ExitPenaltyBoxRunnable(), mCancellationToken, mPenalizedTimeoutMs); + } else { + // Exit the penalty box + mHandler.removeCallbacksAndEqualMessages(mCancellationToken); + } + mEvaluatorCallback.onEvaluationResultChanged(); + } + + public class ExitPenaltyBoxRunnable implements Runnable { + @Override + public void run() { + if (!mIsPenalized) { + logWtf("Evaluator not being penalized but ExitPenaltyBoxRunnable was scheduled"); + return; + } + + // TODO: There might be a future metric monitor (e.g. ping) that will require the + // validation to pass before exiting the penalty box. + mIsPenalized = false; + mEvaluatorCallback.onEvaluationResultChanged(); + } + } + + /** Set the NetworkCapabilities */ + public void setNetworkCapabilities( + @NonNull NetworkCapabilities nc, + @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, + @NonNull ParcelUuid subscriptionGroup, + @NonNull TelephonySubscriptionSnapshot lastSnapshot, + @Nullable PersistableBundleWrapper carrierConfig) { + mNetworkRecordBuilder.setNetworkCapabilities(nc); + + updatePriorityClass( + underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig); + } + + /** Set the LinkProperties */ + public void setLinkProperties( + @NonNull LinkProperties lp, + @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, + @NonNull ParcelUuid subscriptionGroup, + @NonNull TelephonySubscriptionSnapshot lastSnapshot, + @Nullable PersistableBundleWrapper carrierConfig) { + mNetworkRecordBuilder.setLinkProperties(lp); + + updatePriorityClass( + underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig); + } + + /** Set whether the network is blocked */ + public void setIsBlocked( + boolean isBlocked, + @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, + @NonNull ParcelUuid subscriptionGroup, + @NonNull TelephonySubscriptionSnapshot lastSnapshot, + @Nullable PersistableBundleWrapper carrierConfig) { + mNetworkRecordBuilder.setIsBlocked(isBlocked); + + updatePriorityClass( + underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig); + } + + /** Set whether the network is selected as VCN's underlying network */ + public void setIsSelected( + boolean isSelected, + @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, + @NonNull ParcelUuid subscriptionGroup, + @NonNull TelephonySubscriptionSnapshot lastSnapshot, + @Nullable PersistableBundleWrapper carrierConfig) { + mIsSelected = isSelected; + + updatePriorityClass( + underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig); + + for (NetworkMetricMonitor monitor : mMetricMonitors) { + monitor.setIsSelectedUnderlyingNetwork(isSelected); + } + } + + /** + * Update the last TelephonySubscriptionSnapshot and carrier config to reevaluate the network + */ + public void reevaluate( + @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, + @NonNull ParcelUuid subscriptionGroup, + @NonNull TelephonySubscriptionSnapshot lastSnapshot, + @Nullable PersistableBundleWrapper carrierConfig) { + updatePriorityClass( + underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig); + + // The already scheduled event will not be affected. The followup events will be scheduled + // with the new timeout + mPenalizedTimeoutMs = getPenaltyTimeoutMs(carrierConfig); + + for (NetworkMetricMonitor monitor : mMetricMonitors) { + monitor.setCarrierConfig(carrierConfig); + } + } + + /** Update the inbound IpSecTransform applied to the network */ + public void setInboundTransform(@NonNull IpSecTransform transform) { + if (!mIsSelected) { + logWtf("setInboundTransform on an unselected evaluator"); + return; + } + + for (NetworkMetricMonitor monitor : mMetricMonitors) { + monitor.setInboundTransform(transform); + } + } + + /** Close the evaluator and stop all the underlying network metric monitors */ + public void close() { + mHandler.removeCallbacksAndEqualMessages(mCancellationToken); + + for (NetworkMetricMonitor monitor : mMetricMonitors) { + monitor.close(); + } + } + + /** Return whether this network evaluator is valid */ + public boolean isValid() { + return mNetworkRecordBuilder.isValid(); + } + + /** Return the network */ + public Network getNetwork() { + return mNetworkRecordBuilder.getNetwork(); + } + + /** Return the network record */ + public UnderlyingNetworkRecord getNetworkRecord() { + return mNetworkRecordBuilder.build(); + } + + /** Return the priority class for network selection */ + public int getPriorityClass() { + return mPriorityClass; + } + + /** Return whether the network is being penalized */ + public boolean isPenalized() { + return mIsPenalized; + } + + /** Dump the information of this instance */ + public void dump(IndentingPrintWriter pw) { + pw.println("UnderlyingNetworkEvaluator:"); + pw.increaseIndent(); + + if (mNetworkRecordBuilder.isValid()) { + getNetworkRecord().dump(pw); + } else { + pw.println( + "UnderlyingNetworkRecord incomplete: mNetwork: " + + mNetworkRecordBuilder.getNetwork()); + } + + pw.println("mIsSelected: " + mIsSelected); + pw.println("mPriorityClass: " + mPriorityClass); + pw.println("mIsPenalized: " + mIsPenalized); + + pw.decreaseIndent(); + } + + private String getLogPrefix() { + return "[Network " + mNetworkRecordBuilder.getNetwork() + "] "; + } + + private void logInfo(String msg) { + Slog.i(TAG, getLogPrefix() + msg); + LOCAL_LOG.log("[INFO ] " + TAG + getLogPrefix() + msg); + } + + private void logWtf(String msg) { + Slog.wtf(TAG, getLogPrefix() + msg); + LOCAL_LOG.log("[WTF ] " + TAG + getLogPrefix() + msg); + } +} diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java index aea9f4d2dbae..7ab8e552722a 100644 --- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java +++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java @@ -16,24 +16,17 @@ package com.android.server.vcn.routeselection; -import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; import android.annotation.NonNull; import android.annotation.Nullable; import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; -import android.net.vcn.VcnUnderlyingNetworkTemplate; -import android.os.ParcelUuid; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; import com.android.internal.util.IndentingPrintWriter; -import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; -import com.android.server.vcn.VcnContext; -import java.util.Comparator; -import java.util.List; import java.util.Objects; /** @@ -46,54 +39,17 @@ public class UnderlyingNetworkRecord { @NonNull public final NetworkCapabilities networkCapabilities; @NonNull public final LinkProperties linkProperties; public final boolean isBlocked; - public final boolean isSelected; - public final int priorityClass; @VisibleForTesting(visibility = Visibility.PRIVATE) public UnderlyingNetworkRecord( @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities, @NonNull LinkProperties linkProperties, - boolean isBlocked, - VcnContext vcnContext, - List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, - ParcelUuid subscriptionGroup, - TelephonySubscriptionSnapshot snapshot, - UnderlyingNetworkRecord currentlySelected, - PersistableBundleWrapper carrierConfig) { + boolean isBlocked) { this.network = network; this.networkCapabilities = networkCapabilities; this.linkProperties = linkProperties; this.isBlocked = isBlocked; - - this.isSelected = isSelected(this.network, currentlySelected); - - priorityClass = - NetworkPriorityClassifier.calculatePriorityClass( - vcnContext, - this, - underlyingNetworkTemplates, - subscriptionGroup, - snapshot, - currentlySelected, - carrierConfig); - } - - @VisibleForTesting(visibility = Visibility.PRIVATE) - public UnderlyingNetworkRecord( - @NonNull Network network, - @NonNull NetworkCapabilities networkCapabilities, - @NonNull LinkProperties linkProperties, - boolean isBlocked, - boolean isSelected, - int priorityClass) { - this.network = network; - this.networkCapabilities = networkCapabilities; - this.linkProperties = linkProperties; - this.isBlocked = isBlocked; - this.isSelected = isSelected; - - this.priorityClass = priorityClass; } @Override @@ -113,64 +69,20 @@ public class UnderlyingNetworkRecord { return Objects.hash(network, networkCapabilities, linkProperties, isBlocked); } - /** Returns if two records are equal including their priority classes. */ - public static boolean isEqualIncludingPriorities( - UnderlyingNetworkRecord left, UnderlyingNetworkRecord right) { - if (left != null && right != null) { - return left.equals(right) - && left.isSelected == right.isSelected - && left.priorityClass == right.priorityClass; - } - - return left == right; - } - - static Comparator<UnderlyingNetworkRecord> getComparator() { - return (left, right) -> { - final int leftIndex = left.priorityClass; - final int rightIndex = right.priorityClass; - - // In the case of networks in the same priority class, prioritize based on other - // criteria (eg. actively selected network, link metrics, etc) - if (leftIndex == rightIndex) { - // TODO: Improve the strategy of network selection when both UnderlyingNetworkRecord - // fall into the same priority class. - if (left.isSelected) { - return -1; - } - if (right.isSelected) { - return 1; - } - } - return Integer.compare(leftIndex, rightIndex); - }; - } - - private static boolean isSelected( - Network networkToCheck, UnderlyingNetworkRecord currentlySelected) { - if (currentlySelected == null) { - return false; - } - if (currentlySelected.network.equals(networkToCheck)) { - return true; - } - return false; + /** Return whether two records represent the same network */ + public static boolean isSameNetwork( + @Nullable UnderlyingNetworkRecord leftRecord, + @Nullable UnderlyingNetworkRecord rightRecord) { + final Network left = leftRecord == null ? null : leftRecord.network; + final Network right = rightRecord == null ? null : rightRecord.network; + return Objects.equals(left, right); } /** Dumps the state of this record for logging and debugging purposes. */ - void dump( - VcnContext vcnContext, - IndentingPrintWriter pw, - List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, - ParcelUuid subscriptionGroup, - TelephonySubscriptionSnapshot snapshot, - UnderlyingNetworkRecord currentlySelected, - PersistableBundleWrapper carrierConfig) { + void dump(IndentingPrintWriter pw) { pw.println("UnderlyingNetworkRecord:"); pw.increaseIndent(); - pw.println("priorityClass: " + priorityClass); - pw.println("isSelected: " + isSelected); pw.println("mNetwork: " + network); pw.println("mNetworkCapabilities: " + networkCapabilities); pw.println("mLinkProperties: " + linkProperties); @@ -218,29 +130,14 @@ public class UnderlyingNetworkRecord { return mNetworkCapabilities != null && mLinkProperties != null && mWasIsBlockedSet; } - UnderlyingNetworkRecord build( - VcnContext vcnContext, - List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, - ParcelUuid subscriptionGroup, - TelephonySubscriptionSnapshot snapshot, - UnderlyingNetworkRecord currentlySelected, - PersistableBundleWrapper carrierConfig) { + UnderlyingNetworkRecord build() { if (!isValid()) { throw new IllegalArgumentException( "Called build before UnderlyingNetworkRecord was valid"); } return new UnderlyingNetworkRecord( - mNetwork, - mNetworkCapabilities, - mLinkProperties, - mIsBlocked, - vcnContext, - underlyingNetworkTemplates, - subscriptionGroup, - snapshot, - currentlySelected, - carrierConfig); + mNetwork, mNetworkCapabilities, mLinkProperties, mIsBlocked); } } } diff --git a/services/core/java/com/android/server/wearable/OWNERS b/services/core/java/com/android/server/wearable/OWNERS index 073e2d79850b..eca48b742cef 100644 --- a/services/core/java/com/android/server/wearable/OWNERS +++ b/services/core/java/com/android/server/wearable/OWNERS @@ -1,3 +1 @@ -charliewang@google.com -oni@google.com -volnov@google.com
\ No newline at end of file +include /core/java/android/app/wearable/OWNERS
\ No newline at end of file diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 591a55972b81..8cd55c7dd506 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -37,7 +37,6 @@ cc_library_static { "com_android_server_adb_AdbDebuggingManager.cpp", "com_android_server_am_BatteryStatsService.cpp", "com_android_server_biometrics_SurfaceToNativeHandleConverter.cpp", - "com_android_server_BootReceiver.cpp", "com_android_server_ConsumerIrService.cpp", "com_android_server_companion_virtual_InputController.cpp", "com_android_server_devicepolicy_CryptoTestHelper.cpp", @@ -92,16 +91,6 @@ cc_library_static { header_libs: [ "bionic_libc_platform_headers", ], - - static_libs: [ - "libunwindstack", - ], - - whole_static_libs: [ - "libdebuggerd_tombstone_proto_to_text", - ], - - runtime_libs: ["libdexfile"], } cc_defaults { diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS index 33d3686ee9ce..d4f6312d19d9 100644 --- a/services/core/jni/OWNERS +++ b/services/core/jni/OWNERS @@ -32,7 +32,3 @@ per-file com_android_server_companion_virtual_InputController.cpp = file:/servic # Bug component : 158088 = per-file *AnrTimer* per-file *AnrTimer* = file:/PERFORMANCE_OWNERS - -# Bug component : 158088 = per-file com_android_server_utils_AnrTimer*.java -per-file com_android_server_utils_AnrTimer*.java = file:/PERFORMANCE_OWNERS -per-file com_android_server_BootReceiver.cpp = file:/STABILITY_OWNERS diff --git a/services/core/jni/com_android_server_BootReceiver.cpp b/services/core/jni/com_android_server_BootReceiver.cpp deleted file mode 100644 index 3892d284dafb..000000000000 --- a/services/core/jni/com_android_server_BootReceiver.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <libdebuggerd/tombstone.h> -#include <nativehelper/JNIHelp.h> - -#include <sstream> - -#include "jni.h" -#include "tombstone.pb.h" - -namespace android { - -static void writeToString(std::stringstream& ss, const std::string& line, bool should_log) { - ss << line << std::endl; -} - -static jstring com_android_server_BootReceiver_getTombstoneText(JNIEnv* env, jobject, - jbyteArray tombstoneBytes) { - Tombstone tombstone; - tombstone.ParseFromArray(env->GetByteArrayElements(tombstoneBytes, 0), - env->GetArrayLength(tombstoneBytes)); - - std::stringstream tombstoneString; - - tombstone_proto_to_text(tombstone, - std::bind(&writeToString, std::ref(tombstoneString), - std::placeholders::_1, std::placeholders::_2)); - - return env->NewStringUTF(tombstoneString.str().c_str()); -} - -static const JNINativeMethod sMethods[] = { - /* name, signature, funcPtr */ - {"getTombstoneText", "([B)Ljava/lang/String;", - (jstring*)com_android_server_BootReceiver_getTombstoneText}, -}; - -int register_com_android_server_BootReceiver(JNIEnv* env) { - return jniRegisterNativeMethods(env, "com/android/server/BootReceiver", sMethods, - NELEM(sMethods)); -} - -} // namespace android diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index e7de081bd5f8..a87902fe03c5 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -63,7 +63,6 @@ int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env); int register_android_server_sensor_SensorService(JavaVM* vm, JNIEnv* env); int register_android_server_companion_virtual_InputController(JNIEnv* env); int register_android_server_app_GameManagerService(JNIEnv* env); -int register_com_android_server_BootReceiver(JNIEnv* env); int register_com_android_server_wm_TaskFpsCallbackController(JNIEnv* env); int register_com_android_server_display_DisplayControl(JNIEnv* env); int register_com_android_server_SystemClockTime(JNIEnv* env); @@ -123,7 +122,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_android_server_sensor_SensorService(vm, env); register_android_server_companion_virtual_InputController(env); register_android_server_app_GameManagerService(env); - register_com_android_server_BootReceiver(env); register_com_android_server_wm_TaskFpsCallbackController(env); register_com_android_server_display_DisplayControl(env); register_com_android_server_SystemClockTime(env); diff --git a/services/profcollect/Android.bp b/services/profcollect/Android.bp index 2040bb6e544f..fe431f5c8867 100644 --- a/services/profcollect/Android.bp +++ b/services/profcollect/Android.bp @@ -22,24 +22,25 @@ package { } filegroup { - name: "services.profcollect-javasources", - srcs: ["src/**/*.java"], - path: "src", - visibility: ["//frameworks/base/services"], + name: "services.profcollect-javasources", + srcs: ["src/**/*.java"], + path: "src", + visibility: ["//frameworks/base/services"], } filegroup { - name: "services.profcollect-sources", - srcs: [ - ":services.profcollect-javasources", - ":profcollectd_aidl", - ], - visibility: ["//frameworks/base/services:__subpackages__"], + name: "services.profcollect-sources", + srcs: [ + ":services.profcollect-javasources", + ":profcollectd_aidl", + ], + visibility: ["//frameworks/base/services:__subpackages__"], } java_library_static { - name: "services.profcollect", - defaults: ["platform_service_defaults"], - srcs: [":services.profcollect-sources"], - libs: ["services.core"], + name: "services.profcollect", + defaults: ["platform_service_defaults"], + srcs: [":services.profcollect-sources"], + static_libs: ["services.core"], + libs: ["service-art.stubs.system_server"], } diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java index 4007672a0599..582b712ec3fc 100644 --- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java +++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java @@ -41,12 +41,15 @@ import android.util.Log; import com.android.internal.R; import com.android.internal.os.BackgroundThread; import com.android.server.IoThread; +import com.android.server.LocalManagerRegistry; import com.android.server.LocalServices; import com.android.server.SystemService; +import com.android.server.art.ArtManagerLocal; import com.android.server.wm.ActivityMetricsLaunchObserver; import com.android.server.wm.ActivityMetricsLaunchObserverRegistry; import com.android.server.wm.ActivityTaskManagerInternal; +import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; @@ -261,6 +264,7 @@ public final class ProfcollectForwardingService extends SystemService { BackgroundThread.get().getThreadHandler().post( () -> { registerAppLaunchObserver(); + registerDex2oatObserver(); registerOTAObserver(); }); } @@ -304,6 +308,44 @@ public final class ProfcollectForwardingService extends SystemService { } } + private void registerDex2oatObserver() { + ArtManagerLocal aml = LocalManagerRegistry.getManager(ArtManagerLocal.class); + if (aml == null) { + Log.w(LOG_TAG, "Couldn't get ArtManagerLocal"); + return; + } + aml.setBatchDexoptStartCallback(ForkJoinPool.commonPool(), + (snapshot, reason, defaultPackages, builder, passedSignal) -> { + traceOnDex2oatStart(); + }); + } + + private void traceOnDex2oatStart() { + if (mIProfcollect == null) { + return; + } + // Sample for a fraction of dex2oat runs. + final int traceFrequency = + DeviceConfig.getInt(DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT, + "dex2oat_trace_freq", 10); + int randomNum = ThreadLocalRandom.current().nextInt(100); + if (randomNum < traceFrequency) { + if (DEBUG) { + Log.d(LOG_TAG, "Tracing on dex2oat event"); + } + BackgroundThread.get().getThreadHandler().post(() -> { + try { + // Dex2oat could take a while before it starts. Add a short delay before start + // tracing. + Thread.sleep(1000); + mIProfcollect.trace_once("dex2oat"); + } catch (RemoteException | InterruptedException e) { + Log.e(LOG_TAG, "Failed to initiate trace: " + e.getMessage()); + } + }); + } + } + private void registerOTAObserver() { UpdateEngine updateEngine = new UpdateEngine(); updateEngine.bind(new UpdateEngineCallback() { diff --git a/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java index 37ca09d9fa27..b41568298dbc 100644 --- a/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java @@ -47,6 +47,10 @@ import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.UserInfo; import android.hardware.biometrics.BiometricManager; +import android.hardware.biometrics.SensorProperties; +import android.hardware.face.FaceManager; +import android.hardware.face.FaceSensorProperties; +import android.hardware.fingerprint.FingerprintManager; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; @@ -69,6 +73,7 @@ import android.view.WindowManagerGlobal; import androidx.test.core.app.ApplicationProvider; import com.android.internal.widget.LockPatternUtils; +import com.android.internal.widget.LockPatternUtils.StrongAuthTracker; import com.android.modules.utils.testing.ExtendedMockitoRule; import com.android.server.LocalServices; import com.android.server.SystemService; @@ -105,6 +110,8 @@ public class TrustManagerServiceTest { private static final String URI_SCHEME_PACKAGE = "package"; private static final int TEST_USER_ID = 50; + private static final UserInfo TEST_USER = + new UserInfo(TEST_USER_ID, "user", UserInfo.FLAG_FULL); private static final int PARENT_USER_ID = 60; private static final int PROFILE_USER_ID = 70; private static final long[] PARENT_BIOMETRIC_SIDS = new long[] { 600L, 601L }; @@ -117,6 +124,8 @@ public class TrustManagerServiceTest { private @Mock ActivityManager mActivityManager; private @Mock BiometricManager mBiometricManager; private @Mock DevicePolicyManager mDevicePolicyManager; + private @Mock FaceManager mFaceManager; + private @Mock FingerprintManager mFingerprintManager; private @Mock IKeystoreAuthorization mKeystoreAuthorization; private @Mock LockPatternUtils mLockPatternUtils; private @Mock PackageManager mPackageManager; @@ -133,6 +142,9 @@ public class TrustManagerServiceTest { when(mActivityManager.isUserRunning(TEST_USER_ID)).thenReturn(true); doReturn(mock(IActivityManager.class)).when(() -> ActivityManager.getService()); + when(mFaceManager.getSensorProperties()).thenReturn(List.of()); + when(mFingerprintManager.getSensorProperties()).thenReturn(List.of()); + doReturn(mKeystoreAuthorization).when(() -> Authorization.getService()); when(mLockPatternUtils.getDevicePolicyManager()).thenReturn(mDevicePolicyManager); @@ -161,13 +173,16 @@ public class TrustManagerServiceTest { when(mPackageManager.checkPermission(any(), any())).thenReturn( PackageManager.PERMISSION_GRANTED); - when(mUserManager.getAliveUsers()).thenReturn( - List.of(new UserInfo(TEST_USER_ID, "user", UserInfo.FLAG_FULL))); + when(mUserManager.getAliveUsers()).thenReturn(List.of(TEST_USER)); + when(mUserManager.getEnabledProfileIds(TEST_USER_ID)).thenReturn(new int[0]); + when(mUserManager.getUserInfo(TEST_USER_ID)).thenReturn(TEST_USER); when(mWindowManager.isKeyguardLocked()).thenReturn(true); mMockContext.addMockSystemService(ActivityManager.class, mActivityManager); mMockContext.addMockSystemService(BiometricManager.class, mBiometricManager); + mMockContext.addMockSystemService(FaceManager.class, mFaceManager); + mMockContext.addMockSystemService(FingerprintManager.class, mFingerprintManager); mMockContext.setMockPackageManager(mPackageManager); mMockContext.addMockSystemService(UserManager.class, mUserManager); doReturn(mWindowManager).when(() -> WindowManagerGlobal.getWindowManagerService()); @@ -362,9 +377,9 @@ public class TrustManagerServiceTest { when(mWindowManager.isKeyguardLocked()).thenReturn(true); mTrustManager.reportKeyguardShowingChanged(); verify(mKeystoreAuthorization) - .onDeviceLocked(eq(PARENT_USER_ID), eq(PARENT_BIOMETRIC_SIDS)); + .onDeviceLocked(eq(PARENT_USER_ID), eq(PARENT_BIOMETRIC_SIDS), eq(false)); verify(mKeystoreAuthorization) - .onDeviceLocked(eq(PROFILE_USER_ID), eq(PARENT_BIOMETRIC_SIDS)); + .onDeviceLocked(eq(PROFILE_USER_ID), eq(PARENT_BIOMETRIC_SIDS), eq(false)); } // Tests that when the device is locked for a managed profile with a *separate* challenge, the @@ -381,7 +396,188 @@ public class TrustManagerServiceTest { mTrustManager.setDeviceLockedForUser(PROFILE_USER_ID, true); verify(mKeystoreAuthorization) - .onDeviceLocked(eq(PROFILE_USER_ID), eq(PROFILE_BIOMETRIC_SIDS)); + .onDeviceLocked(eq(PROFILE_USER_ID), eq(PROFILE_BIOMETRIC_SIDS), eq(false)); + } + + @Test + @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2) + public void testKeystoreWeakUnlockEnabled_whenWeakFingerprintIsSetupAndAllowed() + throws Exception { + setupStrongAuthTrackerToAllowEverything(); + setupFingerprint(SensorProperties.STRENGTH_WEAK); + verifyWeakUnlockEnabled(); + } + + @Test + @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2) + public void testKeystoreWeakUnlockEnabled_whenWeakFaceIsSetupAndAllowed() throws Exception { + setupStrongAuthTrackerToAllowEverything(); + setupFace(SensorProperties.STRENGTH_WEAK); + verifyWeakUnlockEnabled(); + } + + @Test + @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2) + public void testKeystoreWeakUnlockEnabled_whenConvenienceFingerprintIsSetupAndAllowed() + throws Exception { + setupStrongAuthTrackerToAllowEverything(); + setupFingerprint(SensorProperties.STRENGTH_CONVENIENCE); + verifyWeakUnlockEnabled(); + } + + @Test + @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2) + public void testKeystoreWeakUnlockEnabled_whenConvenienceFaceIsSetupAndAllowed() + throws Exception { + setupStrongAuthTrackerToAllowEverything(); + setupFace(SensorProperties.STRENGTH_CONVENIENCE); + verifyWeakUnlockEnabled(); + } + + @Test + @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2) + public void testKeystoreWeakUnlockDisabled_whenStrongAuthRequired() throws Exception { + setupStrongAuthTracker(StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN, true); + setupFace(SensorProperties.STRENGTH_WEAK); + verifyWeakUnlockDisabled(); + } + + @Test + @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2) + public void testKeystoreWeakUnlockDisabled_whenNonStrongBiometricNotAllowed() throws Exception { + setupStrongAuthTracker(StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED, + /* isNonStrongBiometricAllowed= */ false); + setupFace(SensorProperties.STRENGTH_WEAK); + verifyWeakUnlockDisabled(); + } + + @Test + @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2) + public void testKeystoreWeakUnlockDisabled_whenWeakFingerprintSensorIsPresentButNotEnrolled() + throws Exception { + setupStrongAuthTrackerToAllowEverything(); + setupFingerprint(SensorProperties.STRENGTH_WEAK, /* enrolled= */ false); + verifyWeakUnlockDisabled(); + } + + @Test + @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2) + public void testKeystoreWeakUnlockDisabled_whenWeakFaceSensorIsPresentButNotEnrolled() + throws Exception { + setupStrongAuthTrackerToAllowEverything(); + setupFace(SensorProperties.STRENGTH_WEAK, /* enrolled= */ false); + verifyWeakUnlockDisabled(); + } + + @Test + @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2) + public void + testKeystoreWeakUnlockDisabled_whenWeakFingerprintIsSetupButForbiddenByDevicePolicy() + throws Exception { + setupStrongAuthTrackerToAllowEverything(); + setupFingerprint(SensorProperties.STRENGTH_WEAK); + when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, TEST_USER_ID)) + .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT); + verifyWeakUnlockDisabled(); + } + + @Test + @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2) + public void testKeystoreWeakUnlockDisabled_whenWeakFaceIsSetupButForbiddenByDevicePolicy() + throws Exception { + setupStrongAuthTrackerToAllowEverything(); + setupFace(SensorProperties.STRENGTH_WEAK); + when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, TEST_USER_ID)) + .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FACE); + verifyWeakUnlockDisabled(); + } + + @Test + @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2) + public void testKeystoreWeakUnlockDisabled_whenOnlyStrongFingerprintIsSetup() throws Exception { + setupStrongAuthTrackerToAllowEverything(); + setupFingerprint(SensorProperties.STRENGTH_STRONG); + verifyWeakUnlockDisabled(); + } + + @Test + @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2) + public void testKeystoreWeakUnlockDisabled_whenOnlyStrongFaceIsSetup() throws Exception { + setupStrongAuthTrackerToAllowEverything(); + setupFace(SensorProperties.STRENGTH_STRONG); + verifyWeakUnlockDisabled(); + } + + @Test + @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2) + public void testKeystoreWeakUnlockDisabled_whenNoBiometricsAreSetup() throws Exception { + setupStrongAuthTrackerToAllowEverything(); + verifyWeakUnlockDisabled(); + } + + private void setupStrongAuthTrackerToAllowEverything() throws Exception { + setupStrongAuthTracker(StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED, true); + } + + private void setupStrongAuthTracker(int strongAuthFlags, boolean isNonStrongBiometricAllowed) + throws Exception { + bootService(); + mService.onUserSwitching(null, new SystemService.TargetUser(TEST_USER)); + + ArgumentCaptor<StrongAuthTracker> strongAuthTracker = + ArgumentCaptor.forClass(StrongAuthTracker.class); + verify(mLockPatternUtils).registerStrongAuthTracker(strongAuthTracker.capture()); + strongAuthTracker.getValue().getStub().onStrongAuthRequiredChanged( + strongAuthFlags, TEST_USER_ID); + strongAuthTracker.getValue().getStub().onIsNonStrongBiometricAllowedChanged( + isNonStrongBiometricAllowed, TEST_USER_ID); + mService.waitForIdle(); + } + + private void setupFingerprint(int strength) { + setupFingerprint(strength, /* enrolled= */ true); + } + + private void setupFingerprint(int strength, boolean enrolled) { + int sensorId = 100; + List<SensorProperties.ComponentInfo> componentInfo = List.of(); + SensorProperties sensor = new SensorProperties(sensorId, strength, componentInfo); + when(mFingerprintManager.getSensorProperties()).thenReturn(List.of(sensor)); + when(mFingerprintManager.hasEnrolledTemplates(TEST_USER_ID)).thenReturn(enrolled); + } + + private void setupFace(int strength) { + setupFace(strength, /* enrolled= */ true); + } + + private void setupFace(int strength, boolean enrolled) { + int sensorId = 100; + List<SensorProperties.ComponentInfo> componentInfo = List.of(); + FaceSensorProperties sensor = new FaceSensorProperties( + sensorId, strength, componentInfo, FaceSensorProperties.TYPE_RGB); + when(mFaceManager.getSensorProperties()).thenReturn(List.of(sensor)); + when(mFaceManager.hasEnrolledTemplates(TEST_USER_ID)).thenReturn(enrolled); + } + + private void verifyWeakUnlockEnabled() throws Exception { + verifyWeakUnlockValue(true); + } + + private void verifyWeakUnlockDisabled() throws Exception { + verifyWeakUnlockValue(false); + } + + // Simulates a device unlock and a device lock, then verifies that the expected + // weakUnlockEnabled flag was passed to Keystore's onDeviceLocked method. + private void verifyWeakUnlockValue(boolean expectedWeakUnlockEnabled) throws Exception { + when(mWindowManager.isKeyguardLocked()).thenReturn(false); + mTrustManager.reportKeyguardShowingChanged(); + verify(mKeystoreAuthorization).onDeviceUnlocked(TEST_USER_ID, null); + + when(mWindowManager.isKeyguardLocked()).thenReturn(true); + mTrustManager.reportKeyguardShowingChanged(); + verify(mKeystoreAuthorization).onDeviceLocked(eq(TEST_USER_ID), any(), + eq(expectedWeakUnlockEnabled)); } private void setupMocksForProfile(boolean unifiedChallenge) { diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index 27fd8a66fff1..5cce1c2f6234 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -68,6 +68,7 @@ android_test { "coretests-aidl", "securebox", "flag-junit", + "net_flags_lib", ], libs: [ diff --git a/services/tests/servicestests/src/com/android/server/BootReceiverTest.java b/services/tests/servicestests/src/com/android/server/BootReceiverTest.java deleted file mode 100644 index 523c5c060cf5..000000000000 --- a/services/tests/servicestests/src/com/android/server/BootReceiverTest.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2023 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; - -import static com.google.common.truth.Truth.assertThat; - -import android.test.AndroidTestCase; - -import com.android.server.os.TombstoneProtos; -import com.android.server.os.TombstoneProtos.Tombstone; - -public class BootReceiverTest extends AndroidTestCase { - private static final String TAG = "BootReceiverTest"; - - public void testRemoveMemoryFromTombstone() { - Tombstone tombstoneBase = Tombstone.newBuilder() - .setBuildFingerprint("build_fingerprint") - .setRevision("revision") - .setPid(123) - .setTid(23) - .setUid(34) - .setSelinuxLabel("selinux_label") - .addCommandLine("cmd1") - .addCommandLine("cmd2") - .addCommandLine("cmd3") - .setProcessUptime(300) - .setAbortMessage("abort") - .addCauses(TombstoneProtos.Cause.newBuilder() - .setHumanReadable("cause1") - .setMemoryError(TombstoneProtos.MemoryError.newBuilder() - .setTool(TombstoneProtos.MemoryError.Tool.SCUDO) - .setType(TombstoneProtos.MemoryError.Type.DOUBLE_FREE))) - .addLogBuffers(TombstoneProtos.LogBuffer.newBuilder().setName("name").addLogs( - TombstoneProtos.LogMessage.newBuilder() - .setTimestamp("123") - .setMessage("message"))) - .addOpenFds(TombstoneProtos.FD.newBuilder().setFd(1).setPath("path")) - .build(); - - Tombstone tombstoneWithoutMemory = tombstoneBase.toBuilder() - .putThreads(1, TombstoneProtos.Thread.newBuilder() - .setId(1) - .setName("thread1") - .addRegisters(TombstoneProtos.Register.newBuilder().setName("r1").setU64(1)) - .addRegisters(TombstoneProtos.Register.newBuilder().setName("r2").setU64(2)) - .addBacktraceNote("backtracenote1") - .addUnreadableElfFiles("files1") - .setTaggedAddrCtrl(1) - .setPacEnabledKeys(10) - .build()) - .build(); - - Tombstone tombstoneWithMemory = tombstoneBase.toBuilder() - .addMemoryMappings(TombstoneProtos.MemoryMapping.newBuilder() - .setBeginAddress(1) - .setEndAddress(100) - .setOffset(10) - .setRead(true) - .setWrite(true) - .setExecute(false) - .setMappingName("mapping") - .setBuildId("build") - .setLoadBias(70)) - .putThreads(1, TombstoneProtos.Thread.newBuilder() - .setId(1) - .setName("thread1") - .addRegisters(TombstoneProtos.Register.newBuilder().setName("r1").setU64(1)) - .addRegisters(TombstoneProtos.Register.newBuilder().setName("r2").setU64(2)) - .addBacktraceNote("backtracenote1") - .addUnreadableElfFiles("files1") - .addMemoryDump(TombstoneProtos.MemoryDump.newBuilder() - .setRegisterName("register1") - .setMappingName("mapping") - .setBeginAddress(10)) - .setTaggedAddrCtrl(1) - .setPacEnabledKeys(10) - .build()) - .build(); - - assertThat(BootReceiver.removeMemoryFromTombstone(tombstoneWithMemory)) - .isEqualTo(tombstoneWithoutMemory); - } -} diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java index 5a62d92e8e12..5081198f0058 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java @@ -16,6 +16,8 @@ package com.android.server.locksettings; +import static android.security.Flags.FLAG_REPORT_PRIMARY_AUTH_ATTEMPTS; + import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN; @@ -30,25 +32,30 @@ import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.PropertyInvalidatedCache; +import android.os.IBinder; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.SetFlagsRule; import android.service.gatekeeper.GateKeeperResponse; import android.text.TextUtils; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.widget.ILockSettingsStateListener; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockscreenCredential; import com.android.internal.widget.VerifyCredentialResponse; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -59,6 +66,7 @@ import org.junit.runner.RunWith; @Presubmit @RunWith(AndroidJUnit4.class) public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { + @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @Before public void setUp() { @@ -399,6 +407,60 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { } @Test + public void testVerifyCredential_notifyLockSettingsStateListeners_whenGoodPassword() + throws Exception { + mSetFlagsRule.enableFlags(FLAG_REPORT_PRIMARY_AUTH_ATTEMPTS); + final LockscreenCredential password = newPassword("password"); + setCredential(PRIMARY_USER_ID, password); + final ILockSettingsStateListener listener = mockLockSettingsStateListener(); + mLocalService.registerLockSettingsStateListener(listener); + + assertEquals(VerifyCredentialResponse.RESPONSE_OK, + mService.verifyCredential(password, PRIMARY_USER_ID, 0 /* flags */) + .getResponseCode()); + + verify(listener).onAuthenticationSucceeded(PRIMARY_USER_ID); + } + + @Test + public void testVerifyCredential_notifyLockSettingsStateListeners_whenBadPassword() + throws Exception { + mSetFlagsRule.enableFlags(FLAG_REPORT_PRIMARY_AUTH_ATTEMPTS); + final LockscreenCredential password = newPassword("password"); + setCredential(PRIMARY_USER_ID, password); + final LockscreenCredential badPassword = newPassword("badPassword"); + final ILockSettingsStateListener listener = mockLockSettingsStateListener(); + mLocalService.registerLockSettingsStateListener(listener); + + assertEquals(VerifyCredentialResponse.RESPONSE_ERROR, + mService.verifyCredential(badPassword, PRIMARY_USER_ID, 0 /* flags */) + .getResponseCode()); + + verify(listener).onAuthenticationFailed(PRIMARY_USER_ID); + } + + @Test + public void testLockSettingsStateListener_registeredThenUnregistered() throws Exception { + mSetFlagsRule.enableFlags(FLAG_REPORT_PRIMARY_AUTH_ATTEMPTS); + final LockscreenCredential password = newPassword("password"); + setCredential(PRIMARY_USER_ID, password); + final LockscreenCredential badPassword = newPassword("badPassword"); + final ILockSettingsStateListener listener = mockLockSettingsStateListener(); + + mLocalService.registerLockSettingsStateListener(listener); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, + mService.verifyCredential(password, PRIMARY_USER_ID, 0 /* flags */) + .getResponseCode()); + verify(listener).onAuthenticationSucceeded(PRIMARY_USER_ID); + + mLocalService.unregisterLockSettingsStateListener(listener); + assertEquals(VerifyCredentialResponse.RESPONSE_ERROR, + mService.verifyCredential(badPassword, PRIMARY_USER_ID, 0 /* flags */) + .getResponseCode()); + verify(listener, never()).onAuthenticationFailed(PRIMARY_USER_ID); + } + + @Test public void testSetCredentialNotPossibleInSecureFrpModeDuringSuw() { setUserSetupComplete(false); setSecureFrpMode(true); @@ -537,4 +599,12 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { assertNotEquals(0, mGateKeeperService.getSecureUserId(userId)); } } + + private ILockSettingsStateListener mockLockSettingsStateListener() { + ILockSettingsStateListener listener = mock(ILockSettingsStateListener.Stub.class); + IBinder binder = mock(IBinder.class); + when(binder.isBinderAlive()).thenReturn(true); + when(listener.asBinder()).thenReturn(binder); + return listener; + } } diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java index 13dc12032e7d..d6d2b6d9abd2 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java @@ -16,6 +16,7 @@ package com.android.server.net; +import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND; import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE; import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY; import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE; @@ -327,12 +328,20 @@ public class NetworkManagementServiceTest { isRestrictedForLowPowerStandby.put(INetd.FIREWALL_RULE_DENY, true); expected.put(FIREWALL_CHAIN_LOW_POWER_STANDBY, isRestrictedForLowPowerStandby); + // Background chain + final ArrayMap<Integer, Boolean> isRestrictedInBackground = new ArrayMap<>(); + isRestrictedInBackground.put(NetworkPolicyManager.FIREWALL_RULE_DEFAULT, true); + isRestrictedInBackground.put(INetd.FIREWALL_RULE_ALLOW, false); + isRestrictedInBackground.put(INetd.FIREWALL_RULE_DENY, true); + expected.put(FIREWALL_CHAIN_BACKGROUND, isRestrictedInBackground); + final int[] chains = { FIREWALL_CHAIN_STANDBY, FIREWALL_CHAIN_POWERSAVE, FIREWALL_CHAIN_DOZABLE, FIREWALL_CHAIN_RESTRICTED, - FIREWALL_CHAIN_LOW_POWER_STANDBY + FIREWALL_CHAIN_LOW_POWER_STANDBY, + FIREWALL_CHAIN_BACKGROUND }; final int[] states = { INetd.FIREWALL_RULE_ALLOW, 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 2a764526a436..4451cae8db42 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -26,12 +26,14 @@ import static android.app.ActivityManager.PROCESS_STATE_SERVICE; import static android.app.ActivityManager.PROCESS_STATE_TOP; import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_DATA_SAVER; import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_USER_RESTRICTED; +import static android.net.ConnectivityManager.BLOCKED_REASON_APP_BACKGROUND; import static android.net.ConnectivityManager.BLOCKED_REASON_APP_STANDBY; import static android.net.ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER; import static android.net.ConnectivityManager.BLOCKED_REASON_DOZE; import static android.net.ConnectivityManager.BLOCKED_REASON_LOW_POWER_STANDBY; import static android.net.ConnectivityManager.BLOCKED_REASON_NONE; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; +import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND; import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY; import static android.net.ConnectivityManager.FIREWALL_CHAIN_RESTRICTED; import static android.net.ConnectivityManager.TYPE_MOBILE; @@ -48,8 +50,13 @@ import static android.net.NetworkPolicyManager.ALLOWED_METERED_REASON_SYSTEM; import static android.net.NetworkPolicyManager.ALLOWED_REASON_FOREGROUND; import static android.net.NetworkPolicyManager.ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST; import static android.net.NetworkPolicyManager.ALLOWED_REASON_NONE; +import static android.net.NetworkPolicyManager.ALLOWED_REASON_NOT_IN_BACKGROUND; +import static android.net.NetworkPolicyManager.ALLOWED_REASON_POWER_SAVE_ALLOWLIST; +import static android.net.NetworkPolicyManager.ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST; +import static android.net.NetworkPolicyManager.ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS; import static android.net.NetworkPolicyManager.ALLOWED_REASON_SYSTEM; import static android.net.NetworkPolicyManager.ALLOWED_REASON_TOP; +import static android.net.NetworkPolicyManager.BACKGROUND_THRESHOLD_STATE; import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND; import static android.net.NetworkPolicyManager.POLICY_NONE; @@ -64,6 +71,7 @@ import static android.net.NetworkStats.METERED_YES; import static android.net.NetworkTemplate.MATCH_CARRIER; import static android.net.NetworkTemplate.MATCH_MOBILE; import static android.net.NetworkTemplate.MATCH_WIFI; +import static android.os.PowerExemptionManager.REASON_OTHER; import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED; import static android.telephony.CarrierConfigManager.DATA_CYCLE_THRESHOLD_DISABLED; import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEFAULT; @@ -146,6 +154,8 @@ import android.os.Build; import android.os.Handler; import android.os.INetworkManagementService; import android.os.PersistableBundle; +import android.os.PowerExemptionManager; +import android.os.PowerManager; import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.RemoteException; @@ -153,6 +163,9 @@ import android.os.SimpleClock; import android.os.UserHandle; import android.os.UserManager; import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; @@ -169,6 +182,7 @@ import android.util.Pair; import android.util.Range; import android.util.RecurrenceRule; import android.util.SparseArray; +import android.util.SparseIntArray; import androidx.test.InstrumentationRegistry; import androidx.test.filters.FlakyTest; @@ -243,6 +257,9 @@ import java.util.stream.Collectors; public class NetworkPolicyManagerServiceTest { private static final String TAG = "NetworkPolicyManagerServiceTest"; + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + private static final long TEST_START = 1194220800000L; private static final String TEST_IFACE = "test0"; private static final String TEST_WIFI_NETWORK_KEY = "TestWifiNetworkKey"; @@ -285,6 +302,7 @@ public class NetworkPolicyManagerServiceTest { private @Mock TelephonyManager mTelephonyManager; private @Mock UserManager mUserManager; private @Mock NetworkStatsManager mStatsManager; + private @Mock PowerExemptionManager mPowerExemptionManager; private TestDependencies mDeps; private ArgumentCaptor<ConnectivityManager.NetworkCallback> mNetworkCallbackCaptor = @@ -302,6 +320,7 @@ public class NetworkPolicyManagerServiceTest { private NetworkPolicyManagerService mService; private final ArraySet<BroadcastReceiver> mRegisteredReceivers = new ArraySet<>(); + private BroadcastReceiver mPowerAllowlistReceiver; /** * In some of the tests while initializing NetworkPolicyManagerService, @@ -446,6 +465,7 @@ public class NetworkPolicyManagerServiceTest { @Before public void callSystemReady() throws Exception { MockitoAnnotations.initMocks(this); + when(mPowerExemptionManager.getAllowListedAppIds(anyBoolean())).thenReturn(new int[0]); final Context context = InstrumentationRegistry.getContext(); @@ -482,6 +502,8 @@ public class NetworkPolicyManagerServiceTest { return mUserManager; case Context.NETWORK_STATS_SERVICE: return mStatsManager; + case Context.POWER_EXEMPTION_SERVICE: + return mPowerExemptionManager; default: return super.getSystemService(name); } @@ -495,6 +517,9 @@ public class NetworkPolicyManagerServiceTest { @Override public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler) { + if (filter.hasAction(PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED)) { + mPowerAllowlistReceiver = receiver; + } mRegisteredReceivers.add(receiver); return super.registerReceiver(receiver, filter, broadcastPermission, scheduler); } @@ -2066,6 +2091,12 @@ public class NetworkPolicyManagerServiceTest { expectHasUseRestrictedNetworksPermission(UID_A, true); expectHasUseRestrictedNetworksPermission(UID_B, false); + // Set low enough proc-states to ensure these uids are allowed in the background chain. + // To maintain clean separation between separate firewall chains, the tests could + // check for the specific blockedReasons in the uidBlockedState. + callAndWaitOnUidStateChanged(UID_A, BACKGROUND_THRESHOLD_STATE - 1, 21); + callAndWaitOnUidStateChanged(UID_B, BACKGROUND_THRESHOLD_STATE - 1, 21); + Map<Integer, Integer> firewallUidRules = new ArrayMap<>(); doAnswer(arg -> { int[] uids = arg.getArgument(1); @@ -2113,7 +2144,111 @@ public class NetworkPolicyManagerServiceTest { } @Test + @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) + public void testBackgroundChainEnabled() throws Exception { + verify(mNetworkManager).setFirewallChainEnabled(FIREWALL_CHAIN_BACKGROUND, true); + } + + + @Test + @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) + public void testBackgroundChainOnProcStateChange() throws Exception { + // initialization calls setFirewallChainEnabled, so we want to reset the invocations. + clearInvocations(mNetworkManager); + + mService.mBackgroundRestrictionDelayMs = 500; // To avoid waiting too long in tests. + + // The app will be blocked when there is no prior proc-state. + assertTrue(mService.isUidNetworkingBlocked(UID_A, false)); + + int procStateSeq = 23; + callAndWaitOnUidStateChanged(UID_A, BACKGROUND_THRESHOLD_STATE - 1, procStateSeq++); + + verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, UID_A, + FIREWALL_RULE_ALLOW); + assertFalse(mService.isUidNetworkingBlocked(UID_A, false)); + + callAndWaitOnUidStateChanged(UID_A, BACKGROUND_THRESHOLD_STATE + 1, procStateSeq++); + + // The app should be blocked after a delay. Posting a message just after the delay and + // waiting for it to complete to ensure that the blocking code has executed. + waitForDelayedMessageOnHandler(mService.mBackgroundRestrictionDelayMs + 1); + + verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, UID_A, + FIREWALL_RULE_DEFAULT); + assertTrue(mService.isUidNetworkingBlocked(UID_A, false)); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) + public void testBackgroundChainOnAllowlistChange() throws Exception { + // initialization calls setFirewallChainEnabled, so we want to reset the invocations. + clearInvocations(mNetworkManager); + + // The apps will be blocked when there is no prior proc-state. + assertTrue(mService.isUidNetworkingBlocked(UID_A, false)); + assertTrue(mService.isUidNetworkingBlocked(UID_B, false)); + + final int procStateSeq = 29; + callAndWaitOnUidStateChanged(UID_A, BACKGROUND_THRESHOLD_STATE + 1, procStateSeq); + assertTrue(mService.isUidNetworkingBlocked(UID_A, false)); + + when(mPowerExemptionManager.getAllowListedAppIds(anyBoolean())) + .thenReturn(new int[]{APP_ID_A, APP_ID_B}); + final SparseIntArray firewallUidRules = new SparseIntArray(); + doAnswer(arg -> { + final int[] uids = arg.getArgument(1); + final int[] rules = arg.getArgument(2); + assertTrue(uids.length == rules.length); + + for (int i = 0; i < uids.length; ++i) { + firewallUidRules.put(uids[i], rules[i]); + } + return null; + }).when(mNetworkManager).setFirewallUidRules(eq(FIREWALL_CHAIN_BACKGROUND), + any(int[].class), any(int[].class)); + + mPowerAllowlistReceiver.onReceive(mServiceContext, null); + + assertEquals(FIREWALL_RULE_ALLOW, firewallUidRules.get(UID_A, -1)); + assertEquals(FIREWALL_RULE_ALLOW, firewallUidRules.get(UID_B, -1)); + + assertFalse(mService.isUidNetworkingBlocked(UID_A, false)); + assertFalse(mService.isUidNetworkingBlocked(UID_B, false)); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) + public void testBackgroundChainOnTempAllowlistChange() throws Exception { + // initialization calls setFirewallChainEnabled, so we want to reset the invocations. + clearInvocations(mNetworkManager); + + // The app will be blocked as is no prior proc-state. + assertTrue(mService.isUidNetworkingBlocked(UID_A, false)); + + final int procStateSeq = 19; + callAndWaitOnUidStateChanged(UID_A, BACKGROUND_THRESHOLD_STATE + 1, procStateSeq); + assertTrue(mService.isUidNetworkingBlocked(UID_A, false)); + + final NetworkPolicyManagerInternal internal = LocalServices.getService( + NetworkPolicyManagerInternal.class); + + internal.onTempPowerSaveWhitelistChange(APP_ID_A, true, REASON_OTHER, "testing"); + + verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, UID_A, + FIREWALL_RULE_ALLOW); + assertFalse(mService.isUidNetworkingBlocked(UID_A, false)); + + internal.onTempPowerSaveWhitelistChange(APP_ID_A, false, REASON_OTHER, "testing"); + + verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, UID_A, + FIREWALL_RULE_DEFAULT); + assertTrue(mService.isUidNetworkingBlocked(UID_A, false)); + } + + @Test public void testLowPowerStandbyAllowlist() throws Exception { + // Chain background is also enabled but these procstates are important enough to be exempt. callAndWaitOnUidStateChanged(UID_A, PROCESS_STATE_TOP, 0); callAndWaitOnUidStateChanged(UID_B, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0); callAndWaitOnUidStateChanged(UID_C, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0); @@ -2200,7 +2335,21 @@ public class NetworkPolicyManagerServiceTest { ALLOWED_REASON_TOP), BLOCKED_REASON_NONE); effectiveBlockedReasons.put(Pair.create(BLOCKED_REASON_LOW_POWER_STANDBY, ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST), BLOCKED_REASON_NONE); - // TODO: test more combinations of blocked reasons. + + effectiveBlockedReasons.put(Pair.create(BLOCKED_REASON_APP_BACKGROUND, + ALLOWED_REASON_NOT_IN_BACKGROUND), BLOCKED_REASON_NONE); + effectiveBlockedReasons.put(Pair.create(BLOCKED_REASON_APP_BACKGROUND + | BLOCKED_REASON_BATTERY_SAVER, ALLOWED_REASON_NOT_IN_BACKGROUND), + BLOCKED_REASON_BATTERY_SAVER); + effectiveBlockedReasons.put(Pair.create(BLOCKED_REASON_APP_BACKGROUND + | BLOCKED_REASON_DOZE, ALLOWED_REASON_NOT_IN_BACKGROUND), + BLOCKED_REASON_DOZE); + effectiveBlockedReasons.put(Pair.create(BLOCKED_REASON_APP_BACKGROUND, + ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS), BLOCKED_REASON_APP_BACKGROUND); + effectiveBlockedReasons.put(Pair.create(BLOCKED_REASON_APP_BACKGROUND, + ALLOWED_REASON_POWER_SAVE_ALLOWLIST), BLOCKED_REASON_NONE); + effectiveBlockedReasons.put(Pair.create(BLOCKED_REASON_APP_BACKGROUND, + ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST), BLOCKED_REASON_NONE); for (Map.Entry<Pair<Integer, Integer>, Integer> test : effectiveBlockedReasons.entrySet()) { final int expectedEffectiveBlockedReasons = test.getValue(); @@ -2529,7 +2678,6 @@ public class NetworkPolicyManagerServiceTest { private FutureIntent mRestrictBackgroundChanged; private void postMsgAndWaitForCompletion() throws InterruptedException { - final Handler handler = mService.getHandlerForTesting(); final CountDownLatch latch = new CountDownLatch(1); mService.getHandlerForTesting().post(latch::countDown); if (!latch.await(5, TimeUnit.SECONDS)) { @@ -2537,6 +2685,14 @@ public class NetworkPolicyManagerServiceTest { } } + private void waitForDelayedMessageOnHandler(long delayMs) throws InterruptedException { + final CountDownLatch latch = new CountDownLatch(1); + mService.getHandlerForTesting().postDelayed(latch::countDown, delayMs); + if (!latch.await(delayMs + 5_000, TimeUnit.MILLISECONDS)) { + fail("Timed out waiting for delayed msg to be handled"); + } + } + private void setSubscriptionPlans(int subId, SubscriptionPlan[] plans, String callingPackage) throws InterruptedException { mService.setSubscriptionPlans(subId, plans, 0, callingPackage); diff --git a/services/usage/java/com/android/server/usage/BroadcastResponseStatsLogger.java b/services/usage/java/com/android/server/usage/BroadcastResponseStatsLogger.java index 336bfdd0fb14..a8fd6f29f862 100644 --- a/services/usage/java/com/android/server/usage/BroadcastResponseStatsLogger.java +++ b/services/usage/java/com/android/server/usage/BroadcastResponseStatsLogger.java @@ -35,11 +35,13 @@ import android.util.Slog; import android.util.TimeUtils; import com.android.internal.annotations.GuardedBy; -import com.android.internal.annotations.Keep; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.RingBuffer; import com.android.server.usage.BroadcastResponseStatsTracker.NotificationEventType; +import java.util.function.IntFunction; +import java.util.function.Supplier; + public class BroadcastResponseStatsLogger { private static final int MAX_LOG_SIZE = @@ -49,10 +51,10 @@ public class BroadcastResponseStatsLogger { @GuardedBy("mLock") private final LogBuffer mBroadcastEventsBuffer = new LogBuffer( - BroadcastEvent.class, MAX_LOG_SIZE); + BroadcastEvent::new, BroadcastEvent[]::new, MAX_LOG_SIZE); @GuardedBy("mLock") private final LogBuffer mNotificationEventsBuffer = new LogBuffer( - NotificationEvent.class, MAX_LOG_SIZE); + NotificationEvent::new, NotificationEvent[]::new, MAX_LOG_SIZE); void logBroadcastDispatchEvent(int sourceUid, @NonNull String targetPackage, UserHandle targetUser, long idForResponseEvent, @@ -96,8 +98,8 @@ public class BroadcastResponseStatsLogger { private static final class LogBuffer<T extends Data> extends RingBuffer<T> { - LogBuffer(Class<T> classType, int capacity) { - super(classType, capacity); + LogBuffer(Supplier<T> newItem, IntFunction<T[]> newBacking, int capacity) { + super(newItem, newBacking, capacity); } void logBroadcastDispatchEvent(int sourceUid, @NonNull String targetPackage, @@ -179,8 +181,7 @@ public class BroadcastResponseStatsLogger { } } - @Keep - public static final class BroadcastEvent implements Data { + private static final class BroadcastEvent implements Data { public int sourceUid; public int targetUserId; public int targetUidProcessState; @@ -200,8 +201,7 @@ public class BroadcastResponseStatsLogger { } } - @Keep - public static final class NotificationEvent implements Data { + private static final class NotificationEvent implements Data { public int type; public String packageName; public int userId; @@ -218,7 +218,7 @@ public class BroadcastResponseStatsLogger { } } - public interface Data { + private interface Data { void reset(); } } diff --git a/test-base/Android.bp b/test-base/Android.bp index 527159a78ebf..70a95400bd9e 100644 --- a/test-base/Android.bp +++ b/test-base/Android.bp @@ -120,12 +120,13 @@ filegroup { path: "src", } -// Make the current.txt available for use by the cts/tests/signature tests. +// Make the current.txt available for use by the cts/tests/signature and /vendor tests. // ======================================================================== filegroup { name: "android-test-base-current.txt", visibility: [ "//cts/tests/signature/api", + "//vendor:__subpackages__", ], srcs: [ "api/current.txt", diff --git a/test-mock/Android.bp b/test-mock/Android.bp index 22320fd53631..7c4d12e338a5 100644 --- a/test-mock/Android.bp +++ b/test-mock/Android.bp @@ -54,12 +54,13 @@ java_sdk_library { dist_group: "android", } -// Make the current.txt available for use by the cts/tests/signature tests. +// Make the current.txt available for use by the cts/tests/signature and /vendor tests. // ======================================================================== filegroup { name: "android-test-mock-current.txt", visibility: [ "//cts/tests/signature/api", + "//vendor:__subpackages__", ], srcs: [ "api/current.txt", diff --git a/test-runner/Android.bp b/test-runner/Android.bp index 13a5dac9eb38..21e09d3221ce 100644 --- a/test-runner/Android.bp +++ b/test-runner/Android.bp @@ -79,12 +79,13 @@ java_library { ], } -// Make the current.txt available for use by the cts/tests/signature tests. +// Make the current.txt available for use by the cts/tests/signature and /vendor tests. // ======================================================================== filegroup { name: "android-test-runner-current.txt", visibility: [ "//cts/tests/signature/api", + "//vendor:__subpackages__", ], srcs: [ "api/current.txt", diff --git a/tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/Android.mk b/tests/Camera2Tests/CameraToo/tests/Android.bp index c084849a9d28..8339a2c8ab25 100644 --- a/tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/Android.mk +++ b/tests/Camera2Tests/CameraToo/tests/Android.bp @@ -1,5 +1,4 @@ -// -// Copyright (C) 2019 The Android Open Source Project +// Copyright (C) 2014 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. @@ -12,20 +11,24 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -// -LOCAL_PATH := $(call my-dir) +package { + // See: http://go/android-license-faq + default_applicable_licenses: [ + "frameworks_base_license", + ], +} -include $(CLEAR_VARS) -LOCAL_USE_AAPT2 := true -LOCAL_AAPT_NAMESPACES := true -LOCAL_MODULE := AaptTestMergeOnly_LeafLib -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE -LOCAL_SDK_VERSION := current -LOCAL_MODULE_TAGS := tests -LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res -LOCAL_MIN_SDK_VERSION := 21 -LOCAL_AAPT_FLAGS := --merge-only -include $(BUILD_STATIC_JAVA_LIBRARY) +android_test { + name: "CameraTooTests", + instrumentation_for: "CameraToo", + srcs: ["src/**/*.java"], + sdk_version: "current", + static_libs: [ + "androidx.test.rules", + "mockito-target-minus-junit4", + ], + data: [ + ":CameraToo", + ], +} diff --git a/tests/Camera2Tests/CameraToo/tests/Android.mk b/tests/Camera2Tests/CameraToo/tests/Android.mk deleted file mode 100644 index dfa64f1feade..000000000000 --- a/tests/Camera2Tests/CameraToo/tests/Android.mk +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (C) 2014 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests -LOCAL_PACKAGE_NAME := CameraTooTests -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../NOTICE -LOCAL_INSTRUMENTATION_FOR := CameraToo -LOCAL_SDK_VERSION := current -LOCAL_SRC_FILES := $(call all-java-files-under,src) -LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules mockito-target-minus-junit4 - -include $(BUILD_PACKAGE) diff --git a/tests/Camera2Tests/CameraToo/tests/AndroidTest.xml b/tests/Camera2Tests/CameraToo/tests/AndroidTest.xml new file mode 100644 index 000000000000..884c095fef49 --- /dev/null +++ b/tests/Camera2Tests/CameraToo/tests/AndroidTest.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2023 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="Runs CameraToo tests."> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-instrumentation" /> + + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="test-file-name" value="CameraTooTests.apk" /> + <option name="test-file-name" value="CameraToo.apk" /> + </target_preparer> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.example.android.camera2.cameratoo.tests" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + </test> +</configuration> diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java index f84616426389..20b7f1f14691 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java @@ -269,6 +269,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection @Test public void testCreatedTransformsAreApplied() throws Exception { verifyVcnTransformsApplied(mGatewayConnection, false /* expectForwardTransform */); + verify(mUnderlyingNetworkController).updateInboundTransform(any(), any()); } @Test @@ -327,6 +328,8 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(direction), anyInt(), any()); } + verify(mUnderlyingNetworkController).updateInboundTransform(any(), any()); + assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState()); final List<ChildSaProposal> saProposals = diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java index 692c8a8f0898..49665f7a3304 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java @@ -27,15 +27,18 @@ import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.net.vcn.VcnGatewayConnectionConfig.VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY; import static com.android.server.vcn.VcnGatewayConnection.DUMMY_ADDR; +import static com.android.server.vcn.VcnGatewayConnection.SAFEMODE_TIMEOUT_SECONDS; import static com.android.server.vcn.VcnGatewayConnection.VcnChildSessionConfiguration; import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession; import static com.android.server.vcn.VcnGatewayConnection.VcnNetworkAgent; +import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.CALLS_REAL_METHODS; @@ -55,6 +58,7 @@ import android.net.NetworkRequest; import android.net.TelephonyNetworkSpecifier; import android.net.vcn.VcnGatewayConnectionConfig; import android.net.vcn.VcnGatewayConnectionConfigTest; +import android.net.vcn.VcnManager; import android.net.vcn.VcnTransportInfo; import android.net.wifi.WifiInfo; import android.os.ParcelUuid; @@ -81,6 +85,7 @@ import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; /** Tests for TelephonySubscriptionTracker */ @RunWith(AndroidJUnit4.class) @@ -352,4 +357,71 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase { any(Executor.class), any(ConnectivityDiagnosticsCallback.class)); } + + private void verifyGetSafeModeTimeoutMs( + boolean isInTestMode, + boolean isConfigTimeoutSupported, + PersistableBundleWrapper carrierConfig, + long expectedTimeoutMs) + throws Exception { + doReturn(isInTestMode).when(mVcnContext).isInTestMode(); + doReturn(isConfigTimeoutSupported).when(mVcnContext).isFlagSafeModeTimeoutConfigEnabled(); + + final TelephonySubscriptionSnapshot snapshot = mock(TelephonySubscriptionSnapshot.class); + doReturn(carrierConfig).when(snapshot).getCarrierConfigForSubGrp(TEST_SUB_GRP); + + final long result = + VcnGatewayConnection.getSafeModeTimeoutMs(mVcnContext, snapshot, TEST_SUB_GRP); + + assertEquals(expectedTimeoutMs, result); + } + + @Test + public void testGetSafeModeTimeoutMs_configTimeoutUnsupported() throws Exception { + verifyGetSafeModeTimeoutMs( + false /* isInTestMode */, + false /* isConfigTimeoutSupported */, + null /* carrierConfig */, + TimeUnit.SECONDS.toMillis(SAFEMODE_TIMEOUT_SECONDS)); + } + + @Test + public void testGetSafeModeTimeoutMs_configTimeoutSupported() throws Exception { + final int carrierConfigTimeoutSeconds = 20; + final PersistableBundleWrapper carrierConfig = mock(PersistableBundleWrapper.class); + doReturn(carrierConfigTimeoutSeconds) + .when(carrierConfig) + .getInt(eq(VcnManager.VCN_SAFE_MODE_TIMEOUT_SECONDS_KEY), anyInt()); + + verifyGetSafeModeTimeoutMs( + false /* isInTestMode */, + true /* isConfigTimeoutSupported */, + carrierConfig, + TimeUnit.SECONDS.toMillis(carrierConfigTimeoutSeconds)); + } + + @Test + public void testGetSafeModeTimeoutMs_configTimeoutSupported_carrierConfigNull() + throws Exception { + verifyGetSafeModeTimeoutMs( + false /* isInTestMode */, + true /* isConfigTimeoutSupported */, + null /* carrierConfig */, + TimeUnit.SECONDS.toMillis(SAFEMODE_TIMEOUT_SECONDS)); + } + + @Test + public void testGetSafeModeTimeoutMs_configTimeoutOverrideTestModeDefault() throws Exception { + final int carrierConfigTimeoutSeconds = 20; + final PersistableBundleWrapper carrierConfig = mock(PersistableBundleWrapper.class); + doReturn(carrierConfigTimeoutSeconds) + .when(carrierConfig) + .getInt(eq(VcnManager.VCN_SAFE_MODE_TIMEOUT_SECONDS_KEY), anyInt()); + + verifyGetSafeModeTimeoutMs( + true /* isInTestMode */, + true /* isConfigTimeoutSupported */, + carrierConfig, + TimeUnit.SECONDS.toMillis(carrierConfigTimeoutSeconds)); + } } diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java index edced87427c8..e29e4621d571 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java @@ -67,6 +67,8 @@ 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 com.android.server.vcn.VcnGatewayConnection.VcnIkeSession; +import com.android.server.vcn.VcnGatewayConnection.VcnNetworkAgent; import com.android.server.vcn.VcnGatewayConnection.VcnWakeLock; import com.android.server.vcn.routeselection.UnderlyingNetworkController; import com.android.server.vcn.routeselection.UnderlyingNetworkRecord; @@ -118,13 +120,7 @@ public class VcnGatewayConnectionTestBase { NetworkCapabilities networkCapabilities, LinkProperties linkProperties, boolean isBlocked) { - return new UnderlyingNetworkRecord( - network, - networkCapabilities, - linkProperties, - isBlocked, - false /* isSelected */, - 0 /* priorityClass */); + return new UnderlyingNetworkRecord(network, networkCapabilities, linkProperties, isBlocked); } protected static final String TEST_TCP_BUFFER_SIZES_1 = "1,2,3,4"; @@ -226,6 +222,9 @@ public class VcnGatewayConnectionTestBase { doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper(); doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider(); doReturn(mFeatureFlags).when(mVcnContext).getFeatureFlags(); + doReturn(true).when(mVcnContext).isFlagSafeModeTimeoutConfigEnabled(); + doReturn(true).when(mVcnContext).isFlagIpSecTransformStateEnabled(); + doReturn(true).when(mVcnContext).isFlagNetworkMetricMonitorEnabled(); doReturn(mUnderlyingNetworkController) .when(mDeps) diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java new file mode 100644 index 000000000000..9daba6a79a27 --- /dev/null +++ b/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java @@ -0,0 +1,419 @@ +/* + * Copyright (C) 2023 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.routeselection; + +import static android.net.vcn.VcnManager.VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY; +import static android.net.vcn.VcnManager.VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY; + +import static com.android.server.vcn.routeselection.IpSecPacketLossDetector.PACKET_LOSS_UNAVALAIBLE; +import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.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 static org.mockito.Mockito.when; + +import android.content.BroadcastReceiver; +import android.content.Intent; +import android.net.IpSecTransformState; +import android.os.OutcomeReceiver; +import android.os.PowerManager; + +import com.android.server.vcn.routeselection.IpSecPacketLossDetector.PacketLossCalculator; +import com.android.server.vcn.routeselection.NetworkMetricMonitor.IpSecTransformWrapper; +import com.android.server.vcn.routeselection.NetworkMetricMonitor.NetworkMetricMonitorCallback; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.Spy; + +import java.util.Arrays; +import java.util.BitSet; +import java.util.concurrent.TimeUnit; + +public class IpSecPacketLossDetectorTest extends NetworkEvaluationTestBase { + private static final String TAG = IpSecPacketLossDetectorTest.class.getSimpleName(); + + private static final int REPLAY_BITMAP_LEN_BYTE = 512; + private static final int REPLAY_BITMAP_LEN_BIT = REPLAY_BITMAP_LEN_BYTE * 8; + private static final int IPSEC_PACKET_LOSS_PERCENT_THRESHOLD = 5; + private static final long POLL_IPSEC_STATE_INTERVAL_MS = TimeUnit.SECONDS.toMillis(30L); + + @Mock private IpSecTransformWrapper mIpSecTransform; + @Mock private NetworkMetricMonitorCallback mMetricMonitorCallback; + @Mock private PersistableBundleWrapper mCarrierConfig; + @Mock private IpSecPacketLossDetector.Dependencies mDependencies; + @Spy private PacketLossCalculator mPacketLossCalculator = new PacketLossCalculator(); + + @Captor private ArgumentCaptor<OutcomeReceiver> mTransformStateReceiverCaptor; + @Captor private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor; + + private IpSecPacketLossDetector mIpSecPacketLossDetector; + private IpSecTransformState mTransformStateInitial; + + @Before + public void setUp() throws Exception { + super.setUp(); + mTransformStateInitial = newTransformState(0, 0, newReplayBitmap(0)); + + when(mCarrierConfig.getInt( + eq(VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY), anyInt())) + .thenReturn((int) TimeUnit.MILLISECONDS.toSeconds(POLL_IPSEC_STATE_INTERVAL_MS)); + when(mCarrierConfig.getInt( + eq(VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY), + anyInt())) + .thenReturn(IPSEC_PACKET_LOSS_PERCENT_THRESHOLD); + + when(mDependencies.getPacketLossCalculator()).thenReturn(mPacketLossCalculator); + + mIpSecPacketLossDetector = + new IpSecPacketLossDetector( + mVcnContext, + mNetwork, + mCarrierConfig, + mMetricMonitorCallback, + mDependencies); + } + + private static IpSecTransformState newTransformState( + long rxSeqNo, long packtCount, byte[] replayBitmap) { + return new IpSecTransformState.Builder() + .setRxHighestSequenceNumber(rxSeqNo) + .setPacketCount(packtCount) + .setReplayBitmap(replayBitmap) + .build(); + } + + private static byte[] newReplayBitmap(int receivedPktCnt) { + final BitSet bitSet = new BitSet(REPLAY_BITMAP_LEN_BIT); + for (int i = 0; i < receivedPktCnt; i++) { + bitSet.set(i); + } + return Arrays.copyOf(bitSet.toByteArray(), REPLAY_BITMAP_LEN_BYTE); + } + + private void verifyStopped() { + assertFalse(mIpSecPacketLossDetector.isStarted()); + assertFalse(mIpSecPacketLossDetector.isValidationFailed()); + assertNull(mIpSecPacketLossDetector.getLastTransformState()); + + // No event scheduled + mTestLooper.moveTimeForward(POLL_IPSEC_STATE_INTERVAL_MS); + assertNull(mTestLooper.nextMessage()); + } + + @Test + public void testInitialization() throws Exception { + assertFalse(mIpSecPacketLossDetector.isSelectedUnderlyingNetwork()); + verifyStopped(); + } + + private OutcomeReceiver<IpSecTransformState, RuntimeException> + startMonitorAndCaptureStateReceiver() { + mIpSecPacketLossDetector.setIsSelectedUnderlyingNetwork(true /* setIsSelected */); + mIpSecPacketLossDetector.setInboundTransformInternal(mIpSecTransform); + + // Trigger the runnable + mTestLooper.dispatchAll(); + + verify(mIpSecTransform) + .getIpSecTransformState(any(), mTransformStateReceiverCaptor.capture()); + return mTransformStateReceiverCaptor.getValue(); + } + + @Test + public void testStartMonitor() throws Exception { + final OutcomeReceiver<IpSecTransformState, RuntimeException> xfrmStateReceiver = + startMonitorAndCaptureStateReceiver(); + + assertTrue(mIpSecPacketLossDetector.isStarted()); + assertFalse(mIpSecPacketLossDetector.isValidationFailed()); + assertTrue(mIpSecPacketLossDetector.isSelectedUnderlyingNetwork()); + assertEquals(mIpSecTransform, mIpSecPacketLossDetector.getInboundTransformInternal()); + + // Mock receiving a state + xfrmStateReceiver.onResult(mTransformStateInitial); + + // Verify the first polled state is stored + assertEquals(mTransformStateInitial, mIpSecPacketLossDetector.getLastTransformState()); + verify(mPacketLossCalculator, never()) + .getPacketLossRatePercentage(any(), any(), anyString()); + + // Verify next poll is scheduled + assertNull(mTestLooper.nextMessage()); + mTestLooper.moveTimeForward(POLL_IPSEC_STATE_INTERVAL_MS); + assertNotNull(mTestLooper.nextMessage()); + } + + @Test + public void testStartedMonitor_enterDozeMoze() throws Exception { + final OutcomeReceiver<IpSecTransformState, RuntimeException> xfrmStateReceiver = + startMonitorAndCaptureStateReceiver(); + + // Mock receiving a state + xfrmStateReceiver.onResult(mTransformStateInitial); + assertEquals(mTransformStateInitial, mIpSecPacketLossDetector.getLastTransformState()); + + // Mock entering doze mode + final Intent intent = mock(Intent.class); + when(intent.getAction()).thenReturn(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); + when(mPowerManagerService.isDeviceIdleMode()).thenReturn(true); + + verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), any(), any(), any()); + final BroadcastReceiver broadcastReceiver = mBroadcastReceiverCaptor.getValue(); + broadcastReceiver.onReceive(mContext, intent); + + assertNull(mIpSecPacketLossDetector.getLastTransformState()); + } + + @Test + public void testStartedMonitor_updateInboundTransform() throws Exception { + final OutcomeReceiver<IpSecTransformState, RuntimeException> xfrmStateReceiver = + startMonitorAndCaptureStateReceiver(); + + // Mock receiving a state + xfrmStateReceiver.onResult(mTransformStateInitial); + assertEquals(mTransformStateInitial, mIpSecPacketLossDetector.getLastTransformState()); + + // Update the inbound transform + final IpSecTransformWrapper newTransform = mock(IpSecTransformWrapper.class); + mIpSecPacketLossDetector.setInboundTransformInternal(newTransform); + + // Verifications + assertNull(mIpSecPacketLossDetector.getLastTransformState()); + mTestLooper.moveTimeForward(POLL_IPSEC_STATE_INTERVAL_MS); + mTestLooper.dispatchAll(); + verify(newTransform).getIpSecTransformState(any(), any()); + } + + @Test + public void testStartedMonitor_updateCarrierConfig() throws Exception { + startMonitorAndCaptureStateReceiver(); + + final int additionalPollIntervalMs = (int) TimeUnit.SECONDS.toMillis(10L); + when(mCarrierConfig.getInt( + eq(VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY), anyInt())) + .thenReturn( + (int) + TimeUnit.MILLISECONDS.toSeconds( + POLL_IPSEC_STATE_INTERVAL_MS + additionalPollIntervalMs)); + mIpSecPacketLossDetector.setCarrierConfig(mCarrierConfig); + mTestLooper.dispatchAll(); + + // The already scheduled event is still fired with the old timeout + mTestLooper.moveTimeForward(POLL_IPSEC_STATE_INTERVAL_MS); + mTestLooper.dispatchAll(); + + // The next scheduled event will take 10 more seconds to fire + mTestLooper.moveTimeForward(POLL_IPSEC_STATE_INTERVAL_MS); + assertNull(mTestLooper.nextMessage()); + mTestLooper.moveTimeForward(additionalPollIntervalMs); + assertNotNull(mTestLooper.nextMessage()); + } + + @Test + public void testStopMonitor() throws Exception { + mIpSecPacketLossDetector.setIsSelectedUnderlyingNetwork(true /* setIsSelected */); + mIpSecPacketLossDetector.setInboundTransformInternal(mIpSecTransform); + + assertTrue(mIpSecPacketLossDetector.isStarted()); + assertNotNull(mTestLooper.nextMessage()); + + // Unselect the monitor + mIpSecPacketLossDetector.setIsSelectedUnderlyingNetwork(false /* setIsSelected */); + verifyStopped(); + } + + @Test + public void testClose() throws Exception { + mIpSecPacketLossDetector.setIsSelectedUnderlyingNetwork(true /* setIsSelected */); + mIpSecPacketLossDetector.setInboundTransformInternal(mIpSecTransform); + + assertTrue(mIpSecPacketLossDetector.isStarted()); + assertNotNull(mTestLooper.nextMessage()); + + // Stop the monitor + mIpSecPacketLossDetector.close(); + verifyStopped(); + verify(mIpSecTransform).close(); + } + + @Test + public void testTransformStateReceiverOnResultWhenStopped() throws Exception { + final OutcomeReceiver<IpSecTransformState, RuntimeException> xfrmStateReceiver = + startMonitorAndCaptureStateReceiver(); + xfrmStateReceiver.onResult(mTransformStateInitial); + + // Unselect the monitor + mIpSecPacketLossDetector.setIsSelectedUnderlyingNetwork(false /* setIsSelected */); + verifyStopped(); + + xfrmStateReceiver.onResult(newTransformState(1, 1, newReplayBitmap(1))); + verify(mPacketLossCalculator, never()) + .getPacketLossRatePercentage(any(), any(), anyString()); + } + + @Test + public void testTransformStateReceiverOnError() throws Exception { + final OutcomeReceiver<IpSecTransformState, RuntimeException> xfrmStateReceiver = + startMonitorAndCaptureStateReceiver(); + xfrmStateReceiver.onResult(mTransformStateInitial); + + xfrmStateReceiver.onError(new RuntimeException("Test")); + verify(mPacketLossCalculator, never()) + .getPacketLossRatePercentage(any(), any(), anyString()); + } + + private void checkHandleLossRate( + int mockPacketLossRate, boolean isLastStateExpectedToUpdate, boolean isCallbackExpected) + throws Exception { + final OutcomeReceiver<IpSecTransformState, RuntimeException> xfrmStateReceiver = + startMonitorAndCaptureStateReceiver(); + doReturn(mockPacketLossRate) + .when(mPacketLossCalculator) + .getPacketLossRatePercentage(any(), any(), anyString()); + + // Mock receiving two states with mTransformStateInitial and an arbitrary transformNew + final IpSecTransformState transformNew = newTransformState(1, 1, newReplayBitmap(1)); + xfrmStateReceiver.onResult(mTransformStateInitial); + xfrmStateReceiver.onResult(transformNew); + + // Verifications + verify(mPacketLossCalculator) + .getPacketLossRatePercentage( + eq(mTransformStateInitial), eq(transformNew), anyString()); + + if (isLastStateExpectedToUpdate) { + assertEquals(transformNew, mIpSecPacketLossDetector.getLastTransformState()); + } else { + assertEquals(mTransformStateInitial, mIpSecPacketLossDetector.getLastTransformState()); + } + + if (isCallbackExpected) { + verify(mMetricMonitorCallback).onValidationResultReceived(); + } else { + verify(mMetricMonitorCallback, never()).onValidationResultReceived(); + } + } + + @Test + public void testHandleLossRate_validationPass() throws Exception { + checkHandleLossRate( + 2, true /* isLastStateExpectedToUpdate */, true /* isCallbackExpected */); + } + + @Test + public void testHandleLossRate_validationFail() throws Exception { + checkHandleLossRate( + 22, true /* isLastStateExpectedToUpdate */, true /* isCallbackExpected */); + } + + @Test + public void testHandleLossRate_resultUnavalaible() throws Exception { + checkHandleLossRate( + PACKET_LOSS_UNAVALAIBLE, + false /* isLastStateExpectedToUpdate */, + false /* isCallbackExpected */); + } + + private void checkGetPacketLossRate( + IpSecTransformState oldState, IpSecTransformState newState, int expectedLossRate) + throws Exception { + assertEquals( + expectedLossRate, + mPacketLossCalculator.getPacketLossRatePercentage(oldState, newState, TAG)); + } + + private void checkGetPacketLossRate( + IpSecTransformState oldState, + int rxSeqNo, + int packetCount, + int packetInWin, + int expectedDataLossRate) + throws Exception { + final IpSecTransformState newState = + newTransformState(rxSeqNo, packetCount, newReplayBitmap(packetInWin)); + checkGetPacketLossRate(oldState, newState, expectedDataLossRate); + } + + @Test + public void testGetPacketLossRate_replayWindowUnchanged() throws Exception { + checkGetPacketLossRate( + mTransformStateInitial, mTransformStateInitial, PACKET_LOSS_UNAVALAIBLE); + checkGetPacketLossRate(mTransformStateInitial, 3000, 2000, 2000, PACKET_LOSS_UNAVALAIBLE); + } + + @Test + public void testGetPacketLossRate_againstInitialState() throws Exception { + checkGetPacketLossRate(mTransformStateInitial, 7000, 7001, 4096, 0); + checkGetPacketLossRate(mTransformStateInitial, 7000, 6000, 4096, 15); + checkGetPacketLossRate(mTransformStateInitial, 7000, 6000, 4000, 14); + } + + @Test + public void testGetPktLossRate_oldHiSeqSmallerThanWinSize_overlappedWithNewWin() + throws Exception { + final IpSecTransformState oldState = newTransformState(2000, 1500, newReplayBitmap(1500)); + + checkGetPacketLossRate(oldState, 5000, 5001, 4096, 0); + checkGetPacketLossRate(oldState, 5000, 4000, 4096, 29); + checkGetPacketLossRate(oldState, 5000, 4000, 4000, 27); + } + + @Test + public void testGetPktLossRate_oldHiSeqSmallerThanWinSize_notOverlappedWithNewWin() + throws Exception { + final IpSecTransformState oldState = newTransformState(2000, 1500, newReplayBitmap(1500)); + + checkGetPacketLossRate(oldState, 7000, 7001, 4096, 0); + checkGetPacketLossRate(oldState, 7000, 5000, 4096, 37); + checkGetPacketLossRate(oldState, 7000, 5000, 3000, 21); + } + + @Test + public void testGetPktLossRate_oldHiSeqLargerThanWinSize_overlappedWithNewWin() + throws Exception { + final IpSecTransformState oldState = newTransformState(10000, 5000, newReplayBitmap(3000)); + + checkGetPacketLossRate(oldState, 12000, 8096, 4096, 0); + checkGetPacketLossRate(oldState, 12000, 7000, 4096, 36); + checkGetPacketLossRate(oldState, 12000, 7000, 3000, 0); + } + + @Test + public void testGetPktLossRate_oldHiSeqLargerThanWinSize_notOverlappedWithNewWin() + throws Exception { + final IpSecTransformState oldState = newTransformState(10000, 5000, newReplayBitmap(3000)); + + checkGetPacketLossRate(oldState, 20000, 16096, 4096, 0); + checkGetPacketLossRate(oldState, 20000, 14000, 4096, 19); + checkGetPacketLossRate(oldState, 20000, 14000, 3000, 10); + } +} diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java new file mode 100644 index 000000000000..6015e9318464 --- /dev/null +++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2023 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.routeselection; + +import static com.android.server.vcn.VcnTestUtils.setupSystemService; +import static com.android.server.vcn.routeselection.UnderlyingNetworkControllerTest.getLinkPropertiesWithName; + +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.net.IpSecConfig; +import android.net.IpSecTransform; +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.TelephonyNetworkSpecifier; +import android.net.vcn.FeatureFlags; +import android.os.Handler; +import android.os.IPowerManager; +import android.os.IThermalService; +import android.os.ParcelUuid; +import android.os.PowerManager; +import android.os.test.TestLooper; +import android.telephony.TelephonyManager; + +import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; +import com.android.server.vcn.VcnContext; +import com.android.server.vcn.VcnNetworkProvider; + +import org.junit.Before; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Set; +import java.util.UUID; + +public abstract class NetworkEvaluationTestBase { + protected static final String SSID = "TestWifi"; + protected static final String SSID_OTHER = "TestWifiOther"; + protected static final String PLMN_ID = "123456"; + protected static final String PLMN_ID_OTHER = "234567"; + + protected static final int SUB_ID = 1; + protected static final int WIFI_RSSI = -60; + protected static final int WIFI_RSSI_HIGH = -50; + protected static final int WIFI_RSSI_LOW = -80; + protected static final int CARRIER_ID = 1; + protected static final int CARRIER_ID_OTHER = 2; + + protected static final int LINK_UPSTREAM_BANDWIDTH_KBPS = 1024; + protected static final int LINK_DOWNSTREAM_BANDWIDTH_KBPS = 2048; + + protected static final int TEST_MIN_UPSTREAM_BANDWIDTH_KBPS = 100; + protected static final int TEST_MIN_DOWNSTREAM_BANDWIDTH_KBPS = 200; + + protected static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0)); + + protected static final NetworkCapabilities WIFI_NETWORK_CAPABILITIES = + new NetworkCapabilities.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .setSignalStrength(WIFI_RSSI) + .setSsid(SSID) + .setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS) + .setLinkDownstreamBandwidthKbps(LINK_DOWNSTREAM_BANDWIDTH_KBPS) + .build(); + + protected static final TelephonyNetworkSpecifier TEL_NETWORK_SPECIFIER = + new TelephonyNetworkSpecifier.Builder().setSubscriptionId(SUB_ID).build(); + protected static final NetworkCapabilities CELL_NETWORK_CAPABILITIES = + new NetworkCapabilities.Builder() + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN) + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .setSubscriptionIds(Set.of(SUB_ID)) + .setNetworkSpecifier(TEL_NETWORK_SPECIFIER) + .setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS) + .setLinkDownstreamBandwidthKbps(LINK_DOWNSTREAM_BANDWIDTH_KBPS) + .build(); + + protected static final LinkProperties LINK_PROPERTIES = getLinkPropertiesWithName("test_iface"); + + @Mock protected Context mContext; + @Mock protected Network mNetwork; + @Mock protected FeatureFlags mFeatureFlags; + @Mock protected com.android.net.flags.FeatureFlags mCoreNetFeatureFlags; + @Mock protected TelephonySubscriptionSnapshot mSubscriptionSnapshot; + @Mock protected TelephonyManager mTelephonyManager; + @Mock protected IPowerManager mPowerManagerService; + + protected TestLooper mTestLooper; + protected VcnContext mVcnContext; + protected PowerManager mPowerManager; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + when(mNetwork.getNetId()).thenReturn(-1); + + mTestLooper = new TestLooper(); + mVcnContext = + spy( + new VcnContext( + mContext, + mTestLooper.getLooper(), + mock(VcnNetworkProvider.class), + false /* isInTestMode */)); + doNothing().when(mVcnContext).ensureRunningOnLooperThread(); + + doReturn(true).when(mVcnContext).isFlagNetworkMetricMonitorEnabled(); + doReturn(true).when(mVcnContext).isFlagIpSecTransformStateEnabled(); + + setupSystemService( + mContext, mTelephonyManager, Context.TELEPHONY_SERVICE, TelephonyManager.class); + when(mTelephonyManager.createForSubscriptionId(SUB_ID)).thenReturn(mTelephonyManager); + when(mTelephonyManager.getNetworkOperator()).thenReturn(PLMN_ID); + when(mTelephonyManager.getSimSpecificCarrierId()).thenReturn(CARRIER_ID); + + mPowerManager = + new PowerManager( + mContext, + mPowerManagerService, + mock(IThermalService.class), + mock(Handler.class)); + setupSystemService(mContext, mPowerManager, Context.POWER_SERVICE, PowerManager.class); + } + + protected IpSecTransform makeDummyIpSecTransform() throws Exception { + return new IpSecTransform(mContext, new IpSecConfig()); + } +} diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java index 226604108522..d85c5150f53f 100644 --- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java +++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java @@ -24,152 +24,48 @@ import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_ENTR import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS; import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS; -import static com.android.server.vcn.VcnTestUtils.setupSystemService; import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_FALLBACK; import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_INVALID; import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesCellPriorityRule; import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesPriorityRule; import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesWifiPriorityRule; -import static com.android.server.vcn.routeselection.UnderlyingNetworkControllerTest.getLinkPropertiesWithName; import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; -import android.content.Context; -import android.net.LinkProperties; -import android.net.Network; import android.net.NetworkCapabilities; -import android.net.TelephonyNetworkSpecifier; import android.net.vcn.VcnCellUnderlyingNetworkTemplate; import android.net.vcn.VcnGatewayConnectionConfig; import android.net.vcn.VcnManager; import android.net.vcn.VcnUnderlyingNetworkTemplate; import android.net.vcn.VcnWifiUnderlyingNetworkTemplate; -import android.os.ParcelUuid; import android.os.PersistableBundle; -import android.os.test.TestLooper; -import android.telephony.TelephonyManager; import android.util.ArraySet; -import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; -import com.android.server.vcn.VcnContext; -import com.android.server.vcn.VcnNetworkProvider; - import org.junit.Before; import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; import java.util.Collections; import java.util.List; import java.util.Set; -import java.util.UUID; - -public class NetworkPriorityClassifierTest { - private static final String SSID = "TestWifi"; - private static final String SSID_OTHER = "TestWifiOther"; - private static final String PLMN_ID = "123456"; - private static final String PLMN_ID_OTHER = "234567"; - - private static final int SUB_ID = 1; - private static final int WIFI_RSSI = -60; - private static final int WIFI_RSSI_HIGH = -50; - private static final int WIFI_RSSI_LOW = -80; - private static final int CARRIER_ID = 1; - private static final int CARRIER_ID_OTHER = 2; - - private static final int LINK_UPSTREAM_BANDWIDTH_KBPS = 1024; - private static final int LINK_DOWNSTREAM_BANDWIDTH_KBPS = 2048; - - private static final int TEST_MIN_UPSTREAM_BANDWIDTH_KBPS = 100; - private static final int TEST_MIN_DOWNSTREAM_BANDWIDTH_KBPS = 200; - - private static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0)); - - private static final NetworkCapabilities WIFI_NETWORK_CAPABILITIES = - new NetworkCapabilities.Builder() - .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) - .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) - .setSignalStrength(WIFI_RSSI) - .setSsid(SSID) - .setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS) - .setLinkDownstreamBandwidthKbps(LINK_DOWNSTREAM_BANDWIDTH_KBPS) - .build(); - - private static final TelephonyNetworkSpecifier TEL_NETWORK_SPECIFIER = - new TelephonyNetworkSpecifier.Builder().setSubscriptionId(SUB_ID).build(); - private static final NetworkCapabilities CELL_NETWORK_CAPABILITIES = - new NetworkCapabilities.Builder() - .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) - .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN) - .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) - .setSubscriptionIds(Set.of(SUB_ID)) - .setNetworkSpecifier(TEL_NETWORK_SPECIFIER) - .setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS) - .setLinkDownstreamBandwidthKbps(LINK_DOWNSTREAM_BANDWIDTH_KBPS) - .build(); - - private static final LinkProperties LINK_PROPERTIES = getLinkPropertiesWithName("test_iface"); - - @Mock private Network mNetwork; - @Mock private TelephonySubscriptionSnapshot mSubscriptionSnapshot; - @Mock private TelephonyManager mTelephonyManager; - - private TestLooper mTestLooper; - private VcnContext mVcnContext; + +public class NetworkPriorityClassifierTest extends NetworkEvaluationTestBase { private UnderlyingNetworkRecord mWifiNetworkRecord; private UnderlyingNetworkRecord mCellNetworkRecord; @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - - final Context mockContext = mock(Context.class); - mTestLooper = new TestLooper(); - mVcnContext = - spy( - new VcnContext( - mockContext, - mTestLooper.getLooper(), - mock(VcnNetworkProvider.class), - false /* isInTestMode */)); - doNothing().when(mVcnContext).ensureRunningOnLooperThread(); - - setupSystemService( - mockContext, mTelephonyManager, Context.TELEPHONY_SERVICE, TelephonyManager.class); - when(mTelephonyManager.createForSubscriptionId(SUB_ID)).thenReturn(mTelephonyManager); - when(mTelephonyManager.getNetworkOperator()).thenReturn(PLMN_ID); - when(mTelephonyManager.getSimSpecificCarrierId()).thenReturn(CARRIER_ID); - - mWifiNetworkRecord = - getTestNetworkRecord( - WIFI_NETWORK_CAPABILITIES, - VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES); - mCellNetworkRecord = - getTestNetworkRecord( - CELL_NETWORK_CAPABILITIES, - VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES); - } - - private UnderlyingNetworkRecord getTestNetworkRecord( - NetworkCapabilities nc, List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates) { - return new UnderlyingNetworkRecord( - mNetwork, - nc, - LINK_PROPERTIES, - false /* isBlocked */, - mVcnContext, - underlyingNetworkTemplates, - SUB_GROUP, - mSubscriptionSnapshot, - null /* currentlySelected */, - null /* carrierConfig */); + public void setUp() throws Exception { + super.setUp(); + + mWifiNetworkRecord = getTestNetworkRecord(WIFI_NETWORK_CAPABILITIES); + mCellNetworkRecord = getTestNetworkRecord(CELL_NETWORK_CAPABILITIES); + } + + private UnderlyingNetworkRecord getTestNetworkRecord(NetworkCapabilities nc) { + return new UnderlyingNetworkRecord(mNetwork, nc, LINK_PROPERTIES, false /* isBlocked */); } @Test @@ -186,14 +82,14 @@ public class NetworkPriorityClassifierTest { mWifiNetworkRecord, SUB_GROUP, mSubscriptionSnapshot, - null /* currentlySelecetd */, + false /* isSelected */, null /* carrierConfig */)); } private void verifyMatchesPriorityRuleForUpstreamBandwidth( int entryUpstreamBandwidth, int exitUpstreamBandwidth, - UnderlyingNetworkRecord currentlySelected, + boolean isSelected, boolean expectMatch) { final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority = new VcnWifiUnderlyingNetworkTemplate.Builder() @@ -208,14 +104,14 @@ public class NetworkPriorityClassifierTest { mWifiNetworkRecord, SUB_GROUP, mSubscriptionSnapshot, - currentlySelected, + isSelected, null /* carrierConfig */)); } private void verifyMatchesPriorityRuleForDownstreamBandwidth( int entryDownstreamBandwidth, int exitDownstreamBandwidth, - UnderlyingNetworkRecord currentlySelected, + boolean isSelected, boolean expectMatch) { final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority = new VcnWifiUnderlyingNetworkTemplate.Builder() @@ -231,7 +127,7 @@ public class NetworkPriorityClassifierTest { mWifiNetworkRecord, SUB_GROUP, mSubscriptionSnapshot, - currentlySelected, + isSelected, null /* carrierConfig */)); } @@ -240,7 +136,7 @@ public class NetworkPriorityClassifierTest { verifyMatchesPriorityRuleForUpstreamBandwidth( TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS, TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS, - null /* currentlySelected */, + false /* isSelected */, true); } @@ -249,7 +145,7 @@ public class NetworkPriorityClassifierTest { verifyMatchesPriorityRuleForUpstreamBandwidth( LINK_UPSTREAM_BANDWIDTH_KBPS + 1, LINK_UPSTREAM_BANDWIDTH_KBPS + 1, - null /* currentlySelected */, + false /* isSelected */, false); } @@ -258,7 +154,7 @@ public class NetworkPriorityClassifierTest { verifyMatchesPriorityRuleForDownstreamBandwidth( TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS, TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS, - null /* currentlySelected */, + false /* isSelected */, true); } @@ -267,7 +163,7 @@ public class NetworkPriorityClassifierTest { verifyMatchesPriorityRuleForDownstreamBandwidth( LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1, LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1, - null /* currentlySelected */, + false /* isSelected */, false); } @@ -276,7 +172,7 @@ public class NetworkPriorityClassifierTest { verifyMatchesPriorityRuleForUpstreamBandwidth( TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS, TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS, - mWifiNetworkRecord, + true /* isSelected */, true); } @@ -285,7 +181,7 @@ public class NetworkPriorityClassifierTest { verifyMatchesPriorityRuleForUpstreamBandwidth( LINK_UPSTREAM_BANDWIDTH_KBPS + 1, LINK_UPSTREAM_BANDWIDTH_KBPS + 1, - mWifiNetworkRecord, + true /* isSelected */, false); } @@ -294,7 +190,7 @@ public class NetworkPriorityClassifierTest { verifyMatchesPriorityRuleForDownstreamBandwidth( TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS, TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS, - mWifiNetworkRecord, + true /* isSelected */, true); } @@ -303,7 +199,7 @@ public class NetworkPriorityClassifierTest { verifyMatchesPriorityRuleForDownstreamBandwidth( LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1, LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1, - mWifiNetworkRecord, + true /* isSelected */, false); } @@ -318,14 +214,12 @@ public class NetworkPriorityClassifierTest { TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS, TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS) .build(); - final UnderlyingNetworkRecord selectedNetworkRecord = - isSelectedNetwork ? mWifiNetworkRecord : null; assertEquals( expectMatch, checkMatchesWifiPriorityRule( wifiNetworkPriority, mWifiNetworkRecord, - selectedNetworkRecord, + isSelectedNetwork, carrierConfig == null ? null : new PersistableBundleWrapper(carrierConfig))); @@ -381,7 +275,7 @@ public class NetworkPriorityClassifierTest { checkMatchesWifiPriorityRule( wifiNetworkPriority, mWifiNetworkRecord, - null /* currentlySelecetd */, + false /* isSelected */, null /* carrierConfig */)); } @@ -516,7 +410,7 @@ public class NetworkPriorityClassifierTest { mCellNetworkRecord, SUB_GROUP, mSubscriptionSnapshot, - null /* currentlySelected */, + false /* isSelected */, null /* carrierConfig */)); } @@ -543,7 +437,16 @@ public class NetworkPriorityClassifierTest { @Test public void testCalculatePriorityClass() throws Exception { - assertEquals(2, mCellNetworkRecord.priorityClass); + final int priorityClass = + NetworkPriorityClassifier.calculatePriorityClass( + mVcnContext, + mCellNetworkRecord, + VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES, + SUB_GROUP, + mSubscriptionSnapshot, + false /* isSelected */, + null /* carrierConfig */); + assertEquals(2, priorityClass); } private void checkCalculatePriorityClassFailToMatchAny( @@ -561,10 +464,19 @@ public class NetworkPriorityClassifierTest { ncBuilder.addCapability(NET_CAPABILITY_INTERNET); } - final UnderlyingNetworkRecord nonDunNetworkRecord = - getTestNetworkRecord(ncBuilder.build(), templatesRequireDun); + final UnderlyingNetworkRecord nonDunNetworkRecord = getTestNetworkRecord(ncBuilder.build()); + + final int priorityClass = + NetworkPriorityClassifier.calculatePriorityClass( + mVcnContext, + nonDunNetworkRecord, + templatesRequireDun, + SUB_GROUP, + mSubscriptionSnapshot, + false /* isSelected */, + null /* carrierConfig */); - assertEquals(expectedPriorityClass, nonDunNetworkRecord.priorityClass); + assertEquals(expectedPriorityClass, priorityClass); } @Test diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java index 2941fdea20bb..588624b56221 100644 --- a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java +++ b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java @@ -29,13 +29,12 @@ import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.WI import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.WIFI_EXIT_RSSI_THRESHOLD_DEFAULT; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -48,6 +47,8 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.net.ConnectivityManager; +import android.net.IpSecConfig; +import android.net.IpSecTransform; import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; @@ -67,9 +68,11 @@ import android.util.ArraySet; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import com.android.server.vcn.VcnContext; import com.android.server.vcn.VcnNetworkProvider; +import com.android.server.vcn.routeselection.UnderlyingNetworkController.Dependencies; import com.android.server.vcn.routeselection.UnderlyingNetworkController.NetworkBringupCallback; import com.android.server.vcn.routeselection.UnderlyingNetworkController.UnderlyingNetworkControllerCallback; import com.android.server.vcn.routeselection.UnderlyingNetworkController.UnderlyingNetworkListener; +import com.android.server.vcn.routeselection.UnderlyingNetworkEvaluator.NetworkEvaluatorCallback; import org.junit.Before; import org.junit.Test; @@ -77,6 +80,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.Spy; import java.util.ArrayList; import java.util.Arrays; @@ -152,12 +156,17 @@ public class UnderlyingNetworkControllerTest { @Mock private CarrierConfigManager mCarrierConfigManager; @Mock private TelephonySubscriptionSnapshot mSubscriptionSnapshot; @Mock private UnderlyingNetworkControllerCallback mNetworkControllerCb; + @Mock private NetworkEvaluatorCallback mEvaluatorCallback; @Mock private Network mNetwork; + @Spy private Dependencies mDependencies = new Dependencies(); + @Captor private ArgumentCaptor<UnderlyingNetworkListener> mUnderlyingNetworkListenerCaptor; + @Captor private ArgumentCaptor<NetworkEvaluatorCallback> mEvaluatorCallbackCaptor; private TestLooper mTestLooper; private VcnContext mVcnContext; + private UnderlyingNetworkEvaluator mNetworkEvaluator; private UnderlyingNetworkController mUnderlyingNetworkController; @Before @@ -172,7 +181,7 @@ public class UnderlyingNetworkControllerTest { mTestLooper.getLooper(), mVcnNetworkProvider, false /* isInTestMode */)); - resetVcnContext(); + resetVcnContext(mVcnContext); setupSystemService( mContext, @@ -189,18 +198,36 @@ public class UnderlyingNetworkControllerTest { when(mSubscriptionSnapshot.getAllSubIdsInGroup(eq(SUB_GROUP))).thenReturn(INITIAL_SUB_IDS); + mNetworkEvaluator = + spy( + new UnderlyingNetworkEvaluator( + mVcnContext, + mNetwork, + VcnGatewayConnectionConfigTest.buildTestConfig() + .getVcnUnderlyingNetworkPriorities(), + SUB_GROUP, + mSubscriptionSnapshot, + null, + mEvaluatorCallback)); + doReturn(mNetworkEvaluator) + .when(mDependencies) + .newUnderlyingNetworkEvaluator(any(), any(), any(), any(), any(), any(), any()); + mUnderlyingNetworkController = new UnderlyingNetworkController( mVcnContext, VcnGatewayConnectionConfigTest.buildTestConfig(), SUB_GROUP, mSubscriptionSnapshot, - mNetworkControllerCb); + mNetworkControllerCb, + mDependencies); } - private void resetVcnContext() { - reset(mVcnContext); - doNothing().when(mVcnContext).ensureRunningOnLooperThread(); + private void resetVcnContext(VcnContext vcnContext) { + reset(vcnContext); + doNothing().when(vcnContext).ensureRunningOnLooperThread(); + doReturn(true).when(vcnContext).isFlagNetworkMetricMonitorEnabled(); + doReturn(true).when(vcnContext).isFlagIpSecTransformStateEnabled(); } // Package private for use in NetworkPriorityClassifierTest @@ -226,11 +253,13 @@ public class UnderlyingNetworkControllerTest { final ConnectivityManager cm = mock(ConnectivityManager.class); setupSystemService(mContext, cm, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class); final VcnContext vcnContext = - new VcnContext( - mContext, - mTestLooper.getLooper(), - mVcnNetworkProvider, - true /* isInTestMode */); + spy( + new VcnContext( + mContext, + mTestLooper.getLooper(), + mVcnNetworkProvider, + true /* isInTestMode */)); + resetVcnContext(vcnContext); new UnderlyingNetworkController( vcnContext, @@ -489,13 +518,7 @@ public class UnderlyingNetworkControllerTest { NetworkCapabilities networkCapabilities, LinkProperties linkProperties, boolean isBlocked) { - return new UnderlyingNetworkRecord( - network, - networkCapabilities, - linkProperties, - isBlocked, - false /* isSelected */, - 0 /* priorityClass */); + return new UnderlyingNetworkRecord(network, networkCapabilities, linkProperties, isBlocked); } @Test @@ -515,24 +538,12 @@ public class UnderlyingNetworkControllerTest { UnderlyingNetworkRecord recordC = new UnderlyingNetworkRecord( mNetwork, - INITIAL_NETWORK_CAPABILITIES, - INITIAL_LINK_PROPERTIES, - false /* isBlocked */, - true /* isSelected */, - -1 /* priorityClass */); - UnderlyingNetworkRecord recordD = - getTestNetworkRecord( - mNetwork, UPDATED_NETWORK_CAPABILITIES, UPDATED_LINK_PROPERTIES, false /* isBlocked */); assertEquals(recordA, recordB); - assertEquals(recordA, recordC); - assertNotEquals(recordA, recordD); - - assertTrue(UnderlyingNetworkRecord.isEqualIncludingPriorities(recordA, recordB)); - assertFalse(UnderlyingNetworkRecord.isEqualIncludingPriorities(recordA, recordC)); + assertNotEquals(recordA, recordC); } @Test @@ -540,6 +551,58 @@ public class UnderlyingNetworkControllerTest { verifyRegistrationOnAvailableAndGetCallback(); } + @Test + public void testUpdateSubscriptionSnapshotAndCarrierConfig() { + verifyRegistrationOnAvailableAndGetCallback(); + + TelephonySubscriptionSnapshot subscriptionUpdate = + mock(TelephonySubscriptionSnapshot.class); + when(subscriptionUpdate.getAllSubIdsInGroup(eq(SUB_GROUP))).thenReturn(UPDATED_SUB_IDS); + + mUnderlyingNetworkController.updateSubscriptionSnapshot(subscriptionUpdate); + + verify(mNetworkEvaluator).reevaluate(any(), any(), any(), any()); + } + + @Test + public void testUpdateIpSecTransform() { + verifyRegistrationOnAvailableAndGetCallback(); + + final UnderlyingNetworkRecord expectedRecord = + getTestNetworkRecord( + mNetwork, + INITIAL_NETWORK_CAPABILITIES, + INITIAL_LINK_PROPERTIES, + false /* isBlocked */); + final IpSecTransform expectedTransform = new IpSecTransform(mContext, new IpSecConfig()); + + mUnderlyingNetworkController.updateInboundTransform(expectedRecord, expectedTransform); + verify(mNetworkEvaluator).setInboundTransform(expectedTransform); + } + + @Test + public void testOnEvaluationResultChanged() { + verifyRegistrationOnAvailableAndGetCallback(); + + // Verify #reevaluateNetworks is called by checking #getNetworkRecord + verify(mNetworkEvaluator).getNetworkRecord(); + + // Trigger the callback + verify(mDependencies) + .newUnderlyingNetworkEvaluator( + any(), + any(), + any(), + any(), + any(), + any(), + mEvaluatorCallbackCaptor.capture()); + mEvaluatorCallbackCaptor.getValue().onEvaluationResultChanged(); + + // Verify #reevaluateNetworks is called again + verify(mNetworkEvaluator, times(2)).getNetworkRecord(); + } + private UnderlyingNetworkListener verifyRegistrationOnAvailableAndGetCallback() { return verifyRegistrationOnAvailableAndGetCallback(INITIAL_NETWORK_CAPABILITIES); } @@ -583,6 +646,7 @@ public class UnderlyingNetworkControllerTest { INITIAL_LINK_PROPERTIES, false /* isBlocked */); verifyOnSelectedUnderlyingNetworkChanged(expectedRecord); + verify(mNetworkEvaluator).setIsSelected(eq(true), any(), any(), any(), any()); return cb; } @@ -667,7 +731,7 @@ public class UnderlyingNetworkControllerTest { cb.onBlockedStatusChanged(mNetwork, true /* isBlocked */); - verifyOnSelectedUnderlyingNetworkChanged(null); + verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(null); } @Test @@ -675,6 +739,7 @@ public class UnderlyingNetworkControllerTest { UnderlyingNetworkListener cb = verifyRegistrationOnAvailableAndGetCallback(); cb.onLost(mNetwork); + verify(mNetworkEvaluator).close(); verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(null); } @@ -713,7 +778,8 @@ public class UnderlyingNetworkControllerTest { VcnGatewayConnectionConfigTest.buildTestConfig(networkTemplates), SUB_GROUP, mSubscriptionSnapshot, - mNetworkControllerCb); + mNetworkControllerCb, + mDependencies); verify(cm) .registerNetworkCallback( @@ -724,30 +790,44 @@ public class UnderlyingNetworkControllerTest { return mUnderlyingNetworkListenerCaptor.getValue(); } - private UnderlyingNetworkRecord bringupNetworkAndGetRecord( + private UnderlyingNetworkEvaluator bringupNetworkAndGetEvaluator( UnderlyingNetworkListener cb, NetworkCapabilities requestNetworkCaps, - List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, - UnderlyingNetworkRecord currentlySelected) { + List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates) { final Network network = mock(Network.class); final NetworkCapabilities responseNetworkCaps = buildResponseNwCaps(requestNetworkCaps, INITIAL_SUB_IDS); + final UnderlyingNetworkEvaluator evaluator = + spy( + new UnderlyingNetworkEvaluator( + mVcnContext, + network, + underlyingNetworkTemplates, + SUB_GROUP, + mSubscriptionSnapshot, + null, + mEvaluatorCallback)); + doReturn(evaluator) + .when(mDependencies) + .newUnderlyingNetworkEvaluator(any(), any(), any(), any(), any(), any(), any()); cb.onAvailable(network); cb.onCapabilitiesChanged(network, responseNetworkCaps); cb.onLinkPropertiesChanged(network, INITIAL_LINK_PROPERTIES); cb.onBlockedStatusChanged(network, false /* isFalse */); - return new UnderlyingNetworkRecord( - network, - responseNetworkCaps, - INITIAL_LINK_PROPERTIES, - false /* isBlocked */, - mVcnContext, - underlyingNetworkTemplates, - SUB_GROUP, - mSubscriptionSnapshot, - currentlySelected, - null /* carrierConfig */); + + return evaluator; + } + + private void verifySelectNetwork(UnderlyingNetworkEvaluator expectedEvaluator) { + verifyOnSelectedUnderlyingNetworkChanged(expectedEvaluator.getNetworkRecord()); + verify(expectedEvaluator).setIsSelected(eq(true), any(), any(), any(), any()); + } + + private void verifyNeverSelectNetwork(UnderlyingNetworkEvaluator expectedEvaluator) { + verify(mNetworkControllerCb, never()) + .onSelectedUnderlyingNetworkChanged(eq(expectedEvaluator.getNetworkRecord())); + verify(expectedEvaluator, never()).setIsSelected(eq(true), any(), any(), any(), any()); } @Test @@ -759,19 +839,15 @@ public class UnderlyingNetworkControllerTest { UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates); // Bring up CBS network - final UnderlyingNetworkRecord cbsNetworkRecord = - bringupNetworkAndGetRecord( - cb, - CBS_NETWORK_CAPABILITIES, - networkTemplates, - null /* currentlySelected */); - verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(cbsNetworkRecord)); + final UnderlyingNetworkEvaluator cbsNetworkEvaluator = + bringupNetworkAndGetEvaluator(cb, CBS_NETWORK_CAPABILITIES, networkTemplates); + verifySelectNetwork(cbsNetworkEvaluator); // Bring up DUN network - final UnderlyingNetworkRecord dunNetworkRecord = - bringupNetworkAndGetRecord( - cb, DUN_NETWORK_CAPABILITIES, networkTemplates, cbsNetworkRecord); - verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(dunNetworkRecord)); + final UnderlyingNetworkEvaluator dunNetworkEvaluator = + bringupNetworkAndGetEvaluator(cb, DUN_NETWORK_CAPABILITIES, networkTemplates); + verifySelectNetwork(dunNetworkEvaluator); + verify(cbsNetworkEvaluator).setIsSelected(eq(false), any(), any(), any(), any()); } @Test @@ -783,20 +859,14 @@ public class UnderlyingNetworkControllerTest { UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates); // Bring up DUN network - final UnderlyingNetworkRecord dunNetworkRecord = - bringupNetworkAndGetRecord( - cb, - DUN_NETWORK_CAPABILITIES, - networkTemplates, - null /* currentlySelected */); - verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(dunNetworkRecord)); + final UnderlyingNetworkEvaluator dunNetworkEvaluator = + bringupNetworkAndGetEvaluator(cb, DUN_NETWORK_CAPABILITIES, networkTemplates); + verifySelectNetwork(dunNetworkEvaluator); // Bring up CBS network - final UnderlyingNetworkRecord cbsNetworkRecord = - bringupNetworkAndGetRecord( - cb, CBS_NETWORK_CAPABILITIES, networkTemplates, dunNetworkRecord); - verify(mNetworkControllerCb, never()) - .onSelectedUnderlyingNetworkChanged(eq(cbsNetworkRecord)); + final UnderlyingNetworkEvaluator cbsNetworkEvaluator = + bringupNetworkAndGetEvaluator(cb, CBS_NETWORK_CAPABILITIES, networkTemplates); + verifyNeverSelectNetwork(cbsNetworkEvaluator); } @Test @@ -808,13 +878,9 @@ public class UnderlyingNetworkControllerTest { UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates); // Bring up an Internet network without DUN capability - final UnderlyingNetworkRecord networkRecord = - bringupNetworkAndGetRecord( - cb, - INITIAL_NETWORK_CAPABILITIES, - networkTemplates, - null /* currentlySelected */); - verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(networkRecord)); + final UnderlyingNetworkEvaluator evaluator = + bringupNetworkAndGetEvaluator(cb, INITIAL_NETWORK_CAPABILITIES, networkTemplates); + verifySelectNetwork(evaluator); } @Test @@ -825,10 +891,8 @@ public class UnderlyingNetworkControllerTest { new VcnCellUnderlyingNetworkTemplate.Builder().setDun(MATCH_REQUIRED).build()); UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates); - bringupNetworkAndGetRecord( - cb, CBS_NETWORK_CAPABILITIES, networkTemplates, null /* currentlySelected */); - - verify(mNetworkControllerCb, never()) - .onSelectedUnderlyingNetworkChanged(any(UnderlyingNetworkRecord.class)); + final UnderlyingNetworkEvaluator evaluator = + bringupNetworkAndGetEvaluator(cb, CBS_NETWORK_CAPABILITIES, networkTemplates); + verifyNeverSelectNetwork(evaluator); } } diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java new file mode 100644 index 000000000000..aa81efe9a1ce --- /dev/null +++ b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java @@ -0,0 +1,336 @@ +/* + * Copyright (C) 2023 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.routeselection; + +import static android.net.vcn.VcnManager.VCN_NETWORK_SELECTION_PENALTY_TIMEOUT_MINUTES_LIST_KEY; + +import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_INVALID; +import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyObject; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.net.IpSecTransform; +import android.net.vcn.VcnGatewayConnectionConfig; + +import com.android.server.vcn.routeselection.NetworkMetricMonitor.NetworkMetricMonitorCallback; +import com.android.server.vcn.routeselection.UnderlyingNetworkEvaluator.Dependencies; +import com.android.server.vcn.routeselection.UnderlyingNetworkEvaluator.NetworkEvaluatorCallback; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; + +import java.util.concurrent.TimeUnit; + +public class UnderlyingNetworkEvaluatorTest extends NetworkEvaluationTestBase { + private static final int PENALTY_TIMEOUT_MIN = 10; + private static final long PENALTY_TIMEOUT_MS = TimeUnit.MINUTES.toMillis(PENALTY_TIMEOUT_MIN); + + @Mock private PersistableBundleWrapper mCarrierConfig; + @Mock private IpSecPacketLossDetector mIpSecPacketLossDetector; + @Mock private Dependencies mDependencies; + @Mock private NetworkEvaluatorCallback mEvaluatorCallback; + + @Captor private ArgumentCaptor<NetworkMetricMonitorCallback> mMetricMonitorCbCaptor; + + private UnderlyingNetworkEvaluator mNetworkEvaluator; + + @Before + public void setUp() throws Exception { + super.setUp(); + + when(mDependencies.newIpSecPacketLossDetector(any(), any(), any(), any())) + .thenReturn(mIpSecPacketLossDetector); + + when(mCarrierConfig.getIntArray( + eq(VCN_NETWORK_SELECTION_PENALTY_TIMEOUT_MINUTES_LIST_KEY), anyObject())) + .thenReturn(new int[] {PENALTY_TIMEOUT_MIN}); + + mNetworkEvaluator = newValidUnderlyingNetworkEvaluator(); + } + + private UnderlyingNetworkEvaluator newUnderlyingNetworkEvaluator() { + return new UnderlyingNetworkEvaluator( + mVcnContext, + mNetwork, + VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES, + SUB_GROUP, + mSubscriptionSnapshot, + mCarrierConfig, + mEvaluatorCallback, + mDependencies); + } + + private UnderlyingNetworkEvaluator newValidUnderlyingNetworkEvaluator() { + final UnderlyingNetworkEvaluator evaluator = newUnderlyingNetworkEvaluator(); + + evaluator.setNetworkCapabilities( + CELL_NETWORK_CAPABILITIES, + VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES, + SUB_GROUP, + mSubscriptionSnapshot, + mCarrierConfig); + evaluator.setLinkProperties( + LINK_PROPERTIES, + VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES, + SUB_GROUP, + mSubscriptionSnapshot, + mCarrierConfig); + evaluator.setIsBlocked( + false /* isBlocked */, + VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES, + SUB_GROUP, + mSubscriptionSnapshot, + mCarrierConfig); + + return evaluator; + } + + @Test + public void testInitializedEvaluator() throws Exception { + final UnderlyingNetworkEvaluator evaluator = newUnderlyingNetworkEvaluator(); + + assertFalse(evaluator.isValid()); + assertEquals(mNetwork, evaluator.getNetwork()); + assertEquals(PRIORITY_INVALID, evaluator.getPriorityClass()); + + try { + evaluator.getNetworkRecord(); + fail("Expected to fail because evaluator is not valid"); + } catch (Exception expected) { + } + } + + @Test + public void testValidEvaluator() { + final UnderlyingNetworkEvaluator evaluator = newUnderlyingNetworkEvaluator(); + evaluator.setNetworkCapabilities( + CELL_NETWORK_CAPABILITIES, + VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES, + SUB_GROUP, + mSubscriptionSnapshot, + mCarrierConfig); + evaluator.setLinkProperties( + LINK_PROPERTIES, + VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES, + SUB_GROUP, + mSubscriptionSnapshot, + mCarrierConfig); + evaluator.setIsBlocked( + false /* isBlocked */, + VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES, + SUB_GROUP, + mSubscriptionSnapshot, + mCarrierConfig); + + final UnderlyingNetworkRecord expectedRecord = + new UnderlyingNetworkRecord( + mNetwork, + CELL_NETWORK_CAPABILITIES, + LINK_PROPERTIES, + false /* isBlocked */); + + assertTrue(evaluator.isValid()); + assertEquals(mNetwork, evaluator.getNetwork()); + assertEquals(2, evaluator.getPriorityClass()); + assertEquals(expectedRecord, evaluator.getNetworkRecord()); + } + + private void checkSetSelectedNetwork(boolean isSelected) { + mNetworkEvaluator.setIsSelected( + isSelected, + VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES, + SUB_GROUP, + mSubscriptionSnapshot, + mCarrierConfig); + verify(mIpSecPacketLossDetector).setIsSelectedUnderlyingNetwork(isSelected); + } + + @Test + public void testSetIsSelected_selected() throws Exception { + checkSetSelectedNetwork(true /* isSelectedExpected */); + } + + @Test + public void testSetIsSelected_unselected() throws Exception { + checkSetSelectedNetwork(false /* isSelectedExpected */); + } + + @Test + public void testSetIpSecTransform_onSelectedNetwork() throws Exception { + final IpSecTransform transform = makeDummyIpSecTransform(); + + // Make the network selected + mNetworkEvaluator.setIsSelected( + true, + VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES, + SUB_GROUP, + mSubscriptionSnapshot, + mCarrierConfig); + mNetworkEvaluator.setInboundTransform(transform); + + verify(mIpSecPacketLossDetector).setInboundTransform(transform); + } + + @Test + public void testSetIpSecTransform_onUnSelectedNetwork() throws Exception { + mNetworkEvaluator.setIsSelected( + false, + VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES, + SUB_GROUP, + mSubscriptionSnapshot, + mCarrierConfig); + mNetworkEvaluator.setInboundTransform(makeDummyIpSecTransform()); + + verify(mIpSecPacketLossDetector, never()).setInboundTransform(any()); + } + + @Test + public void close() throws Exception { + mNetworkEvaluator.close(); + + verify(mIpSecPacketLossDetector).close(); + mTestLooper.moveTimeForward(PENALTY_TIMEOUT_MS); + assertNull(mTestLooper.nextMessage()); + } + + private NetworkMetricMonitorCallback getMetricMonitorCbCaptor() throws Exception { + verify(mDependencies) + .newIpSecPacketLossDetector(any(), any(), any(), mMetricMonitorCbCaptor.capture()); + + return mMetricMonitorCbCaptor.getValue(); + } + + private void checkPenalizeNetwork() throws Exception { + assertFalse(mNetworkEvaluator.isPenalized()); + + // Validation failed + when(mIpSecPacketLossDetector.isValidationFailed()).thenReturn(true); + getMetricMonitorCbCaptor().onValidationResultReceived(); + + // Verify the evaluator is penalized + assertTrue(mNetworkEvaluator.isPenalized()); + verify(mEvaluatorCallback).onEvaluationResultChanged(); + } + + @Test + public void testRcvValidationResult_penalizeNetwork_penaltyTimeout() throws Exception { + checkPenalizeNetwork(); + + // Penalty timeout + mTestLooper.moveTimeForward(PENALTY_TIMEOUT_MS); + mTestLooper.dispatchAll(); + + // Verify the evaluator is not penalized + assertFalse(mNetworkEvaluator.isPenalized()); + verify(mEvaluatorCallback, times(2)).onEvaluationResultChanged(); + } + + @Test + public void testRcvValidationResult_penalizeNetwork_passValidation() throws Exception { + checkPenalizeNetwork(); + + // Validation passed + when(mIpSecPacketLossDetector.isValidationFailed()).thenReturn(false); + getMetricMonitorCbCaptor().onValidationResultReceived(); + + // Verify the evaluator is not penalized and penalty timeout is canceled + assertFalse(mNetworkEvaluator.isPenalized()); + verify(mEvaluatorCallback, times(2)).onEvaluationResultChanged(); + mTestLooper.moveTimeForward(PENALTY_TIMEOUT_MS); + assertNull(mTestLooper.nextMessage()); + } + + @Test + public void testRcvValidationResult_penalizeNetwork_closeEvaluator() throws Exception { + checkPenalizeNetwork(); + + mNetworkEvaluator.close(); + + // Verify penalty timeout is canceled + mTestLooper.moveTimeForward(PENALTY_TIMEOUT_MS); + assertNull(mTestLooper.nextMessage()); + } + + @Test + public void testRcvValidationResult_PenaltyStateUnchanged() throws Exception { + assertFalse(mNetworkEvaluator.isPenalized()); + + // Validation passed + when(mIpSecPacketLossDetector.isValidationFailed()).thenReturn(false); + getMetricMonitorCbCaptor().onValidationResultReceived(); + + // Verifications + assertFalse(mNetworkEvaluator.isPenalized()); + verify(mEvaluatorCallback, never()).onEvaluationResultChanged(); + } + + @Test + public void testSetCarrierConfig() throws Exception { + final int additionalTimeoutMin = 10; + when(mCarrierConfig.getIntArray( + eq(VCN_NETWORK_SELECTION_PENALTY_TIMEOUT_MINUTES_LIST_KEY), anyObject())) + .thenReturn(new int[] {PENALTY_TIMEOUT_MIN + additionalTimeoutMin}); + + // Update evaluator and penalize the network + mNetworkEvaluator.reevaluate( + VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES, + SUB_GROUP, + mSubscriptionSnapshot, + mCarrierConfig); + checkPenalizeNetwork(); + + // Verify penalty timeout is changed + mTestLooper.moveTimeForward(PENALTY_TIMEOUT_MS); + assertNull(mTestLooper.nextMessage()); + mTestLooper.moveTimeForward(TimeUnit.MINUTES.toMillis(additionalTimeoutMin)); + assertNotNull(mTestLooper.nextMessage()); + + // Verify NetworkMetricMonitor is notified + verify(mIpSecPacketLossDetector).setCarrierConfig(any()); + } + + @Test + public void testCompare() throws Exception { + when(mIpSecPacketLossDetector.isValidationFailed()).thenReturn(true); + getMetricMonitorCbCaptor().onValidationResultReceived(); + + final UnderlyingNetworkEvaluator penalized = mNetworkEvaluator; + final UnderlyingNetworkEvaluator notPenalized = newValidUnderlyingNetworkEvaluator(); + + assertEquals(penalized.getPriorityClass(), notPenalized.getPriorityClass()); + + final int result = + UnderlyingNetworkEvaluator.getComparator(mVcnContext) + .compare(penalized, notPenalized); + assertEquals(1, result); + } +} diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp index cf4dd79e3d96..24699bf9a7ff 100644 --- a/tools/aapt2/cmd/Link.cpp +++ b/tools/aapt2/cmd/Link.cpp @@ -56,6 +56,7 @@ #include "java/JavaClassGenerator.h" #include "java/ManifestClassGenerator.h" #include "java/ProguardRules.h" +#include "link/FeatureFlagsFilter.h" #include "link/Linkers.h" #include "link/ManifestFixer.h" #include "link/NoDefaultResourceRemover.h" @@ -1986,6 +1987,19 @@ class Linker { context_->SetNameManglerPolicy(NameManglerPolicy{context_->GetCompilationPackage()}); context_->SetSplitNameDependencies(app_info_.split_name_dependencies); + FeatureFlagsFilterOptions flags_filter_options; + if (context_->GetMinSdkVersion() > SDK_UPSIDE_DOWN_CAKE) { + // For API version > U, PackageManager will dynamically read the flag values and disable + // manifest elements accordingly when parsing the manifest. + // For API version <= U, we remove disabled elements from the manifest with the filter. + flags_filter_options.remove_disabled_elements = false; + flags_filter_options.flags_must_have_value = false; + } + FeatureFlagsFilter flags_filter(options_.feature_flag_values, flags_filter_options); + if (!flags_filter.Consume(context_, manifest_xml.get())) { + return 1; + } + // Override the package ID when it is "android". if (context_->GetCompilationPackage() == "android") { context_->SetPackageId(kAndroidPackageId); @@ -2530,7 +2544,7 @@ int LinkCommand::Action(const std::vector<std::string>& args) { } for (const std::string& arg : all_feature_flags_args) { - if (ParseFeatureFlagsParameter(arg, context.GetDiagnostics(), &options_.feature_flag_values)) { + if (!ParseFeatureFlagsParameter(arg, context.GetDiagnostics(), &options_.feature_flag_values)) { return 1; } } diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h index 26713fd92264..dc18b1ccda60 100644 --- a/tools/aapt2/cmd/Link.h +++ b/tools/aapt2/cmd/Link.h @@ -330,7 +330,11 @@ class LinkCommand : public Command { "should only be used together with the --static-lib flag.", &options_.merge_only); AddOptionalSwitch("-v", "Enables verbose logging.", &verbose_); - AddOptionalFlagList("--feature-flags", "Placeholder, to be implemented.", &feature_flags_args_); + AddOptionalFlagList("--feature-flags", + "Specify the values of feature flags. The pairs in the argument\n" + "are separated by ',' and the name is separated from the value by '='.\n" + "Example: \"flag1=true,flag2=false,flag3=\" (flag3 has no given value).", + &feature_flags_args_); } int Action(const std::vector<std::string>& args) override; diff --git a/tools/aapt2/cmd/Link_test.cpp b/tools/aapt2/cmd/Link_test.cpp index 725a1b86f616..7ceb351aaa6f 100644 --- a/tools/aapt2/cmd/Link_test.cpp +++ b/tools/aapt2/cmd/Link_test.cpp @@ -16,11 +16,10 @@ #include "Link.h" -#include <android-base/file.h> - -#include "AppInfo.h" #include "Diagnostics.h" #include "LoadedApk.h" +#include "android-base/file.h" +#include "android-base/stringprintf.h" #include "test/Test.h" using testing::Eq; @@ -993,4 +992,213 @@ TEST_F(LinkTest, LocaleConfigWrongLocaleFormat) { ASSERT_FALSE(Link(link_args, &diag)); } +static void BuildSDKWithFeatureFlagAttr(const std::string& apk_path, const std::string& java_path, + CommandTestFixture* fixture, android::IDiagnostics* diag) { + const std::string android_values = + R"(<resources> + <staging-public-group type="attr" first-id="0x01fe0063"> + <public name="featureFlag" /> + </staging-public-group> + <attr name="featureFlag" format="string" /> + </resources>)"; + + SourceXML source_xml{.res_file_path = "/res/values/values.xml", .file_contents = android_values}; + BuildSDK({source_xml}, apk_path, java_path, fixture, diag); +} + +TEST_F(LinkTest, FeatureFlagDisabled_SdkAtMostUDC) { + StdErrDiagnostics diag; + const std::string android_apk = GetTestPath("android.apk"); + const std::string android_java = GetTestPath("android-java"); + BuildSDKWithFeatureFlagAttr(android_apk, android_java, this, &diag); + + const std::string manifest_contents = android::base::StringPrintf( + R"(<uses-sdk android:minSdkVersion="%d" />" + <permission android:name="FOO" android:featureFlag="flag" />)", + SDK_UPSIDE_DOWN_CAKE); + auto app_manifest = ManifestBuilder(this) + .SetPackageName("com.example.app") + .AddContents(manifest_contents) + .Build(); + + auto app_link_args = LinkCommandBuilder(this) + .SetManifestFile(app_manifest) + .AddParameter("-I", android_apk) + .AddParameter("--feature-flags", "flag=false"); + + const std::string app_apk = GetTestPath("app.apk"); + BuildApk({}, app_apk, std::move(app_link_args), this, &diag); + + // Permission element should be removed if flag is disabled + auto apk = LoadedApk::LoadApkFromPath(app_apk, &diag); + ASSERT_THAT(apk, NotNull()); + auto apk_manifest = apk->GetManifest(); + ASSERT_THAT(apk_manifest, NotNull()); + auto root = apk_manifest->root.get(); + ASSERT_THAT(root, NotNull()); + auto maybe_removed = root->FindChild({}, "permission"); + ASSERT_THAT(maybe_removed, IsNull()); +} + +TEST_F(LinkTest, FeatureFlagEnabled_SdkAtMostUDC) { + StdErrDiagnostics diag; + const std::string android_apk = GetTestPath("android.apk"); + const std::string android_java = GetTestPath("android-java"); + BuildSDKWithFeatureFlagAttr(android_apk, android_java, this, &diag); + + const std::string manifest_contents = android::base::StringPrintf( + R"(<uses-sdk android:minSdkVersion="%d" />" + <permission android:name="FOO" android:featureFlag="flag" />)", + SDK_UPSIDE_DOWN_CAKE); + auto app_manifest = ManifestBuilder(this) + .SetPackageName("com.example.app") + .AddContents(manifest_contents) + .Build(); + + auto app_link_args = LinkCommandBuilder(this) + .SetManifestFile(app_manifest) + .AddParameter("-I", android_apk) + .AddParameter("--feature-flags", "flag=true"); + + const std::string app_apk = GetTestPath("app.apk"); + BuildApk({}, app_apk, std::move(app_link_args), this, &diag); + + // Permission element should be kept if flag is enabled + auto apk = LoadedApk::LoadApkFromPath(app_apk, &diag); + ASSERT_THAT(apk, NotNull()); + auto apk_manifest = apk->GetManifest(); + ASSERT_THAT(apk_manifest, NotNull()); + auto root = apk_manifest->root.get(); + ASSERT_THAT(root, NotNull()); + auto maybe_removed = root->FindChild({}, "permission"); + ASSERT_THAT(maybe_removed, NotNull()); +} + +TEST_F(LinkTest, FeatureFlagWithNoValue_SdkAtMostUDC) { + StdErrDiagnostics diag; + const std::string android_apk = GetTestPath("android.apk"); + const std::string android_java = GetTestPath("android-java"); + BuildSDKWithFeatureFlagAttr(android_apk, android_java, this, &diag); + + const std::string manifest_contents = android::base::StringPrintf( + R"(<uses-sdk android:minSdkVersion="%d" />" + <permission android:name="FOO" android:featureFlag="flag" />)", + SDK_UPSIDE_DOWN_CAKE); + auto app_manifest = ManifestBuilder(this) + .SetPackageName("com.example.app") + .AddContents(manifest_contents) + .Build(); + + auto app_link_args = LinkCommandBuilder(this) + .SetManifestFile(app_manifest) + .AddParameter("-I", android_apk) + .AddParameter("--feature-flags", "flag="); + + // Flags must have values if <= UDC + const std::string app_apk = GetTestPath("app.apk"); + ASSERT_FALSE(Link(app_link_args.Build(app_apk), &diag)); +} + +TEST_F(LinkTest, FeatureFlagDisabled_SdkAfterUDC) { + StdErrDiagnostics diag; + const std::string android_apk = GetTestPath("android.apk"); + const std::string android_java = GetTestPath("android-java"); + BuildSDKWithFeatureFlagAttr(android_apk, android_java, this, &diag); + + const std::string manifest_contents = android::base::StringPrintf( + R"(<uses-sdk android:minSdkVersion="%d" />" + <permission android:name="FOO" android:featureFlag="flag" />)", + SDK_CUR_DEVELOPMENT); + auto app_manifest = ManifestBuilder(this) + .SetPackageName("com.example.app") + .AddContents(manifest_contents) + .Build(); + + auto app_link_args = LinkCommandBuilder(this) + .SetManifestFile(app_manifest) + .AddParameter("-I", android_apk) + .AddParameter("--feature-flags", "flag=false"); + + const std::string app_apk = GetTestPath("app.apk"); + BuildApk({}, app_apk, std::move(app_link_args), this, &diag); + + // Permission element should be kept if > UDC, regardless of flag value + auto apk = LoadedApk::LoadApkFromPath(app_apk, &diag); + ASSERT_THAT(apk, NotNull()); + auto apk_manifest = apk->GetManifest(); + ASSERT_THAT(apk_manifest, NotNull()); + auto root = apk_manifest->root.get(); + ASSERT_THAT(root, NotNull()); + auto maybe_removed = root->FindChild({}, "permission"); + ASSERT_THAT(maybe_removed, NotNull()); +} + +TEST_F(LinkTest, FeatureFlagEnabled_SdkAfterUDC) { + StdErrDiagnostics diag; + const std::string android_apk = GetTestPath("android.apk"); + const std::string android_java = GetTestPath("android-java"); + BuildSDKWithFeatureFlagAttr(android_apk, android_java, this, &diag); + + const std::string manifest_contents = android::base::StringPrintf( + R"(<uses-sdk android:minSdkVersion="%d" />" + <permission android:name="FOO" android:featureFlag="flag" />)", + SDK_CUR_DEVELOPMENT); + auto app_manifest = ManifestBuilder(this) + .SetPackageName("com.example.app") + .AddContents(manifest_contents) + .Build(); + + auto app_link_args = LinkCommandBuilder(this) + .SetManifestFile(app_manifest) + .AddParameter("-I", android_apk) + .AddParameter("--feature-flags", "flag=true"); + + const std::string app_apk = GetTestPath("app.apk"); + BuildApk({}, app_apk, std::move(app_link_args), this, &diag); + + // Permission element should be kept if > UDC, regardless of flag value + auto apk = LoadedApk::LoadApkFromPath(app_apk, &diag); + ASSERT_THAT(apk, NotNull()); + auto apk_manifest = apk->GetManifest(); + ASSERT_THAT(apk_manifest, NotNull()); + auto root = apk_manifest->root.get(); + ASSERT_THAT(root, NotNull()); + auto maybe_removed = root->FindChild({}, "permission"); + ASSERT_THAT(maybe_removed, NotNull()); +} + +TEST_F(LinkTest, FeatureFlagWithNoValue_SdkAfterUDC) { + StdErrDiagnostics diag; + const std::string android_apk = GetTestPath("android.apk"); + const std::string android_java = GetTestPath("android-java"); + BuildSDKWithFeatureFlagAttr(android_apk, android_java, this, &diag); + + const std::string manifest_contents = android::base::StringPrintf( + R"(<uses-sdk android:minSdkVersion="%d" />" + <permission android:name="FOO" android:featureFlag="flag" />)", + SDK_CUR_DEVELOPMENT); + auto app_manifest = ManifestBuilder(this) + .SetPackageName("com.example.app") + .AddContents(manifest_contents) + .Build(); + + auto app_link_args = LinkCommandBuilder(this) + .SetManifestFile(app_manifest) + .AddParameter("-I", android_apk) + .AddParameter("--feature-flags", "flag="); + + const std::string app_apk = GetTestPath("app.apk"); + BuildApk({}, app_apk, std::move(app_link_args), this, &diag); + + // Permission element should be kept if > UDC, regardless of flag value + auto apk = LoadedApk::LoadApkFromPath(app_apk, &diag); + ASSERT_THAT(apk, NotNull()); + auto apk_manifest = apk->GetManifest(); + ASSERT_THAT(apk_manifest, NotNull()); + auto root = apk_manifest->root.get(); + ASSERT_THAT(root, NotNull()); + auto maybe_removed = root->FindChild({}, "permission"); + ASSERT_THAT(maybe_removed, NotNull()); +} + } // namespace aapt diff --git a/tools/aapt2/integration-tests/MergeOnlyTest/Android.mk b/tools/aapt2/integration-tests/MergeOnlyTest/Android.mk deleted file mode 100644 index 6361f9b8ae7d..000000000000 --- a/tools/aapt2/integration-tests/MergeOnlyTest/Android.mk +++ /dev/null @@ -1,2 +0,0 @@ -LOCAL_PATH := $(call my-dir) -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/tools/aapt2/integration-tests/MergeOnlyTest/App/Android.mk b/tools/aapt2/integration-tests/MergeOnlyTest/App/Android.mk deleted file mode 100644 index 27b6068632f3..000000000000 --- a/tools/aapt2/integration-tests/MergeOnlyTest/App/Android.mk +++ /dev/null @@ -1,32 +0,0 @@ -// -// Copyright (C) 2019 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_USE_AAPT2 := true -LOCAL_AAPT_NAMESPACES := true -LOCAL_PACKAGE_NAME := AaptTestMergeOnly_App -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE -LOCAL_SDK_VERSION := current -LOCAL_EXPORT_PACKAGE_RESOURCES := true -LOCAL_MODULE_TAGS := tests -LOCAL_STATIC_ANDROID_LIBRARIES := \ - AaptTestMergeOnly_LeafLib \ - AaptTestMergeOnly_LocalLib -include $(BUILD_PACKAGE) diff --git a/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/Android.mk b/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/Android.mk deleted file mode 100644 index 699ad79ecf1a..000000000000 --- a/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/Android.mk +++ /dev/null @@ -1,31 +0,0 @@ -// -// Copyright (C) 2019 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_USE_AAPT2 := true -LOCAL_AAPT_NAMESPACES := true -LOCAL_MODULE := AaptTestMergeOnly_LocalLib -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE -LOCAL_SDK_VERSION := current -LOCAL_MODULE_TAGS := tests -LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res -LOCAL_MIN_SDK_VERSION := 21 -LOCAL_AAPT_FLAGS := --merge-only -include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/tools/aapt2/integration-tests/NamespaceTest/Android.mk b/tools/aapt2/integration-tests/NamespaceTest/Android.mk deleted file mode 100644 index 6361f9b8ae7d..000000000000 --- a/tools/aapt2/integration-tests/NamespaceTest/Android.mk +++ /dev/null @@ -1,2 +0,0 @@ -LOCAL_PATH := $(call my-dir) -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk b/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk deleted file mode 100644 index 98b74403a7ff..000000000000 --- a/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk +++ /dev/null @@ -1,33 +0,0 @@ -# -# Copyright (C) 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_USE_AAPT2 := true -LOCAL_AAPT_NAMESPACES := true -LOCAL_PACKAGE_NAME := AaptTestNamespace_App -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE -LOCAL_SDK_VERSION := current -LOCAL_EXPORT_PACKAGE_RESOURCES := true -LOCAL_MODULE_TAGS := tests -LOCAL_SRC_FILES := $(call all-java-files-under,src) -LOCAL_STATIC_ANDROID_LIBRARIES := \ - AaptTestNamespace_LibOne \ - AaptTestNamespace_LibTwo -include $(BUILD_PACKAGE) diff --git a/tools/aapt2/integration-tests/NamespaceTest/LibOne/Android.mk b/tools/aapt2/integration-tests/NamespaceTest/LibOne/Android.mk deleted file mode 100644 index dd4170234258..000000000000 --- a/tools/aapt2/integration-tests/NamespaceTest/LibOne/Android.mk +++ /dev/null @@ -1,33 +0,0 @@ -# -# Copyright (C) 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_USE_AAPT2 := true -LOCAL_AAPT_NAMESPACES := true -LOCAL_MODULE := AaptTestNamespace_LibOne -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE -LOCAL_SDK_VERSION := current -LOCAL_MODULE_TAGS := tests -LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res -LOCAL_MIN_SDK_VERSION := 21 - -# We need this to retain the R.java generated for this library. -LOCAL_JAR_EXCLUDE_FILES := none -include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/tools/aapt2/integration-tests/NamespaceTest/LibTwo/Android.mk b/tools/aapt2/integration-tests/NamespaceTest/LibTwo/Android.mk deleted file mode 100644 index 0d11bcbda64d..000000000000 --- a/tools/aapt2/integration-tests/NamespaceTest/LibTwo/Android.mk +++ /dev/null @@ -1,34 +0,0 @@ -# -# Copyright (C) 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_USE_AAPT2 := true -LOCAL_AAPT_NAMESPACES := true -LOCAL_MODULE := AaptTestNamespace_LibTwo -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE -LOCAL_SDK_VERSION := current -LOCAL_MODULE_TAGS := tests -LOCAL_SRC_FILES := $(call all-java-files-under,src) -LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res -LOCAL_MIN_SDK_VERSION := 21 - -# We need this to retain the R.java generated for this library. -LOCAL_JAR_EXCLUDE_FILES := none -include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/tools/aapt2/integration-tests/NamespaceTest/Split/Android.mk b/tools/aapt2/integration-tests/NamespaceTest/Split/Android.mk deleted file mode 100644 index 30375728c9e0..000000000000 --- a/tools/aapt2/integration-tests/NamespaceTest/Split/Android.mk +++ /dev/null @@ -1,32 +0,0 @@ -# -# Copyright (C) 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_USE_AAPT2 := true -LOCAL_AAPT_NAMESPACES := true -LOCAL_PACKAGE_NAME := AaptTestNamespace_Split -LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS := notice -LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE -LOCAL_SDK_VERSION := current -LOCAL_MODULE_TAGS := tests -LOCAL_SRC_FILES := $(call all-java-files-under,src) -LOCAL_APK_LIBRARIES := AaptTestNamespace_App -LOCAL_RES_LIBRARIES := AaptTestNamespace_App -LOCAL_AAPT_FLAGS := --package-id 0x80 --rename-manifest-package com.android.aapt.namespace.app -include $(BUILD_PACKAGE) diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp index 93c1b61f9a57..02e4beaed949 100644 --- a/tools/aapt2/util/Files.cpp +++ b/tools/aapt2/util/Files.cpp @@ -251,10 +251,13 @@ bool AppendArgsFromFile(StringPiece path, std::vector<std::string>* out_arglist, return false; } - for (StringPiece line : util::Tokenize(contents, ' ')) { + for (StringPiece line : util::Tokenize(contents, '\n')) { line = util::TrimWhitespace(line); - if (!line.empty()) { - out_arglist->emplace_back(line); + for (StringPiece arg : util::Tokenize(line, ' ')) { + arg = util::TrimWhitespace(arg); + if (!arg.empty()) { + out_arglist->emplace_back(arg); + } } } return true; @@ -270,10 +273,13 @@ bool AppendSetArgsFromFile(StringPiece path, std::unordered_set<std::string>* ou return false; } - for (StringPiece line : util::Tokenize(contents, ' ')) { + for (StringPiece line : util::Tokenize(contents, '\n')) { line = util::TrimWhitespace(line); - if (!line.empty()) { - out_argset->emplace(line); + for (StringPiece arg : util::Tokenize(line, ' ')) { + arg = util::TrimWhitespace(arg); + if (!arg.empty()) { + out_argset->emplace(arg); + } } } return true; diff --git a/tools/aapt2/util/Files_test.cpp b/tools/aapt2/util/Files_test.cpp index 6c380808c0df..618a3e0d86ae 100644 --- a/tools/aapt2/util/Files_test.cpp +++ b/tools/aapt2/util/Files_test.cpp @@ -25,6 +25,9 @@ using ::android::base::StringPrintf; +using ::testing::ElementsAre; +using ::testing::UnorderedElementsAre; + namespace aapt { namespace file { @@ -34,9 +37,11 @@ constexpr const char sTestDirSep = '\\'; constexpr const char sTestDirSep = '/'; #endif -class FilesTest : public ::testing::Test { +class FilesTest : public TestDirectoryFixture { public: void SetUp() override { + TestDirectoryFixture::SetUp(); + std::stringstream builder; builder << "hello" << sDirSep << "there"; expected_path_ = builder.str(); @@ -66,6 +71,42 @@ TEST_F(FilesTest, AppendPathWithLeadingOrTrailingSeparators) { EXPECT_EQ(expected_path_, base); } +TEST_F(FilesTest, AppendArgsFromFile) { + const std::string args_file = GetTestPath("args.txt"); + WriteFile(args_file, + " \n" + "arg1 arg2 arg3 \n" + " arg4 arg5"); + std::vector<std::string> args; + std::string error; + ASSERT_TRUE(AppendArgsFromFile(args_file, &args, &error)); + EXPECT_THAT(args, ElementsAre("arg1", "arg2", "arg3", "arg4", "arg5")); +} + +TEST_F(FilesTest, AppendArgsFromFile_InvalidFile) { + std::vector<std::string> args; + std::string error; + ASSERT_FALSE(AppendArgsFromFile(GetTestPath("not_found.txt"), &args, &error)); +} + +TEST_F(FilesTest, AppendSetArgsFromFile) { + const std::string args_file = GetTestPath("args.txt"); + WriteFile(args_file, + " \n" + "arg2 arg4 arg1 \n" + " arg5 arg3"); + std::unordered_set<std::string> args; + std::string error; + ASSERT_TRUE(AppendSetArgsFromFile(args_file, &args, &error)); + EXPECT_THAT(args, UnorderedElementsAre("arg1", "arg2", "arg3", "arg4", "arg5")); +} + +TEST_F(FilesTest, AppendSetArgsFromFile_InvalidFile) { + std::unordered_set<std::string> args; + std::string error; + ASSERT_FALSE(AppendSetArgsFromFile(GetTestPath("not_found.txt"), &args, &error)); +} + #ifdef _WIN32 TEST_F(FilesTest, WindowsMkdirsLongPath) { // Creating directory paths longer than the Windows maximum path length (260 charatcers) should |