diff options
36 files changed, 1837 insertions, 258 deletions
diff --git a/Android.bp b/Android.bp index 0c9b82e5990e..62d2632a8af6 100644 --- a/Android.bp +++ b/Android.bp @@ -506,7 +506,7 @@ metalava_framework_docs_args = "" + "--hide Todo " + "--hide Typo " + "--hide UnavailableSymbol " + - "--manifest $(location core/res/AndroidManifest.xml) " + "--manifest $(location :frameworks-base-core-AndroidManifest.xml) " packages_to_document = [ "android", @@ -543,7 +543,7 @@ stubs_defaults { sdk_version: "none", system_modules: "none", java_version: "1.8", - arg_files: ["core/res/AndroidManifest.xml"], + arg_files: [":frameworks-base-core-AndroidManifest.xml"], aidl: { local_include_dirs: [ "media/aidl", @@ -619,12 +619,3 @@ build = [ "ProtoLibraries.bp", "TestProtoLibraries.bp", ] - -java_api_contribution { - name: "api-stubs-docs-non-updatable-public-stubs", - api_surface: "public", - api_file: "core/api/current.txt", - visibility: [ - "//build/orchestrator/apis", - ], -} diff --git a/StubLibraries.bp b/StubLibraries.bp index 38413c22a3d5..b005591980c0 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -36,8 +36,8 @@ droidstubs { args: metalava_framework_docs_args, check_api: { current: { - api_file: "core/api/current.txt", - removed_api_file: "core/api/removed.txt", + api_file: ":non-updatable-current.txt", + removed_api_file: ":non-updatable-removed.txt", }, last_released: { api_file: ":android-non-updatable.api.public.latest", @@ -88,8 +88,8 @@ droidstubs { args: metalava_framework_docs_args + priv_apps, check_api: { current: { - api_file: "core/api/system-current.txt", - removed_api_file: "core/api/system-removed.txt", + api_file: ":non-updatable-system-current.txt", + removed_api_file: ":non-updatable-system-removed.txt", }, last_released: { api_file: ":android-non-updatable.api.system.latest", @@ -99,7 +99,7 @@ droidstubs { api_lint: { enabled: true, new_since: ":android.api.system.latest", - baseline_file: "core/api/system-lint-baseline.txt", + baseline_file: ":non-updatable-system-lint-baseline.txt", }, }, dists: [ @@ -127,12 +127,12 @@ droidstubs { args: metalava_framework_docs_args + test + priv_apps_in_stubs, check_api: { current: { - api_file: "core/api/test-current.txt", - removed_api_file: "core/api/test-removed.txt", + api_file: ":non-updatable-test-current.txt", + removed_api_file: ":non-updatable-test-removed.txt", }, api_lint: { enabled: true, - baseline_file: "core/api/test-lint-baseline.txt", + baseline_file: ":non-updatable-test-lint-baseline.txt", }, }, dists: [ @@ -172,8 +172,8 @@ droidstubs { args: metalava_framework_docs_args + priv_apps_in_stubs + module_libs, check_api: { current: { - api_file: "core/api/module-lib-current.txt", - removed_api_file: "core/api/module-lib-removed.txt", + api_file: ":non-updatable-module-lib-current.txt", + removed_api_file: ":non-updatable-module-lib-removed.txt", }, last_released: { api_file: ":android-non-updatable.api.module-lib.latest", @@ -183,7 +183,7 @@ droidstubs { api_lint: { enabled: true, new_since: ":android.api.module-lib.latest", - baseline_file: "core/api/module-lib-lint-baseline.txt", + baseline_file: ":non-updatable-module-lib-lint-baseline.txt", }, }, dists: [ @@ -364,15 +364,15 @@ java_library { java_library { name: "android_system_server_stubs_current", - defaults: ["android_stubs_dists_default"], + defaults: [ + "android.jar_defaults", + "android_stubs_dists_default", + ], srcs: [":services-non-updatable-stubs"], installable: false, static_libs: [ "android_module_lib_stubs_current", ], - sdk_version: "none", - system_modules: "none", - java_version: "1.8", dist: { dir: "apistubs/android/system-server", }, @@ -575,20 +575,7 @@ droidstubs { droidstubs { name: "hwbinder-stubs-docs", - srcs: [ - "core/java/android/os/HidlSupport.java", - "core/java/android/os/HidlMemory.java", - "core/java/android/os/HwBinder.java", - "core/java/android/os/HwBlob.java", - "core/java/android/os/HwParcel.java", - "core/java/android/os/IHwBinder.java", - "core/java/android/os/IHwInterface.java", - "core/java/android/os/DeadObjectException.java", - "core/java/android/os/DeadSystemException.java", - "core/java/android/os/NativeHandle.java", - "core/java/android/os/RemoteException.java", - "core/java/android/util/AndroidException.java", - ], + srcs: [":hwbinder-stubs-srcs"], libs: ["framework-annotations-lib"], installable: false, sdk_version: "core_platform", @@ -610,12 +597,3 @@ java_library { ], visibility: ["//visibility:public"], } - -java_api_contribution { - name: "frameworks-base-core-api-module-lib-stubs", - api_surface: "module-lib", - api_file: "core/api/module-lib-current.txt", - visibility: [ - "//build/orchestrator/apis", - ], -} diff --git a/api/Android.bp b/api/Android.bp index a2b8038842cc..4cb52bc2d29a 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -31,10 +31,12 @@ bootstrap_go_package { "blueprint", "soong", "soong-android", + "soong-bp2build", "soong-genrule", "soong-java", ], srcs: ["api.go"], + testSrcs: ["api_test.go"], pluginFor: ["soong_build"], } @@ -255,3 +257,15 @@ genrule { out: ["combined-removed-dex.txt"], cmd: "$(location gen_combined_removed_dex.sh) $(location metalava) $(genDir) $(in) > $(out)", } + +java_genrule { + name: "api_fingerprint", + srcs: [ + ":frameworks-base-api-current.txt", + ":frameworks-base-api-system-current.txt", + ":frameworks-base-api-module-lib-current.txt", + ":frameworks-base-api-system-server-current.txt", + ], + out: ["api_fingerprint.txt"], + cmd: "cat $(in) | md5sum | cut -d' ' -f1 > $(out)", +} diff --git a/api/api.go b/api/api.go index 077ab9679ec9..25d97282035e 100644 --- a/api/api.go +++ b/api/api.go @@ -20,6 +20,7 @@ import ( "github.com/google/blueprint/proptools" "android/soong/android" + "android/soong/bazel" "android/soong/genrule" "android/soong/java" ) @@ -30,6 +31,7 @@ const i18n = "i18n.module.public.api" const virtualization = "framework-virtualization" var core_libraries_modules = []string{art, conscrypt, i18n} + // List of modules that are not yet updatable, and hence they can still compile // against hidden APIs. These modules are filtered out when building the // updatable-framework-module-impl (because updatable-framework-module-impl is @@ -59,6 +61,7 @@ type CombinedApisProperties struct { type CombinedApis struct { android.ModuleBase + android.BazelModuleBase properties CombinedApisProperties } @@ -99,6 +102,19 @@ type fgProps struct { Visibility []string } +type Bazel_module struct { + Bp2build_available *bool +} +type bazelProperties struct { + *Bazel_module +} + +var bp2buildNotAvailable = bazelProperties{ + &Bazel_module{ + Bp2build_available: proptools.BoolPtr(false), + }, +} + // Struct to pass parameters for the various merged [current|removed].txt file modules we create. type MergedTxtDefinition struct { // "current.txt" or "removed.txt" @@ -144,7 +160,7 @@ func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition) { }, } props.Visibility = []string{"//visibility:public"} - ctx.CreateModule(genrule.GenRuleFactory, &props) + ctx.CreateModule(genrule.GenRuleFactory, &props, &bp2buildNotAvailable) } func createMergedAnnotationsFilegroups(ctx android.LoadHookContext, modules, system_server_modules []string) { @@ -174,7 +190,7 @@ func createMergedAnnotationsFilegroups(ctx android.LoadHookContext, modules, sys props := fgProps{} props.Name = proptools.StringPtr(i.name) props.Srcs = createSrcs(i.modules, i.tag) - ctx.CreateModule(android.FileGroupFactory, &props) + ctx.CreateModule(android.FileGroupFactory, &props, &bp2buildNotAvailable) } } @@ -223,7 +239,7 @@ func createFilteredApiVersions(ctx android.LoadHookContext, modules []string) { props.Tools = []string{"api_versions_trimmer"} props.Cmd = proptools.StringPtr("$(location api_versions_trimmer) $(out) $(in)") props.Dists = []android.Dist{{Targets: []string{"sdk"}}} - ctx.CreateModule(genrule.GenRuleFactory, &props) + ctx.CreateModule(genrule.GenRuleFactory, &props, &bp2buildNotAvailable) } } @@ -315,7 +331,7 @@ func createPublicStubsSourceFilegroup(ctx android.LoadHookContext, modules []str props.Name = proptools.StringPtr("all-modules-public-stubs-source") props.Srcs = createSrcs(modules, "{.public.stubs.source}") props.Visibility = []string{"//frameworks/base"} - ctx.CreateModule(android.FileGroupFactory, &props) + ctx.CreateModule(android.FileGroupFactory, &props, &bp2buildNotAvailable) } func createMergedTxts(ctx android.LoadHookContext, bootclasspath, system_server_classpath []string) { @@ -389,9 +405,57 @@ func combinedApisModuleFactory() android.Module { module.AddProperties(&module.properties) android.InitAndroidModule(module) android.AddLoadHook(module, func(ctx android.LoadHookContext) { module.createInternalModules(ctx) }) + android.InitBazelModule(module) return module } +type bazelCombinedApisAttributes struct { + Scope bazel.StringAttribute + Base bazel.LabelAttribute + Deps bazel.LabelListAttribute +} + +// combined_apis bp2build converter +func (a *CombinedApis) ConvertWithBp2build(ctx android.TopDownMutatorContext) { + basePrefix := "non-updatable" + scopeNames := []string{"public", "system", "module-lib", "system-server"} + scopeToSuffix := map[string]string{ + "public": "-current.txt", + "system": "-system-current.txt", + "module-lib": "-module-lib-current.txt", + "system-server": "-system-server-current.txt", + } + + for _, scopeName := range scopeNames{ + suffix := scopeToSuffix[scopeName] + name := a.Name() + suffix + + var scope bazel.StringAttribute + scope.SetValue(scopeName) + + var base bazel.LabelAttribute + base.SetValue(android.BazelLabelForModuleDepSingle(ctx, basePrefix+suffix)) + + var deps bazel.LabelListAttribute + classpath := a.properties.Bootclasspath + if scopeName == "system-server" { + classpath = a.properties.System_server_classpath + } + deps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, classpath)) + + attrs := bazelCombinedApisAttributes{ + Scope: scope, + Base: base, + Deps: deps, + } + props := bazel.BazelTargetModuleProperties{ + Rule_class: "merged_txts", + Bzl_load_location: "//build/bazel/rules/java:merged_txts.bzl", + } + ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: name}, &attrs) + } +} + // Various utility methods below. // Creates an array of ":<m><tag>" for each m in <modules>. diff --git a/api/api_test.go b/api/api_test.go new file mode 100644 index 000000000000..15b695ca0d36 --- /dev/null +++ b/api/api_test.go @@ -0,0 +1,68 @@ +// 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 api + +import ( + "testing" + + "android/soong/android" + "android/soong/bp2build" +) + +func runCombinedApisTestCaseWithRegistrationCtxFunc(t *testing.T, tc bp2build.Bp2buildTestCase, registrationCtxFunc func(ctx android.RegistrationContext)) { + t.Helper() + (&tc).ModuleTypeUnderTest = "combined_apis" + (&tc).ModuleTypeUnderTestFactory = combinedApisModuleFactory + bp2build.RunBp2BuildTestCase(t, registrationCtxFunc, tc) +} + +func runCombinedApisTestCase(t *testing.T, tc bp2build.Bp2buildTestCase) { + t.Helper() + runCombinedApisTestCaseWithRegistrationCtxFunc(t, tc, func(ctx android.RegistrationContext) {}) +} + +func TestCombinedApisGeneral(t *testing.T) { + runCombinedApisTestCase(t, bp2build.Bp2buildTestCase{ + Description: "combined_apis, general case", + Blueprint: `combined_apis { + name: "foo", + bootclasspath: ["bcp"], + system_server_classpath: ["ssc"], +} +`, + ExpectedBazelTargets: []string{ + bp2build.MakeBazelTargetNoRestrictions("merged_txts", "foo-current.txt", bp2build.AttrNameToString{ + "scope": `"public"`, + "base": `":non-updatable-current.txt__BP2BUILD__MISSING__DEP"`, + "deps": `[":bcp__BP2BUILD__MISSING__DEP"]`, + }), + bp2build.MakeBazelTargetNoRestrictions("merged_txts", "foo-system-current.txt", bp2build.AttrNameToString{ + "scope": `"system"`, + "base": `":non-updatable-system-current.txt__BP2BUILD__MISSING__DEP"`, + "deps": `[":bcp__BP2BUILD__MISSING__DEP"]`, + }), + bp2build.MakeBazelTargetNoRestrictions("merged_txts", "foo-module-lib-current.txt", bp2build.AttrNameToString{ + "scope": `"module-lib"`, + "base": `":non-updatable-module-lib-current.txt__BP2BUILD__MISSING__DEP"`, + "deps": `[":bcp__BP2BUILD__MISSING__DEP"]`, + }), + bp2build.MakeBazelTargetNoRestrictions("merged_txts", "foo-system-server-current.txt", bp2build.AttrNameToString{ + "scope": `"system-server"`, + "base": `":non-updatable-system-server-current.txt__BP2BUILD__MISSING__DEP"`, + "deps": `[":ssc__BP2BUILD__MISSING__DEP"]`, + }), + }, + }) +} diff --git a/core/api/Android.bp b/core/api/Android.bp index 114a957674ae..71a2ca2903f6 100644 --- a/core/api/Android.bp +++ b/core/api/Android.bp @@ -13,7 +13,10 @@ // limitations under the License. package { - default_visibility: ["//visibility:private"], + default_visibility: [ + "//frameworks/base", + "//frameworks/base/api", + ], // See: http://go/android-license-faq // A large-scale-change added 'default_applicable_licenses' to import // all of the 'license_kinds' from "frameworks_base_license" @@ -27,31 +30,33 @@ package { filegroup { name: "non-updatable-current.txt", srcs: ["current.txt"], - visibility: ["//frameworks/base/api"], } filegroup { name: "non-updatable-removed.txt", srcs: ["removed.txt"], - visibility: ["//frameworks/base/api"], } filegroup { name: "non-updatable-system-current.txt", srcs: ["system-current.txt"], - visibility: ["//frameworks/base/api"], } filegroup { name: "non-updatable-system-removed.txt", srcs: ["system-removed.txt"], - visibility: ["//frameworks/base/api"], +} + +filegroup { + name: "non-updatable-system-lint-baseline.txt", + srcs: ["system-lint-baseline.txt"], } filegroup { name: "non-updatable-module-lib-current.txt", srcs: ["module-lib-current.txt"], visibility: [ + "//frameworks/base", "//frameworks/base/api", "//cts/tests/signature/api", ], @@ -61,7 +66,46 @@ filegroup { name: "non-updatable-module-lib-removed.txt", srcs: ["module-lib-removed.txt"], visibility: [ + "//frameworks/base", "//frameworks/base/api", "//cts/tests/signature/api", ], } + +filegroup { + name: "non-updatable-module-lib-lint-baseline.txt", + srcs: ["module-lib-lint-baseline.txt"], +} + +filegroup { + name: "non-updatable-test-current.txt", + srcs: ["test-current.txt"], +} + +filegroup { + name: "non-updatable-test-removed.txt", + srcs: ["test-removed.txt"], +} + +filegroup { + name: "non-updatable-test-lint-baseline.txt", + srcs: ["test-lint-baseline.txt"], +} + +java_api_contribution { + name: "api-stubs-docs-non-updatable-public-stubs", + api_surface: "public", + api_file: "current.txt", + visibility: [ + "//build/orchestrator/apis", + ], +} + +java_api_contribution { + name: "frameworks-base-core-api-module-lib-stubs", + api_surface: "module-lib", + api_file: "module-lib-current.txt", + visibility: [ + "//build/orchestrator/apis", + ], +} diff --git a/core/api/current.txt b/core/api/current.txt index c381b16dc414..729d8dfa906a 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -5003,6 +5003,31 @@ package android.app { field @NonNull public static final android.os.Parcelable.Creator<android.app.BackgroundServiceStartNotAllowedException> CREATOR; } + public class BroadcastOptions { + method public void clearDeferralPolicy(); + method public void clearDeliveryGroupMatchingFilter(); + method public void clearDeliveryGroupMatchingKey(); + method public void clearDeliveryGroupPolicy(); + method @NonNull public static android.app.BroadcastOptions fromBundle(@NonNull android.os.Bundle); + method public int getDeferralPolicy(); + method @Nullable public android.content.IntentFilter getDeliveryGroupMatchingFilter(); + method @Nullable public String getDeliveryGroupMatchingKey(); + method public int getDeliveryGroupPolicy(); + method public boolean isShareIdentityEnabled(); + method @NonNull public static android.app.BroadcastOptions makeBasic(); + method @NonNull public android.app.BroadcastOptions setDeferralPolicy(int); + method @NonNull public android.app.BroadcastOptions setDeliveryGroupMatchingFilter(@NonNull android.content.IntentFilter); + method @NonNull public android.app.BroadcastOptions setDeliveryGroupMatchingKey(@NonNull String, @NonNull String); + method @NonNull public android.app.BroadcastOptions setDeliveryGroupPolicy(int); + method @NonNull public android.app.BroadcastOptions setShareIdentityEnabled(boolean); + method @NonNull public android.os.Bundle toBundle(); + field public static final int DEFERRAL_POLICY_DEFAULT = 0; // 0x0 + field public static final int DEFERRAL_POLICY_NONE = 1; // 0x1 + field public static final int DEFERRAL_POLICY_UNTIL_ACTIVE = 2; // 0x2 + field public static final int DELIVERY_GROUP_POLICY_ALL = 0; // 0x0 + field public static final int DELIVERY_GROUP_POLICY_MOST_RECENT = 1; // 0x1 + } + public class DatePickerDialog extends android.app.AlertDialog implements android.widget.DatePicker.OnDateChangedListener android.content.DialogInterface.OnClickListener { ctor public DatePickerDialog(@NonNull android.content.Context); ctor public DatePickerDialog(@NonNull android.content.Context, @StyleRes int); @@ -41276,6 +41301,8 @@ package android.telephony { method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, "carrier privileges"}) public android.os.PersistableBundle getConfigForSubId(int, @NonNull java.lang.String...); method public static boolean isConfigForIdentifiedCarrier(android.os.PersistableBundle); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyConfigChangedForSubId(int); + method public void registerCarrierConfigChangeListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.CarrierConfigManager.CarrierConfigChangeListener); + method public void unregisterCarrierConfigChangeListener(@NonNull android.telephony.CarrierConfigManager.CarrierConfigChangeListener); field public static final String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED"; field public static final int CARRIER_NR_AVAILABILITY_NSA = 1; // 0x1 field public static final int CARRIER_NR_AVAILABILITY_SA = 2; // 0x2 @@ -41590,6 +41617,10 @@ package android.telephony { field public static final String KEY_PREFIX = "bsf."; } + public static interface CarrierConfigManager.CarrierConfigChangeListener { + method public void onCarrierConfigChanged(int, int, int, int); + } + public static final class CarrierConfigManager.Gps { field public static final String KEY_PERSIST_LPP_MODE_BOOL = "gps.persist_lpp_mode_bool"; field public static final String KEY_PREFIX = "gps."; diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 89cae4bd55e4..7eedbc341132 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -761,25 +761,20 @@ package android.app { public class BroadcastOptions { method public void clearRequireCompatChange(); - method public boolean isDeferUntilActive(); - method public boolean isPendingIntentBackgroundActivityLaunchAllowed(); - method public static android.app.BroadcastOptions makeBasic(); + method public int getPendingIntentBackgroundActivityStartMode(); + method @Deprecated public boolean isDeferUntilActive(); + method @Deprecated public boolean isPendingIntentBackgroundActivityLaunchAllowed(); method @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RESPONSE_STATS) public void recordResponseEventWhileInBackground(@IntRange(from=0) long); method @RequiresPermission(android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND) public void setBackgroundActivityStartsAllowed(boolean); - method @NonNull public android.app.BroadcastOptions setDeferUntilActive(boolean); - method public void setDeliveryGroupMatchingFilter(@NonNull android.content.IntentFilter); - method public void setDeliveryGroupMatchingKey(@NonNull String, @NonNull String); - method public void setDeliveryGroupPolicy(int); + method @Deprecated @NonNull public android.app.BroadcastOptions setDeferUntilActive(boolean); method public void setDontSendToRestrictedApps(boolean); - method public void setPendingIntentBackgroundActivityLaunchAllowed(boolean); + method @Deprecated public void setPendingIntentBackgroundActivityLaunchAllowed(boolean); + method @NonNull public android.app.BroadcastOptions setPendingIntentBackgroundActivityStartMode(int); method public void setRequireAllOfPermissions(@Nullable String[]); method public void setRequireCompatChange(long, boolean); method public void setRequireNoneOfPermissions(@Nullable String[]); method @RequiresPermission(anyOf={android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppAllowlist(long, int, int, @Nullable String); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppWhitelistDuration(long); - method public android.os.Bundle toBundle(); - field public static final int DELIVERY_GROUP_POLICY_ALL = 0; // 0x0 - field public static final int DELIVERY_GROUP_POLICY_MOST_RECENT = 1; // 0x1 } public class DownloadManager { diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 2d86051d074e..11ae86edfb2b 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -264,6 +264,7 @@ package android.app { } public class BroadcastOptions { + ctor public BroadcastOptions(); ctor public BroadcastOptions(@NonNull android.os.Bundle); method @Deprecated public int getMaxManifestReceiverApiLevel(); method public long getTemporaryAppAllowlistDuration(); diff --git a/core/java/Android.bp b/core/java/Android.bp index 3921408c8c64..7df3d1e13aa0 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -293,6 +293,28 @@ filegroup { ], } +filegroup { + name: "hwbinder-stubs-srcs", + srcs: [ + "android/os/HidlSupport.java", + "android/os/HidlMemory.java", + "android/os/HwBinder.java", + "android/os/HwBlob.java", + "android/os/HwParcel.java", + "android/os/IHwBinder.java", + "android/os/IHwInterface.java", + "android/os/DeadObjectException.java", + "android/os/DeadSystemException.java", + "android/os/NativeHandle.java", + "android/os/RemoteException.java", + "android/util/AndroidException.java", + ], + visibility: [ + "//frameworks/base", + "//frameworks/base/api", + ], +} + cc_defaults { name: "incremental_default", cflags: [ diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java index 16c5b0845107..aa253f2ebe31 100644 --- a/core/java/android/app/BroadcastOptions.java +++ b/core/java/android/app/BroadcastOptions.java @@ -27,10 +27,12 @@ import android.app.compat.CompatChanges; import android.compat.annotation.ChangeId; import android.compat.annotation.Disabled; import android.compat.annotation.EnabledSince; +import android.content.BroadcastReceiver; import android.content.Intent; import android.content.IntentFilter; import android.os.Build; import android.os.Bundle; +import android.os.BundleMerger; import android.os.PowerExemptionManager; import android.os.PowerExemptionManager.ReasonCode; import android.os.PowerExemptionManager.TempAllowListType; @@ -45,28 +47,43 @@ import java.util.Objects; * Helper class for building an options Bundle that can be used with * {@link android.content.Context#sendBroadcast(android.content.Intent) * Context.sendBroadcast(Intent)} and related methods. - * {@hide} */ -@SystemApi public class BroadcastOptions extends ComponentOptions { + private @Flags int mFlags; private long mTemporaryAppAllowlistDuration; private @TempAllowListType int mTemporaryAppAllowlistType; private @ReasonCode int mTemporaryAppAllowlistReasonCode; private @Nullable String mTemporaryAppAllowlistReason; private int mMinManifestReceiverApiLevel = 0; private int mMaxManifestReceiverApiLevel = Build.VERSION_CODES.CUR_DEVELOPMENT; - private boolean mDontSendToRestrictedApps = false; - private boolean mAllowBackgroundActivityStarts; private String[] mRequireAllOfPermissions; private String[] mRequireNoneOfPermissions; private long mRequireCompatChangeId = CHANGE_INVALID; - private boolean mRequireCompatChangeEnabled = true; - private boolean mIsAlarmBroadcast = false; private long mIdForResponseEvent; private @DeliveryGroupPolicy int mDeliveryGroupPolicy; private @Nullable String mDeliveryGroupMatchingKey; + private @Nullable BundleMerger mDeliveryGroupExtrasMerger; private @Nullable IntentFilter mDeliveryGroupMatchingFilter; - private boolean mIsDeferUntilActive = false; + private @DeferralPolicy int mDeferralPolicy; + + /** @hide */ + @IntDef(flag = true, prefix = { "FLAG_" }, value = { + FLAG_DONT_SEND_TO_RESTRICTED_APPS, + FLAG_ALLOW_BACKGROUND_ACTIVITY_STARTS, + FLAG_REQUIRE_COMPAT_CHANGE_ENABLED, + FLAG_IS_ALARM_BROADCAST, + FLAG_SHARE_IDENTITY, + FLAG_INTERACTIVE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Flags {} + + private static final int FLAG_DONT_SEND_TO_RESTRICTED_APPS = 1 << 0; + private static final int FLAG_ALLOW_BACKGROUND_ACTIVITY_STARTS = 1 << 1; + private static final int FLAG_REQUIRE_COMPAT_CHANGE_ENABLED = 1 << 2; + private static final int FLAG_IS_ALARM_BROADCAST = 1 << 3; + private static final int FLAG_SHARE_IDENTITY = 1 << 4; + private static final int FLAG_INTERACTIVE = 1 << 5; /** * Change ID which is invalid. @@ -96,6 +113,11 @@ public class BroadcastOptions extends ComponentOptions { public static final long CHANGE_ALWAYS_DISABLED = 210856463L; /** + * Corresponds to {@link #mFlags}. + */ + private static final String KEY_FLAGS = "android:broadcast.flags"; + + /** * How long to temporarily put an app on the power allowlist when executing this broadcast * to it. */ @@ -124,18 +146,6 @@ public class BroadcastOptions extends ComponentOptions { = "android:broadcast.maxManifestReceiverApiLevel"; /** - * Corresponds to {@link #setDontSendToRestrictedApps}. - */ - private static final String KEY_DONT_SEND_TO_RESTRICTED_APPS = - "android:broadcast.dontSendToRestrictedApps"; - - /** - * Corresponds to {@link #setBackgroundActivityStartsAllowed}. - */ - private static final String KEY_ALLOW_BACKGROUND_ACTIVITY_STARTS = - "android:broadcast.allowBackgroundActivityStarts"; - - /** * Corresponds to {@link #setRequireAllOfPermissions} * @hide */ @@ -193,13 +203,44 @@ public class BroadcastOptions extends ComponentOptions { "android:broadcast.idForResponseEvent"; /** + * Corresponds to {@link #setDeliveryGroupPolicy(int)}. + */ + private static final String KEY_DELIVERY_GROUP_POLICY = + "android:broadcast.deliveryGroupPolicy"; + + /** + * Corresponds to {@link #setDeliveryGroupMatchingKey(String, String)}. + */ + private static final String KEY_DELIVERY_GROUP_KEY = + "android:broadcast.deliveryGroupMatchingKey"; + + /** + * Corresponds to {@link #setDeliveryGroupExtrasMerger(BundleMerger)}. + */ + private static final String KEY_DELIVERY_GROUP_EXTRAS_MERGER = + "android:broadcast.deliveryGroupExtrasMerger"; + + /** + * Corresponds to {@link #setDeliveryGroupMatchingFilter(IntentFilter)}. + */ + private static final String KEY_DELIVERY_GROUP_MATCHING_FILTER = + "android:broadcast.deliveryGroupMatchingFilter"; + + /** + * Corresponds to {@link #setDeferralPolicy(int)} + */ + private static final String KEY_DEFERRAL_POLICY = + "android:broadcast.deferralPolicy"; + + /** * The list of delivery group policies which specify how multiple broadcasts belonging to * the same delivery group has to be handled. * @hide */ - @IntDef(flag = true, prefix = { "DELIVERY_GROUP_POLICY_" }, value = { + @IntDef(prefix = { "DELIVERY_GROUP_POLICY_" }, value = { DELIVERY_GROUP_POLICY_ALL, DELIVERY_GROUP_POLICY_MOST_RECENT, + DELIVERY_GROUP_POLICY_MERGED, }) @Retention(RetentionPolicy.SOURCE) public @interface DeliveryGroupPolicy {} @@ -207,27 +248,80 @@ public class BroadcastOptions extends ComponentOptions { /** * Delivery group policy that indicates that all the broadcasts in the delivery group * need to be delivered as is. - * - * @hide */ - @SystemApi public static final int DELIVERY_GROUP_POLICY_ALL = 0; /** * Delivery group policy that indicates that only the most recent broadcast in the delivery * group need to be delivered and the rest can be dropped. + */ + public static final int DELIVERY_GROUP_POLICY_MOST_RECENT = 1; + + /** + * Delivery group policy that indicates that the extras data from the broadcasts in the + * delivery group need to be merged into a single broadcast and the rest can be dropped. * * @hide */ - @SystemApi - public static final int DELIVERY_GROUP_POLICY_MOST_RECENT = 1; + public static final int DELIVERY_GROUP_POLICY_MERGED = 2; + + /** {@hide} */ + @IntDef(prefix = { "DEFERRAL_POLICY_" }, value = { + DEFERRAL_POLICY_DEFAULT, + DEFERRAL_POLICY_NONE, + DEFERRAL_POLICY_UNTIL_ACTIVE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface DeferralPolicy {} + + /** + * Deferral policy that indicates no desire has been expressed, and that the + * system should use a reasonable default behavior. + */ + public static final int DEFERRAL_POLICY_DEFAULT = 0; - public static BroadcastOptions makeBasic() { + /** + * Deferral policy that indicates a strong desire that no receiver of this + * broadcast should be deferred. + */ + public static final int DEFERRAL_POLICY_NONE = 1; + + /** + * Deferral policy that indicates a strong desire that each receiver of this + * broadcast should be deferred until that receiver's process is in an + * active (non-cached) state. Whether an app's process state is considered + * active is independent of its standby bucket. + * <p> + * This policy only applies to runtime registered receivers of a broadcast, + * and does not apply to ordered broadcasts, alarm broadcasts, interactive + * broadcasts, or manifest broadcasts. + * <p> + * This policy means that a runtime registered receiver will not typically + * execute until that receiver's process is brought to an active state by + * some other action, such as a job, alarm, or service binding. As a result, + * the receiver may be delayed indefinitely. + * <p> + * When this policy is set on an unordered broadcast with a completion + * callback, the completion callback will run once all eligible processes + * have finished receiving the broadcast. Processes in inactive process + * state are not considered eligible and may not receive the broadcast prior + * to the completion callback. + */ + public static final int DEFERRAL_POLICY_UNTIL_ACTIVE = 2; + + /** + * Creates a basic {@link BroadcastOptions} with no options initially set. + * + * @return an instance of {@code BroadcastOptions} against which options can be set + */ + public static @NonNull BroadcastOptions makeBasic() { BroadcastOptions opts = new BroadcastOptions(); return opts; } - private BroadcastOptions() { + /** @hide */ + @TestApi + public BroadcastOptions() { super(); resetTemporaryAppAllowlist(); } @@ -237,6 +331,7 @@ public class BroadcastOptions extends ComponentOptions { public BroadcastOptions(@NonNull Bundle opts) { super(opts); // Match the logic in toBundle(). + mFlags = opts.getInt(KEY_FLAGS, 0); if (opts.containsKey(KEY_TEMPORARY_APP_ALLOWLIST_DURATION)) { mTemporaryAppAllowlistDuration = opts.getLong(KEY_TEMPORARY_APP_ALLOWLIST_DURATION); mTemporaryAppAllowlistType = opts.getInt(KEY_TEMPORARY_APP_ALLOWLIST_TYPE); @@ -249,15 +344,18 @@ public class BroadcastOptions extends ComponentOptions { mMinManifestReceiverApiLevel = opts.getInt(KEY_MIN_MANIFEST_RECEIVER_API_LEVEL, 0); mMaxManifestReceiverApiLevel = opts.getInt(KEY_MAX_MANIFEST_RECEIVER_API_LEVEL, Build.VERSION_CODES.CUR_DEVELOPMENT); - mDontSendToRestrictedApps = opts.getBoolean(KEY_DONT_SEND_TO_RESTRICTED_APPS, false); - mAllowBackgroundActivityStarts = opts.getBoolean(KEY_ALLOW_BACKGROUND_ACTIVITY_STARTS, - false); mRequireAllOfPermissions = opts.getStringArray(KEY_REQUIRE_ALL_OF_PERMISSIONS); mRequireNoneOfPermissions = opts.getStringArray(KEY_REQUIRE_NONE_OF_PERMISSIONS); mRequireCompatChangeId = opts.getLong(KEY_REQUIRE_COMPAT_CHANGE_ID, CHANGE_INVALID); - mRequireCompatChangeEnabled = opts.getBoolean(KEY_REQUIRE_COMPAT_CHANGE_ENABLED, true); mIdForResponseEvent = opts.getLong(KEY_ID_FOR_RESPONSE_EVENT); - mIsAlarmBroadcast = opts.getBoolean(KEY_ALARM_BROADCAST, false); + mDeliveryGroupPolicy = opts.getInt(KEY_DELIVERY_GROUP_POLICY, + DELIVERY_GROUP_POLICY_ALL); + mDeliveryGroupMatchingKey = opts.getString(KEY_DELIVERY_GROUP_KEY); + mDeliveryGroupExtrasMerger = opts.getParcelable(KEY_DELIVERY_GROUP_EXTRAS_MERGER, + BundleMerger.class); + mDeliveryGroupMatchingFilter = opts.getParcelable(KEY_DELIVERY_GROUP_MATCHING_FILTER, + IntentFilter.class); + mDeferralPolicy = opts.getInt(KEY_DEFERRAL_POLICY, DEFERRAL_POLICY_DEFAULT); } /** @@ -265,8 +363,10 @@ public class BroadcastOptions extends ComponentOptions { * power allowlist when this broadcast is being delivered to it. * @param duration The duration in milliseconds; 0 means to not place on allowlist. * @deprecated use {@link #setTemporaryAppAllowlist(long, int, int, String)} instead. + * @hide */ @Deprecated + @SystemApi @RequiresPermission(anyOf = {android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) @@ -280,6 +380,8 @@ public class BroadcastOptions extends ComponentOptions { * Set a duration for which the system should temporary place an application on the * power allowlist when this broadcast is being delivered to it, specify the temp allowlist * type. + * @hide + * * @param duration the duration in milliseconds. * 0 means to not place on allowlist, and clears previous call to this method. * @param type one of {@link TempAllowListType}. @@ -290,6 +392,7 @@ public class BroadcastOptions extends ComponentOptions { * @param reason A human-readable reason explaining why the app is temp allowlisted. Only * used for logging purposes. Could be null or empty string. */ + @SystemApi @RequiresPermission(anyOf = {android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) @@ -319,26 +422,6 @@ public class BroadcastOptions extends ComponentOptions { } /** - * Set PendingIntent activity is allowed to be started in the background if the caller - * can start background activities. - * @hide - */ - @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS) - public void setPendingIntentBackgroundActivityLaunchAllowed(boolean allowed) { - super.setPendingIntentBackgroundActivityLaunchAllowed(allowed); - } - - /** - * Get PendingIntent activity is allowed to be started in the background if the caller - * can start background activities. - * @hide - */ - @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS) - public boolean isPendingIntentBackgroundActivityLaunchAllowed() { - return super.isPendingIntentBackgroundActivityLaunchAllowed(); - } - - /** * Return {@link #setTemporaryAppAllowlist}. * @hide */ @@ -450,9 +533,15 @@ public class BroadcastOptions extends ComponentOptions { * Sets whether pending intent can be sent for an application with background restrictions * @param dontSendToRestrictedApps if true, pending intent will not be sent for an application * with background restrictions. Default value is {@code false} + * @hide */ + @SystemApi public void setDontSendToRestrictedApps(boolean dontSendToRestrictedApps) { - mDontSendToRestrictedApps = dontSendToRestrictedApps; + if (dontSendToRestrictedApps) { + mFlags |= FLAG_DONT_SEND_TO_RESTRICTED_APPS; + } else { + mFlags &= ~FLAG_DONT_SEND_TO_RESTRICTED_APPS; + } } /** @@ -460,24 +549,31 @@ public class BroadcastOptions extends ComponentOptions { * @return #setDontSendToRestrictedApps */ public boolean isDontSendToRestrictedApps() { - return mDontSendToRestrictedApps; + return (mFlags & FLAG_DONT_SEND_TO_RESTRICTED_APPS) != 0; } /** * Sets the process will be able to start activities from background for the duration of * the broadcast dispatch. Default value is {@code false} + * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND) public void setBackgroundActivityStartsAllowed(boolean allowBackgroundActivityStarts) { - mAllowBackgroundActivityStarts = allowBackgroundActivityStarts; + if (allowBackgroundActivityStarts) { + mFlags |= FLAG_ALLOW_BACKGROUND_ACTIVITY_STARTS; + } else { + mFlags &= ~FLAG_ALLOW_BACKGROUND_ACTIVITY_STARTS; + } } /** * @hide * @return #setAllowBackgroundActivityStarts */ + @Deprecated public boolean allowsBackgroundActivityStarts() { - return mAllowBackgroundActivityStarts; + return (mFlags & FLAG_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0; } /** @@ -528,6 +624,7 @@ public class BroadcastOptions extends ComponentOptions { * <p> * This requirement applies to both manifest registered and runtime * registered receivers. + * @hide * * @param changeId the {@link ChangeId} to inspect * @param enabled the required enabled state of the inspected @@ -535,18 +632,24 @@ public class BroadcastOptions extends ComponentOptions { * @see CompatChanges#isChangeEnabled * @see #clearRequireCompatChange() */ + @SystemApi public void setRequireCompatChange(long changeId, boolean enabled) { mRequireCompatChangeId = changeId; - mRequireCompatChangeEnabled = enabled; + if (enabled) { + mFlags |= FLAG_REQUIRE_COMPAT_CHANGE_ENABLED; + } else { + mFlags &= ~FLAG_REQUIRE_COMPAT_CHANGE_ENABLED; + } } /** * Clear any previously defined requirement for this broadcast requested via * {@link #setRequireCompatChange(long, boolean)}. + * @hide */ + @SystemApi public void clearRequireCompatChange() { - mRequireCompatChangeId = CHANGE_INVALID; - mRequireCompatChangeEnabled = true; + setRequireCompatChange(CHANGE_INVALID, true); } /** @@ -558,7 +661,11 @@ public class BroadcastOptions extends ComponentOptions { * @param senderIsAlarm Whether the broadcast is alarm-triggered. */ public void setAlarmBroadcast(boolean senderIsAlarm) { - mIsAlarmBroadcast = senderIsAlarm; + if (senderIsAlarm) { + mFlags |= FLAG_IS_ALARM_BROADCAST; + } else { + mFlags &= ~FLAG_IS_ALARM_BROADCAST; + } } /** @@ -567,7 +674,44 @@ public class BroadcastOptions extends ComponentOptions { * @hide */ public boolean isAlarmBroadcast() { - return mIsAlarmBroadcast; + return (mFlags & FLAG_IS_ALARM_BROADCAST) != 0; + } + + /** + * Sets whether the identity of the broadcasting app should be shared with all receivers + * that will receive this broadcast. + * + * <p>Use this option when broadcasting to a receiver that needs to know the identity of the + * broadcaster; with this set to {@code true}, the receiver will have access to the broadcasting + * app's package name and uid. + * + * <p>Defaults to {@code false} if not set. + * + * @param shareIdentityEnabled whether the broadcasting app's identity should be shared with the + * receiver + * @return {@code this} {@link BroadcastOptions} instance + * @see BroadcastReceiver#getSentFromUid() + * @see BroadcastReceiver#getSentFromPackage() + */ + public @NonNull BroadcastOptions setShareIdentityEnabled(boolean shareIdentityEnabled) { + if (shareIdentityEnabled) { + mFlags |= FLAG_SHARE_IDENTITY; + } else { + mFlags &= ~FLAG_SHARE_IDENTITY; + } + return this; + } + + /** + * Returns whether the broadcasting app has opted-in to sharing its identity with the receiver. + * + * @return {@code true} if the broadcasting app has opted in to sharing its identity + * @see #setShareIdentityEnabled(boolean) + * @see BroadcastReceiver#getSentFromUid() + * @see BroadcastReceiver#getSentFromPackage() + */ + public boolean isShareIdentityEnabled() { + return (mFlags & FLAG_SHARE_IDENTITY) != 0; } /** @@ -606,8 +750,8 @@ public class BroadcastOptions extends ComponentOptions { @TestApi public boolean testRequireCompatChange(int uid) { if (mRequireCompatChangeId != CHANGE_INVALID) { - return CompatChanges.isChangeEnabled(mRequireCompatChangeId, - uid) == mRequireCompatChangeEnabled; + final boolean requireEnabled = (mFlags & FLAG_REQUIRE_COMPAT_CHANGE_ENABLED) != 0; + return CompatChanges.isChangeEnabled(mRequireCompatChangeId, uid) == requireEnabled; } else { return true; } @@ -637,15 +781,77 @@ public class BroadcastOptions extends ComponentOptions { return mIdForResponseEvent; } + /** {@hide} */ + @SystemApi + @Deprecated + // STOPSHIP: remove entirely after this API change lands in AOSP + public @NonNull BroadcastOptions setDeferUntilActive(boolean shouldDefer) { + if (shouldDefer) { + setDeferralPolicy(DEFERRAL_POLICY_UNTIL_ACTIVE); + } else { + setDeferralPolicy(DEFERRAL_POLICY_NONE); + } + return this; + } + + /** {@hide} */ + @SystemApi + @Deprecated + // STOPSHIP: remove entirely after this API change lands in AOSP + public boolean isDeferUntilActive() { + return (mDeferralPolicy == DEFERRAL_POLICY_UNTIL_ACTIVE); + } + + /** + * Sets deferral policy for this broadcast that specifies how this broadcast + * can be deferred for delivery at some future point. + */ + public @NonNull BroadcastOptions setDeferralPolicy(@DeferralPolicy int deferralPolicy) { + mDeferralPolicy = deferralPolicy; + return this; + } + + /** + * Gets deferral policy for this broadcast that specifies how this broadcast + * can be deferred for delivery at some future point. + */ + public @DeferralPolicy int getDeferralPolicy() { + return mDeferralPolicy; + } + + /** + * Clears any deferral policy for this broadcast that specifies how this + * broadcast can be deferred for delivery at some future point. + */ + public void clearDeferralPolicy() { + mDeferralPolicy = DEFERRAL_POLICY_DEFAULT; + } + /** * Set delivery group policy for this broadcast to specify how multiple broadcasts belonging to * the same delivery group has to be handled. - * - * @hide */ - @SystemApi - public void setDeliveryGroupPolicy(@DeliveryGroupPolicy int policy) { + @NonNull + public BroadcastOptions setDeliveryGroupPolicy(@DeliveryGroupPolicy int policy) { mDeliveryGroupPolicy = policy; + return this; + } + + /** + * Get the delivery group policy for this broadcast that specifies how multiple broadcasts + * belonging to the same delivery group has to be handled. + */ + public @DeliveryGroupPolicy int getDeliveryGroupPolicy() { + return mDeliveryGroupPolicy; + } + + /** + * Clears any previously set delivery group policies using + * {@link #setDeliveryGroupMatchingKey(String, String)} and resets the delivery group policy to + * the default value ({@link #DELIVERY_GROUP_POLICY_ALL}). + */ + public void clearDeliveryGroupPolicy() { + mDeliveryGroupPolicy = DELIVERY_GROUP_POLICY_ALL; } /** @@ -658,16 +864,36 @@ public class BroadcastOptions extends ComponentOptions { * <p> If neither matching key using this API nor matching filter using * {@link #setDeliveryGroupMatchingFilter(IntentFilter)} is specified, then by default * {@link Intent#filterEquals(Intent)} will be used to identify the delivery group. + */ + @NonNull + public BroadcastOptions setDeliveryGroupMatchingKey(@NonNull String namespace, + @NonNull String key) { + Preconditions.checkArgument(!namespace.contains(":"), + "namespace should not contain ':'"); + Preconditions.checkArgument(!key.contains(":"), + "key should not contain ':'"); + mDeliveryGroupMatchingKey = namespace + ":" + key; + return this; + } + + /** + * Return the namespace and key that is used to identify the delivery group that this + * broadcast belongs to. * - * @hide + * @return the delivery group namespace and key that was previously set using + * {@link #setDeliveryGroupMatchingKey(String, String)}, concatenated with a {@code :}. */ - @SystemApi - public void setDeliveryGroupMatchingKey(@NonNull String namespace, @NonNull String key) { - Preconditions.checkArgument(!namespace.contains("/"), - "namespace should not contain '/'"); - Preconditions.checkArgument(!key.contains("/"), - "key should not contain '/'"); - mDeliveryGroupMatchingKey = namespace + "/" + key; + @Nullable + public String getDeliveryGroupMatchingKey() { + return mDeliveryGroupMatchingKey; + } + + /** + * Clears the namespace and key that was previously set using + * {@link #setDeliveryGroupMatchingKey(String, String)}. + */ + public void clearDeliveryGroupMatchingKey() { + mDeliveryGroupMatchingKey = null; } /** @@ -680,47 +906,158 @@ public class BroadcastOptions extends ComponentOptions { * <p> If neither matching key using {@link #setDeliveryGroupMatchingKey(String, String)} nor * matching filter using this API is specified, then by default * {@link Intent#filterEquals(Intent)} will be used to identify the delivery group. + */ + @NonNull + public BroadcastOptions setDeliveryGroupMatchingFilter(@NonNull IntentFilter matchingFilter) { + mDeliveryGroupMatchingFilter = Objects.requireNonNull(matchingFilter); + return this; + } + + /** + * Return the {@link IntentFilter} object that is used to identify the delivery group + * that this broadcast belongs to. + * + * @return the {@link IntentFilter} object that was previously set using + * {@link #setDeliveryGroupMatchingFilter(IntentFilter)}. + */ + @Nullable + public IntentFilter getDeliveryGroupMatchingFilter() { + return mDeliveryGroupMatchingFilter; + } + + /** + * Clears the {@link IntentFilter} object that was previously set using + * {@link #setDeliveryGroupMatchingFilter(IntentFilter)}. + */ + public void clearDeliveryGroupMatchingFilter() { + mDeliveryGroupMatchingFilter = null; + } + + /** + * Set the {@link BundleMerger} that specifies how to merge the extras data from + * broadcasts in a delivery group. + * + * <p>Note that this value will be ignored if the delivery group policy is not set as + * {@link #DELIVERY_GROUP_POLICY_MERGED}. * * @hide */ - @SystemApi - public void setDeliveryGroupMatchingFilter(@NonNull IntentFilter matchingFilter) { - mDeliveryGroupMatchingFilter = Objects.requireNonNull(matchingFilter); + @NonNull + public BroadcastOptions setDeliveryGroupExtrasMerger(@NonNull BundleMerger extrasMerger) { + mDeliveryGroupExtrasMerger = Objects.requireNonNull(extrasMerger); + return this; + } + + /** + * Return the {@link BundleMerger} that specifies how to merge the extras data from + * broadcasts in a delivery group. + * + * @return the {@link BundleMerger} object that was previously set using + * {@link #setDeliveryGroupExtrasMerger(BundleMerger)}. + * @hide + */ + @Nullable + public BundleMerger getDeliveryGroupExtrasMerger() { + return mDeliveryGroupExtrasMerger; + } + + /** + * Clear the {@link BundleMerger} object that was previously set using + * {@link #setDeliveryGroupExtrasMerger(BundleMerger)}. + * @hide + */ + public void clearDeliveryGroupExtrasMerger() { + mDeliveryGroupExtrasMerger = null; } /** - * Sets whether the broadcast should not run until the process is in an active process state - * (ie, a process exists for the app and the app is not in a cached process state). + * Sets whether the broadcast should be considered as having originated from + * some direct interaction by the user such as a notification tap or button + * press. This signal is used internally to ensure the broadcast is + * delivered quickly with low latency. * - * Whether an app's process state is considered active is independent of its standby bucket. + * @hide + */ + public @NonNull BroadcastOptions setInteractive(boolean interactive) { + if (interactive) { + mFlags |= FLAG_INTERACTIVE; + } else { + mFlags &= ~FLAG_INTERACTIVE; + } + return this; + } + + /** + * Returns whether the broadcast should be considered as having originated + * from some direct interaction by the user such as a notification tap or + * button press. * - * A broadcast that is deferred until the process is active will not execute until the process - * is brought to an active state by some other action, like a job, alarm, or service binding. As - * a result, the broadcast may be delayed indefinitely. This deferral only applies to runtime - * registered receivers of a broadcast. Any manifest receivers will run immediately, similar to - * how a manifest receiver would start a new process in order to run a broadcast receiver. + * @hide + */ + public boolean isInteractive() { + return (mFlags & FLAG_INTERACTIVE) != 0; + } + + /** + * Set PendingIntent activity is allowed to be started in the background if the caller + * can start background activities. * - * Ordered broadcasts, alarm broadcasts, interactive broadcasts, and manifest broadcasts are - * never deferred. + * @deprecated use #setPendingIntentBackgroundActivityStartMode(int) to set the full range + * of states + * @hide + */ + @SystemApi + @Override + @Deprecated public void setPendingIntentBackgroundActivityLaunchAllowed(boolean allowed) { + super.setPendingIntentBackgroundActivityLaunchAllowed(allowed); + } + + /** + * Get PendingIntent activity is allowed to be started in the background if the caller can start + * background activities. * - * Unordered broadcasts and unordered broadcasts with completion callbacks may be - * deferred. Completion callbacks for broadcasts deferred until active are - * best-effort. Completion callbacks will run when all eligible processes have finished - * executing the broadcast. Processes in inactive process states that defer the broadcast are - * not considered eligible and may not execute the broadcast prior to the completion callback. + * @deprecated use {@link #getPendingIntentBackgroundActivityStartMode()} since for apps + * targeting {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or higher this value might + * not match the actual behavior if the value was not explicitly set. + * @hide + */ + @SystemApi + @Override + @Deprecated public boolean isPendingIntentBackgroundActivityLaunchAllowed() { + return super.isPendingIntentBackgroundActivityLaunchAllowed(); + } + + + /** + * Sets the mode for allowing or denying the senders privileges to start background activities + * to the PendingIntent. * + * This is typically used when executing {@link PendingIntent#send(Bundle)} or similar + * methods. A privileged sender of a PendingIntent should only grant + * MODE_BACKGROUND_ACTIVITY_START_ALLOWED if the PendingIntent is from a trusted source and/or + * executed on behalf the user. * @hide */ @SystemApi - public @NonNull BroadcastOptions setDeferUntilActive(boolean shouldDefer) { - mIsDeferUntilActive = shouldDefer; + @NonNull + // @Override // to narrow down the return type + public BroadcastOptions setPendingIntentBackgroundActivityStartMode(int state) { + // super.setPendingIntentBackgroundActivityStartMode(state); return this; } - /** @hide */ + /** + * Gets the mode for allowing or denying the senders privileges to start background activities + * to the PendingIntent. + * + * @see #setPendingIntentBackgroundActivityStartMode(int) + * @hide + */ @SystemApi - public boolean isDeferUntilActive() { - return mIsDeferUntilActive; + // @Override // to narrow down the return type + public int getPendingIntentBackgroundActivityStartMode() { + return 0; + // return super.getPendingIntentBackgroundActivityStartMode(); } /** @@ -730,31 +1067,29 @@ public class BroadcastOptions extends ComponentOptions { * Note that the returned Bundle is still owned by the BroadcastOptions * object; you must not modify it, but can supply it to the sendBroadcast * methods that take an options Bundle. + * + * @throws IllegalStateException if the broadcast option values are inconsistent. For example, + * if the delivery group policy is specified as "MERGED" but no + * extras merger is supplied. */ @Override - public Bundle toBundle() { + public @NonNull Bundle toBundle() { Bundle b = super.toBundle(); + if (mFlags != 0) { + b.putInt(KEY_FLAGS, mFlags); + } if (isTemporaryAppAllowlistSet()) { b.putLong(KEY_TEMPORARY_APP_ALLOWLIST_DURATION, mTemporaryAppAllowlistDuration); b.putInt(KEY_TEMPORARY_APP_ALLOWLIST_TYPE, mTemporaryAppAllowlistType); b.putInt(KEY_TEMPORARY_APP_ALLOWLIST_REASON_CODE, mTemporaryAppAllowlistReasonCode); b.putString(KEY_TEMPORARY_APP_ALLOWLIST_REASON, mTemporaryAppAllowlistReason); } - if (mIsAlarmBroadcast) { - b.putBoolean(KEY_ALARM_BROADCAST, true); - } if (mMinManifestReceiverApiLevel != 0) { b.putInt(KEY_MIN_MANIFEST_RECEIVER_API_LEVEL, mMinManifestReceiverApiLevel); } if (mMaxManifestReceiverApiLevel != Build.VERSION_CODES.CUR_DEVELOPMENT) { b.putInt(KEY_MAX_MANIFEST_RECEIVER_API_LEVEL, mMaxManifestReceiverApiLevel); } - if (mDontSendToRestrictedApps) { - b.putBoolean(KEY_DONT_SEND_TO_RESTRICTED_APPS, true); - } - if (mAllowBackgroundActivityStarts) { - b.putBoolean(KEY_ALLOW_BACKGROUND_ACTIVITY_STARTS, true); - } if (mRequireAllOfPermissions != null) { b.putStringArray(KEY_REQUIRE_ALL_OF_PERMISSIONS, mRequireAllOfPermissions); } @@ -763,11 +1098,44 @@ public class BroadcastOptions extends ComponentOptions { } if (mRequireCompatChangeId != CHANGE_INVALID) { b.putLong(KEY_REQUIRE_COMPAT_CHANGE_ID, mRequireCompatChangeId); - b.putBoolean(KEY_REQUIRE_COMPAT_CHANGE_ENABLED, mRequireCompatChangeEnabled); } if (mIdForResponseEvent != 0) { b.putLong(KEY_ID_FOR_RESPONSE_EVENT, mIdForResponseEvent); } - return b.isEmpty() ? null : b; + if (mDeliveryGroupPolicy != DELIVERY_GROUP_POLICY_ALL) { + b.putInt(KEY_DELIVERY_GROUP_POLICY, mDeliveryGroupPolicy); + } + if (mDeliveryGroupMatchingKey != null) { + b.putString(KEY_DELIVERY_GROUP_KEY, mDeliveryGroupMatchingKey); + } + if (mDeliveryGroupPolicy == DELIVERY_GROUP_POLICY_MERGED) { + if (mDeliveryGroupExtrasMerger != null) { + b.putParcelable(KEY_DELIVERY_GROUP_EXTRAS_MERGER, + mDeliveryGroupExtrasMerger); + } else { + throw new IllegalStateException("Extras merger cannot be empty " + + "when delivery group policy is 'MERGED'"); + } + } + if (mDeliveryGroupMatchingFilter != null) { + b.putParcelable(KEY_DELIVERY_GROUP_MATCHING_FILTER, mDeliveryGroupMatchingFilter); + } + if (mDeferralPolicy != DEFERRAL_POLICY_DEFAULT) { + b.putInt(KEY_DEFERRAL_POLICY, mDeferralPolicy); + } + return b; + } + + /** + * Returns a {@link BroadcastOptions} parsed from the given {@link Bundle}, + * typically generated from {@link #toBundle()}. + */ + public static @NonNull BroadcastOptions fromBundle(@NonNull Bundle options) { + return new BroadcastOptions(options); + } + + /** {@hide} */ + public static @Nullable BroadcastOptions fromBundleNullable(@Nullable Bundle options) { + return (options != null) ? new BroadcastOptions(options) : null; } } diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java index 1bb44af81cec..656cd997acbe 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -419,6 +419,7 @@ public final class NfcAdapter { static boolean sIsInitialized = false; static boolean sHasNfcFeature; static boolean sHasBeamFeature; + static boolean sHasCeFeature; // Final after first constructor, except for // attemptDeadServiceRecovery() when NFC crashes - we accept a best effort @@ -616,11 +617,13 @@ public final class NfcAdapter { pm = context.getPackageManager(); sHasNfcFeature = pm.hasSystemFeature(PackageManager.FEATURE_NFC); sHasBeamFeature = pm.hasSystemFeature(PackageManager.FEATURE_NFC_BEAM); - boolean hasHceFeature = + sHasCeFeature = pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION) - || pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF); + || pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF) + || pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC) + || pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE); /* is this device meant to have NFC */ - if (!sHasNfcFeature && !hasHceFeature) { + if (!sHasNfcFeature && !sHasCeFeature) { Log.v(TAG, "this device does not have NFC support"); throw new UnsupportedOperationException(); } @@ -643,7 +646,7 @@ public final class NfcAdapter { throw new UnsupportedOperationException(); } } - if (hasHceFeature) { + if (sHasCeFeature) { try { sNfcFCardEmulationService = sService.getNfcFCardEmulationInterface(); } catch (RemoteException e) { @@ -1846,7 +1849,7 @@ public final class NfcAdapter { @SystemApi @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableSecureNfc(boolean enable) { - if (!sHasNfcFeature) { + if (!sHasNfcFeature && !sHasCeFeature) { throw new UnsupportedOperationException(); } try { @@ -1871,10 +1874,13 @@ public final class NfcAdapter { * Checks if the device supports Secure NFC functionality. * * @return True if device supports Secure NFC, false otherwise - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. + * @throws UnsupportedOperationException if FEATURE_NFC, + * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF, + * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE + * are unavailable */ public boolean isSecureNfcSupported() { - if (!sHasNfcFeature) { + if (!sHasNfcFeature && !sHasCeFeature) { throw new UnsupportedOperationException(); } try { @@ -1900,11 +1906,14 @@ public final class NfcAdapter { * such as their relative positioning on the device. * * @return Information on the nfc antenna(s) on the device. - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. + * @throws UnsupportedOperationException if FEATURE_NFC, + * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF, + * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE + * are unavailable */ @Nullable public NfcAntennaInfo getNfcAntennaInfo() { - if (!sHasNfcFeature) { + if (!sHasNfcFeature && !sHasCeFeature) { throw new UnsupportedOperationException(); } try { @@ -1929,12 +1938,15 @@ public final class NfcAdapter { * Checks Secure NFC feature is enabled. * * @return True if Secure NFC is enabled, false otherwise - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. + * @throws UnsupportedOperationException if FEATURE_NFC, + * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF, + * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE + * are unavailable * @throws UnsupportedOperationException if device doesn't support * Secure NFC functionality. {@link #isSecureNfcSupported} */ public boolean isSecureNfcEnabled() { - if (!sHasNfcFeature) { + if (!sHasNfcFeature && !sHasCeFeature) { throw new UnsupportedOperationException(); } try { @@ -2281,14 +2293,17 @@ public final class NfcAdapter { * always on. * @param value if true the NFCC will be kept on (with no RF enabled if NFC adapter is * disabled), if false the NFCC will follow completely the Nfc adapter state. - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. + * @throws UnsupportedOperationException if FEATURE_NFC, + * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF, + * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE + * are unavailable * @return void * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean setControllerAlwaysOn(boolean value) { - if (!sHasNfcFeature) { + if (!sHasNfcFeature && !sHasCeFeature) { throw new UnsupportedOperationException(); } try { @@ -2313,7 +2328,10 @@ public final class NfcAdapter { * Checks NFC controller always on feature is enabled. * * @return True if NFC controller always on is enabled, false otherwise - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. + * @throws UnsupportedOperationException if FEATURE_NFC, + * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF, + * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE + * are unavailable * @hide */ @SystemApi @@ -2341,13 +2359,16 @@ public final class NfcAdapter { * Checks if the device supports NFC controller always on functionality. * * @return True if device supports NFC controller always on, false otherwise - * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. + * @throws UnsupportedOperationException if FEATURE_NFC, + * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF, + * FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC and FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE + * are unavailable * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOnSupported() { - if (!sHasNfcFeature) { + if (!sHasNfcFeature && !sHasCeFeature) { throw new UnsupportedOperationException(); } try { diff --git a/core/java/android/os/BundleMerger.java b/core/java/android/os/BundleMerger.java new file mode 100644 index 000000000000..857aaf57f640 --- /dev/null +++ b/core/java/android/os/BundleMerger.java @@ -0,0 +1,388 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Objects; +import java.util.function.BinaryOperator; + +/** + * Configured rules for merging two {@link Bundle} instances. + * <p> + * By default, values from both {@link Bundle} instances are blended together on + * a key-wise basis, and conflicting value definitions for a key are dropped. + * <p> + * Nuanced strategies for handling conflicting value definitions can be applied + * using {@link #setMergeStrategy(String, int)} and + * {@link #setDefaultMergeStrategy(int)}. + * <p> + * When conflicting values have <em>inconsistent</em> data types (such as trying + * to merge a {@link String} and a {@link Integer}), both conflicting values are + * rejected and the key becomes undefined, regardless of the requested strategy. + * + * @hide + */ +public class BundleMerger implements Parcelable { + private static final String TAG = "BundleMerger"; + + private @Strategy int mDefaultStrategy = STRATEGY_REJECT; + + private final ArrayMap<String, Integer> mStrategies = new ArrayMap<>(); + + /** + * Merge strategy that rejects both conflicting values. + */ + public static final int STRATEGY_REJECT = 0; + + /** + * Merge strategy that selects the first of conflicting values. + */ + public static final int STRATEGY_FIRST = 1; + + /** + * Merge strategy that selects the last of conflicting values. + */ + public static final int STRATEGY_LAST = 2; + + /** + * Merge strategy that selects the "minimum" of conflicting values which are + * {@link Comparable} with each other. + */ + public static final int STRATEGY_COMPARABLE_MIN = 3; + + /** + * Merge strategy that selects the "maximum" of conflicting values which are + * {@link Comparable} with each other. + */ + public static final int STRATEGY_COMPARABLE_MAX = 4; + + /** + * Merge strategy that numerically adds both conflicting values. + */ + public static final int STRATEGY_NUMBER_ADD = 10; + + /** + * Merge strategy that numerically increments the first conflicting value by + * {@code 1} and ignores the last conflicting value. + */ + public static final int STRATEGY_NUMBER_INCREMENT_FIRST = 20; + + /** + * Merge strategy that numerically increments the first conflicting value by + * {@code 1} and also numerically adds both conflicting values. + */ + public static final int STRATEGY_NUMBER_INCREMENT_FIRST_AND_ADD = 25; + + /** + * Merge strategy that combines conflicting values using a boolean "and" + * operation. + */ + public static final int STRATEGY_BOOLEAN_AND = 30; + + /** + * Merge strategy that combines conflicting values using a boolean "or" + * operation. + */ + public static final int STRATEGY_BOOLEAN_OR = 40; + + /** + * Merge strategy that combines two conflicting array values by appending + * the last array after the first array. + */ + public static final int STRATEGY_ARRAY_APPEND = 50; + + /** + * Merge strategy that combines two conflicting {@link ArrayList} values by + * appending the last {@link ArrayList} after the first {@link ArrayList}. + */ + public static final int STRATEGY_ARRAY_LIST_APPEND = 60; + + @IntDef(flag = false, prefix = { "STRATEGY_" }, value = { + STRATEGY_REJECT, + STRATEGY_FIRST, + STRATEGY_LAST, + STRATEGY_COMPARABLE_MIN, + STRATEGY_COMPARABLE_MAX, + STRATEGY_NUMBER_ADD, + STRATEGY_NUMBER_INCREMENT_FIRST, + STRATEGY_NUMBER_INCREMENT_FIRST_AND_ADD, + STRATEGY_BOOLEAN_AND, + STRATEGY_BOOLEAN_OR, + STRATEGY_ARRAY_APPEND, + STRATEGY_ARRAY_LIST_APPEND, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Strategy {} + + /** + * Create a empty set of rules for merging two {@link Bundle} instances. + */ + public BundleMerger() { + } + + private BundleMerger(@NonNull Parcel in) { + mDefaultStrategy = in.readInt(); + final int N = in.readInt(); + for (int i = 0; i < N; i++) { + mStrategies.put(in.readString(), in.readInt()); + } + } + + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeInt(mDefaultStrategy); + final int N = mStrategies.size(); + out.writeInt(N); + for (int i = 0; i < N; i++) { + out.writeString(mStrategies.keyAt(i)); + out.writeInt(mStrategies.valueAt(i)); + } + } + + @Override + public int describeContents() { + return 0; + } + + /** + * Configure the default merge strategy to be used when there isn't a + * more-specific strategy defined for a particular key via + * {@link #setMergeStrategy(String, int)}. + */ + public void setDefaultMergeStrategy(@Strategy int strategy) { + mDefaultStrategy = strategy; + } + + /** + * Configure the merge strategy to be used for the given key. + * <p> + * Subsequent calls for the same key will overwrite any previously + * configured strategy. + */ + public void setMergeStrategy(@NonNull String key, @Strategy int strategy) { + mStrategies.put(key, strategy); + } + + /** + * Return the merge strategy to be used for the given key, as defined by + * {@link #setMergeStrategy(String, int)}. + * <p> + * If no specific strategy has been configured for the given key, this + * returns {@link #setDefaultMergeStrategy(int)}. + */ + public @Strategy int getMergeStrategy(@NonNull String key) { + return (int) mStrategies.getOrDefault(key, mDefaultStrategy); + } + + /** + * Return a {@link BinaryOperator} which applies the strategies configured + * in this object to merge the two given {@link Bundle} arguments. + */ + public BinaryOperator<Bundle> asBinaryOperator() { + return this::merge; + } + + /** + * Apply the strategies configured in this object to merge the two given + * {@link Bundle} arguments. + * + * @return the merged {@link Bundle} result. If one argument is {@code null} + * it will return the other argument. If both arguments are null it + * will return {@code null}. + */ + @SuppressWarnings("deprecation") + public @Nullable Bundle merge(@Nullable Bundle first, @Nullable Bundle last) { + if (first == null && last == null) { + return null; + } + if (first == null) { + first = Bundle.EMPTY; + } + if (last == null) { + last = Bundle.EMPTY; + } + + // Start by bulk-copying all values without attempting to unpack any + // custom parcelables; we'll circle back to handle conflicts below + final Bundle res = new Bundle(); + res.putAll(first); + res.putAll(last); + + final ArraySet<String> conflictingKeys = new ArraySet<>(); + conflictingKeys.addAll(first.keySet()); + conflictingKeys.retainAll(last.keySet()); + for (int i = 0; i < conflictingKeys.size(); i++) { + final String key = conflictingKeys.valueAt(i); + final int strategy = getMergeStrategy(key); + final Object firstValue = first.get(key); + final Object lastValue = last.get(key); + try { + res.putObject(key, merge(strategy, firstValue, lastValue)); + } catch (Exception e) { + Log.w(TAG, "Failed to merge key " + key + " with " + firstValue + " and " + + lastValue + " using strategy " + strategy, e); + } + } + return res; + } + + /** + * Merge the two given values. If only one of the values is defined, it + * always wins, otherwise the given strategy is applied. + * + * @hide + */ + @VisibleForTesting + public static @Nullable Object merge(@Strategy int strategy, + @Nullable Object first, @Nullable Object last) { + if (first == null) return last; + if (last == null) return first; + + if (first.getClass() != last.getClass()) { + throw new IllegalArgumentException("Merging requires consistent classes; first " + + first.getClass() + " last " + last.getClass()); + } + + switch (strategy) { + case STRATEGY_REJECT: + // Only actually reject when the values are different + if (Objects.deepEquals(first, last)) { + return first; + } else { + return null; + } + case STRATEGY_FIRST: + return first; + case STRATEGY_LAST: + return last; + case STRATEGY_COMPARABLE_MIN: + return comparableMin(first, last); + case STRATEGY_COMPARABLE_MAX: + return comparableMax(first, last); + case STRATEGY_NUMBER_ADD: + return numberAdd(first, last); + case STRATEGY_NUMBER_INCREMENT_FIRST: + return numberIncrementFirst(first, last); + case STRATEGY_NUMBER_INCREMENT_FIRST_AND_ADD: + return numberAdd(numberIncrementFirst(first, last), last); + case STRATEGY_BOOLEAN_AND: + return booleanAnd(first, last); + case STRATEGY_BOOLEAN_OR: + return booleanOr(first, last); + case STRATEGY_ARRAY_APPEND: + return arrayAppend(first, last); + case STRATEGY_ARRAY_LIST_APPEND: + return arrayListAppend(first, last); + default: + throw new UnsupportedOperationException(); + } + } + + @SuppressWarnings("unchecked") + private static @NonNull Object comparableMin(@NonNull Object first, @NonNull Object last) { + return ((Comparable<Object>) first).compareTo(last) < 0 ? first : last; + } + + @SuppressWarnings("unchecked") + private static @NonNull Object comparableMax(@NonNull Object first, @NonNull Object last) { + return ((Comparable<Object>) first).compareTo(last) >= 0 ? first : last; + } + + private static @NonNull Object numberAdd(@NonNull Object first, @NonNull Object last) { + if (first instanceof Integer) { + return ((Integer) first) + ((Integer) last); + } else if (first instanceof Long) { + return ((Long) first) + ((Long) last); + } else if (first instanceof Float) { + return ((Float) first) + ((Float) last); + } else if (first instanceof Double) { + return ((Double) first) + ((Double) last); + } else { + throw new IllegalArgumentException("Unable to add " + first.getClass()); + } + } + + private static @NonNull Number numberIncrementFirst(@NonNull Object first, + @NonNull Object last) { + if (first instanceof Integer) { + return ((Integer) first) + 1; + } else if (first instanceof Long) { + return ((Long) first) + 1L; + } else { + throw new IllegalArgumentException("Unable to add " + first.getClass()); + } + } + + private static @NonNull Object booleanAnd(@NonNull Object first, @NonNull Object last) { + return ((Boolean) first) && ((Boolean) last); + } + + private static @NonNull Object booleanOr(@NonNull Object first, @NonNull Object last) { + return ((Boolean) first) || ((Boolean) last); + } + + private static @NonNull Object arrayAppend(@NonNull Object first, @NonNull Object last) { + if (!first.getClass().isArray()) { + throw new IllegalArgumentException("Unable to append " + first.getClass()); + } + final Class<?> clazz = first.getClass().getComponentType(); + final int firstLength = Array.getLength(first); + final int lastLength = Array.getLength(last); + final Object res = Array.newInstance(clazz, firstLength + lastLength); + System.arraycopy(first, 0, res, 0, firstLength); + System.arraycopy(last, 0, res, firstLength, lastLength); + return res; + } + + @SuppressWarnings("unchecked") + private static @NonNull Object arrayListAppend(@NonNull Object first, @NonNull Object last) { + if (!(first instanceof ArrayList)) { + throw new IllegalArgumentException("Unable to append " + first.getClass()); + } + final ArrayList<Object> firstList = (ArrayList<Object>) first; + final ArrayList<Object> lastList = (ArrayList<Object>) last; + final ArrayList<Object> res = new ArrayList<>(firstList.size() + lastList.size()); + res.addAll(firstList); + res.addAll(lastList); + return res; + } + + public static final @android.annotation.NonNull Parcelable.Creator<BundleMerger> CREATOR = + new Parcelable.Creator<BundleMerger>() { + @Override + public BundleMerger createFromParcel(Parcel in) { + return new BundleMerger(in); + } + + @Override + public BundleMerger[] newArray(int size) { + return new BundleMerger[size]; + } + }; +} diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index a3696e398668..4bcdf0d0d50d 100644 --- a/core/java/android/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -45,6 +45,7 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.listeners.ListenerExecutor; +import com.android.internal.telephony.ICarrierConfigChangeListener; import com.android.internal.telephony.ICarrierPrivilegesCallback; import com.android.internal.telephony.IOnSubscriptionsChangedListener; import com.android.internal.telephony.ITelephonyRegistry; @@ -54,8 +55,10 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.WeakHashMap; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.stream.Collectors; @@ -89,6 +92,14 @@ public class TelephonyRegistryManager { IOnSubscriptionsChangedListener> mOpportunisticSubscriptionChangedListenerMap = new HashMap<>(); + /** + * A mapping between {@link CarrierConfigManager.CarrierConfigChangeListener} and its callback + * ICarrierConfigChangeListener. + */ + private final ConcurrentHashMap<CarrierConfigManager.CarrierConfigChangeListener, + ICarrierConfigChangeListener> + mCarrierConfigChangeListenerMap = new ConcurrentHashMap<>(); + /** @hide **/ public TelephonyRegistryManager(@NonNull Context context) { @@ -1409,4 +1420,94 @@ public class TelephonyRegistryManager { throw e.rethrowFromSystemServer(); } } + + /** + * Register a {@link android.telephony.CarrierConfigManager.CarrierConfigChangeListener} to get + * notification when carrier configurations have changed. + * + * @param executor The executor on which the callback will be executed. + * @param listener The CarrierConfigChangeListener to be registered with. + */ + public void addCarrierConfigChangedListener( + @NonNull @CallbackExecutor Executor executor, + @NonNull CarrierConfigManager.CarrierConfigChangeListener listener) { + Objects.requireNonNull(executor, "Executor should be non-null."); + Objects.requireNonNull(listener, "Listener should be non-null."); + if (mCarrierConfigChangeListenerMap.get(listener) != null) { + Log.e(TAG, "registerCarrierConfigChangeListener: listener already present"); + return; + } + + ICarrierConfigChangeListener callback = new ICarrierConfigChangeListener.Stub() { + @Override + public void onCarrierConfigChanged(int slotIndex, int subId, int carrierId, + int specificCarrierId) { + Log.d(TAG, "onCarrierConfigChanged call in ICarrierConfigChangeListener callback"); + final long identify = Binder.clearCallingIdentity(); + try { + executor.execute(() -> listener.onCarrierConfigChanged(slotIndex, subId, + carrierId, specificCarrierId)); + } finally { + Binder.restoreCallingIdentity(identify); + } + } + }; + + try { + sRegistry.addCarrierConfigChangeListener(callback, + mContext.getOpPackageName(), mContext.getAttributionTag()); + mCarrierConfigChangeListenerMap.put(listener, callback); + } catch (RemoteException re) { + // system server crashes + throw re.rethrowFromSystemServer(); + } + } + + /** + * Unregister to stop the notification when carrier configurations changed. + * + * @param listener The CarrierConfigChangeListener to be unregistered with. + */ + public void removeCarrierConfigChangedListener( + @NonNull CarrierConfigManager.CarrierConfigChangeListener listener) { + Objects.requireNonNull(listener, "Listener should be non-null."); + if (mCarrierConfigChangeListenerMap.get(listener) == null) { + Log.e(TAG, "removeCarrierConfigChangedListener: listener was not present"); + return; + } + + try { + sRegistry.removeCarrierConfigChangeListener( + mCarrierConfigChangeListenerMap.get(listener), mContext.getOpPackageName()); + mCarrierConfigChangeListenerMap.remove(listener); + } catch (RemoteException re) { + // System sever crashes + throw re.rethrowFromSystemServer(); + } + } + + /** + * Notify the registrants the carrier configurations have changed. + * + * @param slotIndex The SIM slot index on which to monitor and get notification. + * @param subId The subscription on the SIM slot. May be + * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}. + * @param carrierId The optional carrier Id, may be + * {@link TelephonyManager#UNKNOWN_CARRIER_ID}. + * @param specificCarrierId The optional specific carrier Id, may be {@link + * TelephonyManager#UNKNOWN_CARRIER_ID}. + */ + public void notifyCarrierConfigChanged(int slotIndex, int subId, int carrierId, + int specificCarrierId) { + // Only validate slotIndex, all others are optional and allowed to be invalid + if (!SubscriptionManager.isValidPhoneId(slotIndex)) { + Log.e(TAG, "notifyCarrierConfigChanged, ignored: invalid slotIndex " + slotIndex); + return; + } + try { + sRegistry.notifyCarrierConfigChanged(slotIndex, subId, carrierId, specificCarrierId); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } } diff --git a/core/java/com/android/internal/telephony/ICarrierConfigChangeListener.aidl b/core/java/com/android/internal/telephony/ICarrierConfigChangeListener.aidl new file mode 100644 index 000000000000..0f7ab0a3d4fb --- /dev/null +++ b/core/java/com/android/internal/telephony/ICarrierConfigChangeListener.aidl @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony; + +oneway interface ICarrierConfigChangeListener { + void onCarrierConfigChanged(int slotIndex, int subId, int carrierId, int specificCarrierId); +}
\ No newline at end of file diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl index c7fa757ac0b7..747c40df9492 100644 --- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl +++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl @@ -32,6 +32,7 @@ import android.telephony.PreciseDataConnectionState; import android.telephony.ServiceState; import android.telephony.SignalStrength; import android.telephony.emergency.EmergencyNumber; +import com.android.internal.telephony.ICarrierConfigChangeListener; import com.android.internal.telephony.ICarrierPrivilegesCallback; import com.android.internal.telephony.IPhoneStateListener; import com.android.internal.telephony.IOnSubscriptionsChangedListener; @@ -109,4 +110,8 @@ interface ITelephonyRegistry { int phoneId, in List<String> privilegedPackageNames, in int[] privilegedUids); void notifyCarrierServiceChanged(int phoneId, in String packageName, int uid); + void addCarrierConfigChangeListener(ICarrierConfigChangeListener listener, + String pkg, String featureId); + void removeCarrierConfigChangeListener(ICarrierConfigChangeListener listener, String pkg); + void notifyCarrierConfigChanged(int phoneId, int subId, int carrierId, int specificCarrierId); } diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp index 4bc567abf27a..ad54004ec81c 100644 --- a/core/jni/android_view_InputEventSender.cpp +++ b/core/jni/android_view_InputEventSender.cpp @@ -20,20 +20,21 @@ #include <android_runtime/AndroidRuntime.h> #include <input/InputTransport.h> +#include <inttypes.h> #include <log/log.h> #include <nativehelper/JNIHelp.h> #include <nativehelper/ScopedLocalRef.h> #include <utils/Looper.h> + +#include <optional> +#include <unordered_map> + #include "android_os_MessageQueue.h" #include "android_view_InputChannel.h" #include "android_view_KeyEvent.h" #include "android_view_MotionEvent.h" #include "core_jni_helpers.h" -#include <inttypes.h> -#include <unordered_map> - - using android::base::Result; namespace android { @@ -67,7 +68,7 @@ private: jobject mSenderWeakGlobal; InputPublisher mInputPublisher; sp<MessageQueue> mMessageQueue; - std::unordered_map<uint32_t, uint32_t> mPublishedSeqMap; + std::unordered_map<uint32_t, std::optional<uint32_t>> mPublishedSeqMap; uint32_t mNextPublishedSeq; @@ -165,8 +166,14 @@ status_t NativeInputEventSender::sendMotionEvent(uint32_t seq, const MotionEvent getInputChannelName().c_str(), status); return status; } + // mPublishedSeqMap tracks all sequences published from this sender. Only the last + // sequence number is used to signal this motion event is finished. + if (i == event->getHistorySize()) { + mPublishedSeqMap.emplace(publishedSeq, seq); + } else { + mPublishedSeqMap.emplace(publishedSeq, std::nullopt); + } } - mPublishedSeqMap.emplace(publishedSeq, seq); return OK; } @@ -277,8 +284,16 @@ bool NativeInputEventSender::notifyConsumerResponse( // does something wrong and sends bad data. Just ignore and process other events. return true; } - const uint32_t seq = it->second; + + const std::optional<uint32_t> seqOptional = it->second; mPublishedSeqMap.erase(it); + // If this optional does not have a value, it means we are processing an event that had history + // and was split. There are more events coming, so we can't call 'dispatchInputEventFinished' + // yet. The final split event will have a valid sequence number. + if (!seqOptional.has_value()) { + return true; + } + const uint32_t seq = seqOptional.value(); if (kDebugDispatchCycle) { ALOGD("channel '%s' ~ Received finished signal, seq=%u, handled=%s, pendingEvents=%zu.", diff --git a/core/res/Android.bp b/core/res/Android.bp index 93ce7832824b..42c95d6cfd58 100644 --- a/core/res/Android.bp +++ b/core/res/Android.bp @@ -172,3 +172,12 @@ genrule { " > $(out)", tools: ["xmllint"], } + +filegroup { + name: "frameworks-base-core-AndroidManifest.xml", + srcs: ["AndroidManifest.xml"], + visibility: [ + "//frameworks/base", + "//frameworks/base/api", + ], +} diff --git a/core/tests/coretests/src/android/content/pm/TEST_MAPPING b/core/tests/coretests/src/android/content/pm/TEST_MAPPING index 15e04d11acf7..978d80cb52f6 100644 --- a/core/tests/coretests/src/android/content/pm/TEST_MAPPING +++ b/core/tests/coretests/src/android/content/pm/TEST_MAPPING @@ -1,5 +1,5 @@ { - "presubmit-large": [ + "presubmit": [ { "name": "FrameworksCoreTests", "options": [ diff --git a/core/tests/coretests/src/android/view/OWNERS b/core/tests/coretests/src/android/view/OWNERS index a142e27a1ab0..2ca99943a8a0 100644 --- a/core/tests/coretests/src/android/view/OWNERS +++ b/core/tests/coretests/src/android/view/OWNERS @@ -1,4 +1,5 @@ # Accessibility +per-file AccessibilityInteractionControllerTest.java = file:/services/accessibility/OWNERS per-file WindowInfoTest.java = file:/services/accessibility/OWNERS # Input diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java index ca3c84729388..6e60e9e2df6a 100644 --- a/graphics/java/android/graphics/ColorSpace.java +++ b/graphics/java/android/graphics/ColorSpace.java @@ -170,7 +170,7 @@ public abstract class ColorSpace { /** * Standard CIE 1931 2° illuminant D65, encoded in xyY. * This illuminant has a color temperature of 6504K. This illuminant - * is commonly used in RGB color spaces such as sRGB, BT.209, etc. + * is commonly used in RGB color spaces such as sRGB, BT.709, etc. */ public static final float[] ILLUMINANT_D65 = { 0.31271f, 0.32902f }; /** @@ -832,8 +832,8 @@ public abstract class ColorSpace { public enum Model { /** * The RGB model is a color model with 3 components that - * refer to the three additive primiaries: red, green - * andd blue. + * refer to the three additive primaries: red, green + * and blue. */ RGB(3), /** @@ -2405,7 +2405,7 @@ public abstract class ColorSpace { * does not need to be specified and is assumed to be 1.0. Only the xy components * are required.</p> * - * <p class="note">The ID, areturned by {@link #getId()}, of an object created by + * <p class="note">The ID, as returned by {@link #getId()}, of an object created by * this constructor is always {@link #MIN_ID}.</p> * * @param name Name of the color space, cannot be null, its length must be >= 1 @@ -3832,7 +3832,7 @@ public abstract class ColorSpace { * * <p>We can only connect color spaces if they use the same profile * connection space. We assume the connection space is always - * CIE XYZ but we maye need to perform a chromatic adaptation to + * CIE XYZ but we maybe need to perform a chromatic adaptation to * match the white points. If an adaptation is needed, we use the * CIE standard illuminant D50. The unmatched color space is adapted * using the von Kries transform and the {@link Adaptation#BRADFORD} diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp index 09be630dc741..d78df3dca3b1 100644 --- a/libs/hwui/jni/fonts/Font.cpp +++ b/libs/hwui/jni/fonts/Font.cpp @@ -105,8 +105,9 @@ static jlong Font_Builder_build(JNIEnv* env, jobject clazz, jlong builderPtr, jo std::move(data), std::string_view(fontPath.c_str(), fontPath.size()), fontPtr, fontSize, ttcIndex, builder->axes); if (minikinFont == nullptr) { - jniThrowException(env, "java/lang/IllegalArgumentException", - "Failed to create internal object. maybe invalid font data."); + jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", + "Failed to create internal object. maybe invalid font data. filePath %s", + fontPath.c_str()); return 0; } uint32_t localeListId = minikin::registerLocaleList(langTagStr.c_str()); diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index c8187b89b0ed..8b2eebe8afe5 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -208,7 +208,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> synchronized (mProfileLock) { if (profile instanceof A2dpProfile || profile instanceof HeadsetProfile - || profile instanceof HearingAidProfile) { + || profile instanceof HearingAidProfile || profile instanceof LeAudioProfile) { setProfileConnectedStatus(profile.getProfileId(), false); switch (newProfileState) { case BluetoothProfile.STATE_CONNECTED: @@ -226,7 +226,20 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> case BluetoothProfile.STATE_DISCONNECTED: if (mHandler.hasMessages(profile.getProfileId())) { mHandler.removeMessages(profile.getProfileId()); - setProfileConnectedStatus(profile.getProfileId(), true); + if (profile.getConnectionPolicy(mDevice) > + BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) { + /* + * If we received state DISCONNECTED and previous state was + * CONNECTING and connection policy is FORBIDDEN or UNKNOWN + * then it's not really a failure to connect. + * + * Connection profile is considered as failed when connection + * policy indicates that profile should be connected + * but it got disconnected. + */ + Log.w(TAG, "onProfileStateChanged(): Failed to connect profile"); + setProfileConnectedStatus(profile.getProfileId(), true); + } } break; default: @@ -1188,6 +1201,13 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> } private boolean isProfileConnectedFail() { + Log.d(TAG, "anonymizedAddress=" + mDevice.getAnonymizedAddress() + + " mIsA2dpProfileConnectedFail=" + mIsA2dpProfileConnectedFail + + " mIsHearingAidProfileConnectedFail=" + mIsHearingAidProfileConnectedFail + + " mIsLeAudioProfileConnectedFail=" + mIsLeAudioProfileConnectedFail + + " mIsHeadsetProfileConnectedFail=" + mIsHeadsetProfileConnectedFail + + " isConnectedSapDevice()=" + isConnectedSapDevice()); + return mIsA2dpProfileConnectedFail || mIsHearingAidProfileConnectedFail || (!isConnectedSapDevice() && mIsHeadsetProfileConnectedFail) || mIsLeAudioProfileConnectedFail; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java index 315ab0aac878..df3861a699b0 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java @@ -74,6 +74,8 @@ public class CachedBluetoothDeviceTest { @Mock private HearingAidProfile mHearingAidProfile; @Mock + private LeAudioProfile mLeAudioProfile; + @Mock private BluetoothDevice mDevice; @Mock private BluetoothDevice mSubDevice; @@ -92,15 +94,76 @@ public class CachedBluetoothDeviceTest { mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter()); when(mDevice.getAddress()).thenReturn(DEVICE_ADDRESS); when(mHfpProfile.isProfileReady()).thenReturn(true); + when(mHfpProfile.getProfileId()).thenReturn(BluetoothProfile.HEADSET); when(mA2dpProfile.isProfileReady()).thenReturn(true); + when(mA2dpProfile.getProfileId()).thenReturn(BluetoothProfile.A2DP); when(mPanProfile.isProfileReady()).thenReturn(true); + when(mPanProfile.getProfileId()).thenReturn(BluetoothProfile.PAN); when(mHearingAidProfile.isProfileReady()).thenReturn(true); + when(mHearingAidProfile.getProfileId()).thenReturn(BluetoothProfile.HEARING_AID); + when(mLeAudioProfile.isProfileReady()).thenReturn(true); + when(mLeAudioProfile.getProfileId()).thenReturn(BluetoothProfile.LE_AUDIO); mCachedDevice = spy(new CachedBluetoothDevice(mContext, mProfileManager, mDevice)); mSubCachedDevice = spy(new CachedBluetoothDevice(mContext, mProfileManager, mSubDevice)); doAnswer((invocation) -> mBatteryLevel).when(mCachedDevice).getBatteryLevel(); doAnswer((invocation) -> mBatteryLevel).when(mSubCachedDevice).getBatteryLevel(); } + private void testTransitionFromConnectingToDisconnected( + LocalBluetoothProfile connectingProfile, LocalBluetoothProfile connectedProfile, + int connectionPolicy, String expectedSummary) { + // Arrange: + // At least one profile has to be connected + updateProfileStatus(connectedProfile, BluetoothProfile.STATE_CONNECTED); + // Set profile under test to CONNECTING + updateProfileStatus(connectingProfile, BluetoothProfile.STATE_CONNECTING); + // Set connection policy + when(connectingProfile.getConnectionPolicy(mDevice)).thenReturn(connectionPolicy); + + // Act & Assert: + // Get the expected connection summary. + updateProfileStatus(connectingProfile, BluetoothProfile.STATE_DISCONNECTED); + assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(expectedSummary); + } + + @Test + public void onProfileStateChanged_testConnectingToDisconnected_policyAllowed_problem() { + String connectTimeoutString = mContext.getString(R.string.profile_connect_timeout_subtext); + + testTransitionFromConnectingToDisconnected(mA2dpProfile, mLeAudioProfile, + BluetoothProfile.CONNECTION_POLICY_ALLOWED, connectTimeoutString); + testTransitionFromConnectingToDisconnected(mHearingAidProfile, mLeAudioProfile, + BluetoothProfile.CONNECTION_POLICY_ALLOWED, connectTimeoutString); + testTransitionFromConnectingToDisconnected(mHfpProfile, mLeAudioProfile, + BluetoothProfile.CONNECTION_POLICY_ALLOWED, connectTimeoutString); + testTransitionFromConnectingToDisconnected(mLeAudioProfile, mA2dpProfile, + BluetoothProfile.CONNECTION_POLICY_ALLOWED, connectTimeoutString); + } + + @Test + public void onProfileStateChanged_testConnectingToDisconnected_policyForbidden_noProblem() { + testTransitionFromConnectingToDisconnected(mA2dpProfile, mLeAudioProfile, + BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, null); + testTransitionFromConnectingToDisconnected(mHearingAidProfile, mLeAudioProfile, + BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, null); + testTransitionFromConnectingToDisconnected(mHfpProfile, mLeAudioProfile, + BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, null); + testTransitionFromConnectingToDisconnected(mLeAudioProfile, mA2dpProfile, + BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, null); + } + + @Test + public void onProfileStateChanged_testConnectingToDisconnected_policyUnknown_noProblem() { + testTransitionFromConnectingToDisconnected(mA2dpProfile, mLeAudioProfile, + BluetoothProfile.CONNECTION_POLICY_UNKNOWN, null); + testTransitionFromConnectingToDisconnected(mHearingAidProfile, mLeAudioProfile, + BluetoothProfile.CONNECTION_POLICY_UNKNOWN, null); + testTransitionFromConnectingToDisconnected(mHfpProfile, mLeAudioProfile, + BluetoothProfile.CONNECTION_POLICY_UNKNOWN, null); + testTransitionFromConnectingToDisconnected(mLeAudioProfile, mA2dpProfile, + BluetoothProfile.CONNECTION_POLICY_UNKNOWN, null); + } + @Test public void getConnectionSummary_testProfilesInactive_returnPairing() { // Arrange: diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index ca86021cd629..a69d3f0276f3 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -91,6 +91,7 @@ import android.util.Pair; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; +import com.android.internal.telephony.ICarrierConfigChangeListener; import com.android.internal.telephony.ICarrierPrivilegesCallback; import com.android.internal.telephony.IOnSubscriptionsChangedListener; import com.android.internal.telephony.IPhoneStateListener; @@ -154,6 +155,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { IOnSubscriptionsChangedListener onSubscriptionsChangedListenerCallback; IOnSubscriptionsChangedListener onOpportunisticSubscriptionsChangedListenerCallback; ICarrierPrivilegesCallback carrierPrivilegesCallback; + ICarrierConfigChangeListener carrierConfigChangeListener; int callerUid; int callerPid; @@ -182,6 +184,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { return carrierPrivilegesCallback != null; } + boolean matchCarrierConfigChangeListener() { + return carrierConfigChangeListener != null; + } + boolean canReadCallLog() { try { return TelephonyPermissions.checkReadCallLog( @@ -200,6 +206,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { + " onOpportunisticSubscriptionsChangedListenererCallback=" + onOpportunisticSubscriptionsChangedListenerCallback + " carrierPrivilegesCallback=" + carrierPrivilegesCallback + + " carrierConfigChangeListener=" + carrierConfigChangeListener + " subId=" + subId + " phoneId=" + phoneId + " events=" + eventList + "}"; } } @@ -2955,6 +2962,82 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } + @Override + public void addCarrierConfigChangeListener(ICarrierConfigChangeListener listener, + String pkg, String featureId) { + final int callerUserId = UserHandle.getCallingUserId(); + mAppOps.checkPackage(Binder.getCallingUid(), pkg); + if (VDBG) { + log("addCarrierConfigChangeListener pkg=" + pii(pkg) + " uid=" + Binder.getCallingUid() + + " myUserId=" + UserHandle.myUserId() + " callerUerId" + callerUserId + + " listener=" + listener + " listener.asBinder=" + listener.asBinder()); + } + + synchronized (mRecords) { + IBinder b = listener.asBinder(); + boolean doesLimitApply = doesLimitApplyForListeners(Binder.getCallingUid(), + Process.myUid()); + Record r = add(b, Binder.getCallingUid(), Binder.getCallingPid(), doesLimitApply); + + if (r == null) { + loge("Can not create Record instance!"); + return; + } + + r.context = mContext; + r.carrierConfigChangeListener = listener; + r.callingPackage = pkg; + r.callingFeatureId = featureId; + r.callerUid = Binder.getCallingUid(); + r.callerPid = Binder.getCallingPid(); + r.eventList = new ArraySet<>(); + if (DBG) { + log("addCarrierConfigChangeListener: Register r=" + r); + } + } + } + + @Override + public void removeCarrierConfigChangeListener(ICarrierConfigChangeListener listener, + String pkg) { + if (DBG) log("removeCarrierConfigChangeListener listener=" + listener + ", pkg=" + pkg); + mAppOps.checkPackage(Binder.getCallingUid(), pkg); + remove(listener.asBinder()); + } + + @Override + public void notifyCarrierConfigChanged(int phoneId, int subId, int carrierId, + int specificCarrierId) { + if (!validatePhoneId(phoneId)) { + throw new IllegalArgumentException("Invalid phoneId: " + phoneId); + } + if (!checkNotifyPermission("notifyCarrierConfigChanged")) { + loge("Caller has no notify permission!"); + return; + } + if (VDBG) { + log("notifyCarrierConfigChanged: phoneId=" + phoneId + ", subId=" + subId + + ", carrierId=" + carrierId + ", specificCarrierId=" + specificCarrierId); + } + + synchronized (mRecords) { + mRemoveList.clear(); + for (Record r : mRecords) { + // Listeners are "global", neither per-slot nor per-sub, so no idMatch check here + if (!r.matchCarrierConfigChangeListener()) { + continue; + } + try { + r.carrierConfigChangeListener.onCarrierConfigChanged(phoneId, subId, carrierId, + specificCarrierId); + } catch (RemoteException re) { + mRemoveList.add(r.binder); + } + } + handleRemoveListLocked(); + } + } + @NeverCompile // Avoid size overhead of debugging code. @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 1ce917cb7841..6d3f8fd77232 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -21,6 +21,7 @@ import static android.Manifest.permission.CONTROL_VPN; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_VPN; import static android.net.RouteInfo.RTN_THROW; import static android.net.RouteInfo.RTN_UNREACHABLE; @@ -29,6 +30,7 @@ import static android.net.ipsec.ike.IkeSessionParams.ESP_ENCAP_TYPE_AUTO; import static android.net.ipsec.ike.IkeSessionParams.ESP_IP_VERSION_AUTO; import static android.os.PowerWhitelistManager.REASON_VPN; import static android.os.UserHandle.PER_USER_RANGE; +import static android.telephony.CarrierConfigManager.KEY_MIN_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT; import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU; import static com.android.server.vcn.util.PersistableBundleUtils.STRING_DESERIALIZER; @@ -79,7 +81,9 @@ import android.net.NetworkInfo.DetailedState; import android.net.NetworkProvider; import android.net.NetworkRequest; import android.net.NetworkScore; +import android.net.NetworkSpecifier; import android.net.RouteInfo; +import android.net.TelephonyNetworkSpecifier; import android.net.UidRangeParcel; import android.net.UnderlyingNetworkInfo; import android.net.Uri; @@ -127,12 +131,16 @@ import android.security.keystore.KeyProperties; import android.system.keystore2.Domain; import android.system.keystore2.KeyDescriptor; import android.system.keystore2.KeyPermission; +import android.telephony.CarrierConfigManager; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.ArraySet; import android.util.IndentingPrintWriter; import android.util.LocalLog; import android.util.Log; import android.util.Range; +import android.util.SparseArray; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; @@ -268,6 +276,10 @@ public class Vpn { private final ConnectivityManager mConnectivityManager; private final AppOpsManager mAppOpsManager; private final ConnectivityDiagnosticsManager mConnectivityDiagnosticsManager; + private final TelephonyManager mTelephonyManager; + private final CarrierConfigManager mCarrierConfigManager; + private final SubscriptionManager mSubscriptionManager; + // The context is for specific user which is created from mUserId private final Context mUserIdContext; @VisibleForTesting final Dependencies mDeps; @@ -314,6 +326,14 @@ public class Vpn { private final LocalLog mVpnManagerEvents = new LocalLog(MAX_EVENTS_LOGS); /** + * Cached Map of <subscription ID, keepalive delay ms> since retrieving the PersistableBundle + * and the target value from CarrierConfigManager is somewhat expensive as it has hundreds of + * fields. This cache is cleared when the carrier config changes to ensure data freshness. + */ + @GuardedBy("this") + private final SparseArray<Integer> mCachedKeepalivePerSubId = new SparseArray<>(); + + /** * Whether to keep the connection active after rebooting, or upgrading or reinstalling. This * only applies to {@link VpnService} connections. */ @@ -626,6 +646,10 @@ public class Vpn { mUserIdContext = context.createContextAsUser(UserHandle.of(userId), 0 /* flags */); mConnectivityDiagnosticsManager = mContext.getSystemService(ConnectivityDiagnosticsManager.class); + mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class); + mTelephonyManager = mContext.getSystemService(TelephonyManager.class); + mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class); + mDeps = deps; mNms = netService; mNetd = netd; @@ -2893,6 +2917,24 @@ public class Vpn { */ private int mRetryCount = 0; + private CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener = + new CarrierConfigManager.CarrierConfigChangeListener() { + @Override + public void onCarrierConfigChanged(int slotIndex, int subId, int carrierId, + int specificCarrierId) { + synchronized (Vpn.this) { + mCachedKeepalivePerSubId.remove(subId); + + // Ignore stale runner. + if (mVpnRunner != Vpn.IkeV2VpnRunner.this) return; + + maybeMigrateIkeSession(mActiveNetwork); + } + // TODO: update the longLivedTcpConnectionsExpensive value in the + // networkcapabilities of the VPN network. + } + }; + IkeV2VpnRunner( @NonNull Ikev2VpnProfile profile, @NonNull ScheduledThreadPoolExecutor executor) { super(TAG); @@ -2918,6 +2960,9 @@ public class Vpn { setVpnNetworkPreference(mSessionKey, createUserAndRestrictedProfilesRanges(mUserId, mConfig.allowedApplications, mConfig.disallowedApplications)); + + mCarrierConfigManager.registerCarrierConfigChangeListener(mExecutor, + mCarrierConfigChangeListener); } @Override @@ -3257,8 +3302,6 @@ public class Vpn { mUnderlyingLinkProperties = null; mUnderlyingNetworkCapabilities = null; mRetryCount = 0; - - startOrMigrateIkeSession(network); } @NonNull @@ -3356,9 +3399,36 @@ public class Vpn { } private int guessNattKeepaliveTimerForNetwork() { - // TODO : guess the keepalive delay based on carrier if auto keepalive timer is - // enabled - return AUTOMATIC_KEEPALIVE_DELAY_SECONDS; + final int subId = getCellSubIdForNetworkCapabilities(mUnderlyingNetworkCapabilities); + if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { + Log.d(TAG, "Underlying network is not a cellular network"); + return AUTOMATIC_KEEPALIVE_DELAY_SECONDS; + } + + synchronized (Vpn.this) { + if (mCachedKeepalivePerSubId.contains(subId)) { + Log.d(TAG, "Get cached keepalive config"); + return mCachedKeepalivePerSubId.get(subId); + } + + final TelephonyManager perSubTm = mTelephonyManager.createForSubscriptionId(subId); + if (perSubTm.getSimApplicationState() != TelephonyManager.SIM_STATE_LOADED) { + Log.d(TAG, "SIM card is not ready on sub " + subId); + return AUTOMATIC_KEEPALIVE_DELAY_SECONDS; + } + + final PersistableBundle carrierConfig = + mCarrierConfigManager.getConfigForSubId(subId); + if (!CarrierConfigManager.isConfigForIdentifiedCarrier(carrierConfig)) { + return AUTOMATIC_KEEPALIVE_DELAY_SECONDS; + } + + final int natKeepalive = + carrierConfig.getInt(KEY_MIN_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT); + mCachedKeepalivePerSubId.put(subId, natKeepalive); + Log.d(TAG, "Get customized keepalive=" + natKeepalive); + return natKeepalive; + } } boolean maybeMigrateIkeSession(@NonNull Network underlyingNetwork) { @@ -3456,7 +3526,15 @@ public class Vpn { /** Called when the NetworkCapabilities of underlying network is changed */ public void onDefaultNetworkCapabilitiesChanged(@NonNull NetworkCapabilities nc) { + final NetworkCapabilities oldNc = mUnderlyingNetworkCapabilities; mUnderlyingNetworkCapabilities = nc; + if (oldNc == null) { + // A new default network is available. + startOrMigrateIkeSession(mActiveNetwork); + } else if (!nc.getSubscriptionIds().equals(oldNc.getSubscriptionIds())) { + // Renew carrierConfig values. + maybeMigrateIkeSession(mActiveNetwork); + } } /** Called when the LinkProperties of underlying network is changed */ @@ -3812,6 +3890,8 @@ public class Vpn { resetIkeState(); + mCarrierConfigManager.unregisterCarrierConfigChangeListener( + mCarrierConfigChangeListener); mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); mConnectivityDiagnosticsManager.unregisterConnectivityDiagnosticsCallback( mDiagnosticsCallback); @@ -4804,6 +4884,8 @@ public class Vpn { pw.println("Reset session scheduled"); } } + pw.println("mCachedKeepalivePerSubId=" + mCachedKeepalivePerSubId); + pw.println("mUnderlyNetworkChanges (most recent first):"); pw.increaseIndent(); mUnderlyNetworkChanges.reverseDump(pw); @@ -4815,4 +4897,19 @@ public class Vpn { pw.decreaseIndent(); } } + + private static int getCellSubIdForNetworkCapabilities(@Nullable NetworkCapabilities nc) { + if (nc == null) return SubscriptionManager.INVALID_SUBSCRIPTION_ID; + + if (!nc.hasTransport(TRANSPORT_CELLULAR)) { + return SubscriptionManager.INVALID_SUBSCRIPTION_ID; + } + + final NetworkSpecifier specifier = nc.getNetworkSpecifier(); + if (specifier instanceof TelephonyNetworkSpecifier) { + return ((TelephonyNetworkSpecifier) specifier).getSubscriptionId(); + } + + return SubscriptionManager.INVALID_SUBSCRIPTION_ID; + } } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index e2c4cbdc829c..dcf19062834c 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -1650,15 +1650,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mContext, 0, snoozeIntent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE)); final Intent viewIntent = buildViewDataUsageIntent(res, policy.template); - // TODO: Resolve to single code path. - if (UserManager.isHeadlessSystemUserMode()) { - builder.setContentIntent(PendingIntent.getActivityAsUser( - mContext, 0, viewIntent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE, - /* options= */ null, UserHandle.CURRENT)); - } else { - builder.setContentIntent(PendingIntent.getActivity( - mContext, 0, viewIntent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE)); - } + setContentIntent(builder, viewIntent); break; } case TYPE_LIMIT: { @@ -1679,15 +1671,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { builder.setSmallIcon(R.drawable.stat_notify_disabled_data); final Intent intent = buildNetworkOverLimitIntent(res, policy.template); - // TODO: Resolve to single code path. - if (UserManager.isHeadlessSystemUserMode()) { - builder.setContentIntent(PendingIntent.getActivityAsUser( - mContext, 0, intent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE, - /* options= */ null, UserHandle.CURRENT)); - } else { - builder.setContentIntent(PendingIntent.getActivity( - mContext, 0, intent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE)); - } + setContentIntent(builder, intent); break; } case TYPE_LIMIT_SNOOZED: { @@ -1711,15 +1695,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { builder.setChannelId(SystemNotificationChannels.NETWORK_STATUS); final Intent intent = buildViewDataUsageIntent(res, policy.template); - // TODO: Resolve to single code path. - if (UserManager.isHeadlessSystemUserMode()) { - builder.setContentIntent(PendingIntent.getActivityAsUser( - mContext, 0, intent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE, - /* options= */ null, UserHandle.CURRENT)); - } else { - builder.setContentIntent(PendingIntent.getActivity( - mContext, 0, intent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE)); - } + setContentIntent(builder, intent); break; } case TYPE_RAPID: { @@ -1739,15 +1715,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mContext, 0, snoozeIntent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE)); final Intent viewIntent = buildViewDataUsageIntent(res, policy.template); - // TODO: Resolve to single code path. - if (UserManager.isHeadlessSystemUserMode()) { - builder.setContentIntent(PendingIntent.getActivityAsUser( - mContext, 0, viewIntent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE, - /* options= */ null, UserHandle.CURRENT)); - } else { - builder.setContentIntent(PendingIntent.getActivity( - mContext, 0, viewIntent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE)); - } + setContentIntent(builder, viewIntent); break; } default: { @@ -1765,6 +1733,17 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mActiveNotifs.add(notificationId); } + private void setContentIntent(Notification.Builder builder, Intent intent) { + if (UserManager.isHeadlessSystemUserMode()) { + builder.setContentIntent(PendingIntent.getActivityAsUser( + mContext, 0, intent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE, + /* options= */ null, UserHandle.CURRENT)); + } else { + builder.setContentIntent(PendingIntent.getActivity( + mContext, 0, intent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE)); + } + } + private void cancelNotification(NotificationId notificationId) { mContext.getSystemService(NotificationManager.class).cancel(notificationId.getTag(), notificationId.getId()); diff --git a/services/core/java/com/android/server/notification/OWNERS b/services/core/java/com/android/server/notification/OWNERS index 5a19656b36a6..6c4dd6d13d92 100644 --- a/services/core/java/com/android/server/notification/OWNERS +++ b/services/core/java/com/android/server/notification/OWNERS @@ -1,4 +1,6 @@ -dsandler@android.com +# Bug component: 34005 + juliacr@google.com -beverlyt@google.com -pixel@google.com +yurilin@google.com +jeffdq@google.com +dsandler@android.com
\ No newline at end of file diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index b8486e7aa2b4..49cab3da1966 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -557,6 +557,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A Drawable mEnterpriseThumbnailDrawable; + boolean mPauseSchedulePendingForPip = false; + private void updateEnterpriseThumbnailDrawable(Context context) { DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class); mEnterpriseThumbnailDrawable = dpm.getResources().getDrawable( @@ -1709,7 +1711,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A : null; } - private void clearLastParentBeforePip() { + void clearLastParentBeforePip() { if (mLastParentBeforePip != null) { mLastParentBeforePip.mChildPipActivity = null; mLastParentBeforePip = null; diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 12714ed51c73..14927f9eee7e 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -3583,7 +3583,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { null /* launchIntoPipHostActivity */, "enterPictureInPictureMode", transition); // Continue the pausing process after entering pip. - if (r.isState(PAUSING)) { + if (r.isState(PAUSING) && r.mPauseSchedulePendingForPip) { r.getTask().schedulePauseActivity(r, false /* userLeaving */, false /* pauseImmediately */, true /* autoEnteringPip */, "auto-pip"); } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 8993840a529e..a7c2733686b7 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -2569,6 +2569,9 @@ class Task extends TaskFragment { EventLogTags.writeWmTaskRemoved(mTaskId, reason); clearPinnedTaskIfNeed(); + if (mChildPipActivity != null) { + mChildPipActivity.clearLastParentBeforePip(); + } // If applicable let the TaskOrganizer know the Task is vanishing. setTaskOrganizer(null); diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index 1a3e7d195859..ab979ad8340a 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -1619,6 +1619,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { if (prev.attachedToProcess()) { if (shouldAutoPip) { + prev.mPauseSchedulePendingForPip = true; boolean didAutoPip = mAtmService.enterPictureInPictureMode( prev, prev.pictureInPictureArgs, false /* fromClient */); ProtoLog.d(WM_DEBUG_STATES, "Auto-PIP allowed, entering PIP mode " @@ -1682,6 +1683,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { boolean pauseImmediately, boolean autoEnteringPip, String reason) { ProtoLog.v(WM_DEBUG_STATES, "Enqueueing pending pause: %s", prev); try { + prev.mPauseSchedulePendingForPip = false; EventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev), prev.shortComponentName, "userLeaving=" + userLeaving, reason); diff --git a/services/tests/servicestests/src/com/android/server/dreams/OWNERS b/services/tests/servicestests/src/com/android/server/dreams/OWNERS new file mode 100644 index 000000000000..2f19cf557966 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/dreams/OWNERS @@ -0,0 +1 @@ +include /core/java/android/service/dreams/OWNERS diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index c98ea7b16c48..5ad5001cb121 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -17,6 +17,7 @@ package android.telephony; import android.Manifest; +import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -53,6 +54,7 @@ import com.android.telephony.Rlog; import java.util.List; import java.util.Objects; +import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; /** @@ -8572,6 +8574,16 @@ public class CarrierConfigManager { public static final String KEY_VONR_ENABLED_BOOL = "vonr_enabled_bool"; /** + * Boolean indicating the default VoNR user preference setting. + * If true, the VoNR setting will be enabled. If false, it will be disabled initially. + * + * Enabled by default. + * + * @hide + */ + public static final String KEY_VONR_ON_BY_DEFAULT_BOOL = "vonr_on_by_default_bool"; + + /** * Determine whether unthrottle data retry when tracking area code (TAC/LAC) from cell changes * * @hide @@ -9252,6 +9264,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_UNTHROTTLE_DATA_RETRY_WHEN_TAC_CHANGES_BOOL, false); sDefaults.putBoolean(KEY_VONR_SETTING_VISIBILITY_BOOL, true); sDefaults.putBoolean(KEY_VONR_ENABLED_BOOL, false); + sDefaults.putBoolean(KEY_VONR_ON_BY_DEFAULT_BOOL, true); sDefaults.putStringArray(KEY_IWLAN_HANDOVER_POLICY_STRING_ARRAY, new String[]{ "source=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, " + "target=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, type=allowed"}); @@ -9715,4 +9728,85 @@ public class CarrierConfigManager { configs.putPersistableBundle(key, (PersistableBundle) value); } } + + /** + * Listener interface to get a notification when the carrier configurations have changed. + * + * Use this listener to receive timely updates when the carrier configuration changes. System + * components should prefer this listener over {@link #ACTION_CARRIER_CONFIG_CHANGED} + * whenever possible. + * + * To register the listener, call + * {@link #registerCarrierConfigChangeListener(Executor, CarrierConfigChangeListener)}. + * To unregister, call + * {@link #unregisterCarrierConfigChangeListener(CarrierConfigChangeListener)}. + * + * Note that on registration, registrants will NOT receive a notification on last carrier config + * change. Only carrier configs change AFTER the registration will be sent to registrants. And + * unlike {@link #ACTION_CARRIER_CONFIG_CHANGED}, notification wouldn't send when the device is + * unlocked. Registrants only receive the notification when there has been real carrier config + * changes. + * + * @see #registerCarrierConfigChangeListener(Executor, CarrierConfigChangeListener) + * @see #unregisterCarrierConfigChangeListener(CarrierConfigChangeListener) + * @see #ACTION_CARRIER_CONFIG_CHANGED + * @see #getConfig(String...) + * @see #getConfigForSubId(int, String...) + */ + public interface CarrierConfigChangeListener { + /** + * Called when carrier configurations have changed. + * + * @param logicalSlotIndex The logical SIM slot index on which to monitor and get + * notification. It is guaranteed to be valid. + * @param subscriptionId The subscription on the SIM slot. May be + * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}. + * @param carrierId The optional carrier Id, may be + * {@link TelephonyManager#UNKNOWN_CARRIER_ID}. + * See {@link TelephonyManager#getSimCarrierId()}. + * @param specificCarrierId The optional fine-grained carrierId, may be {@link + * TelephonyManager#UNKNOWN_CARRIER_ID}. A specific carrierId may + * be different from the carrierId above in a MVNO scenario. See + * detail in {@link TelephonyManager#getSimSpecificCarrierId()}. + */ + void onCarrierConfigChanged(int logicalSlotIndex, int subscriptionId, int carrierId, + int specificCarrierId); + } + + /** + * Register a {@link CarrierConfigChangeListener} to get a notification when carrier + * configurations have changed. + * + * @param executor The executor on which the listener will be called. + * @param listener The CarrierConfigChangeListener called when carrier configs has changed. + */ + public void registerCarrierConfigChangeListener(@NonNull @CallbackExecutor Executor executor, + @NonNull CarrierConfigChangeListener listener) { + Objects.requireNonNull(executor, "Executor should be non-null."); + Objects.requireNonNull(listener, "Listener should be non-null."); + + TelephonyRegistryManager trm = mContext.getSystemService(TelephonyRegistryManager.class); + if (trm == null) { + throw new IllegalStateException("Telephony registry service is null"); + } + trm.addCarrierConfigChangedListener(executor, listener); + } + + /** + * Unregister the {@link CarrierConfigChangeListener} to stop notification on carrier + * configurations change. + * + * @param listener The CarrierConfigChangeListener which was registered with method + * {@link #registerCarrierConfigChangeListener(Executor, CarrierConfigChangeListener)}. + */ + public void unregisterCarrierConfigChangeListener( + @NonNull CarrierConfigChangeListener listener) { + Objects.requireNonNull(listener, "Listener should be non-null."); + + TelephonyRegistryManager trm = mContext.getSystemService(TelephonyRegistryManager.class); + if (trm == null) { + throw new IllegalStateException("Telephony registry service is null"); + } + trm.removeCarrierConfigChangedListener(listener); + } } diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java index e055f637b72c..2ddc98aca90c 100644 --- a/telephony/java/android/telephony/SubscriptionInfo.java +++ b/telephony/java/android/telephony/SubscriptionInfo.java @@ -649,6 +649,15 @@ public class SubscriptionInfo implements Parcelable { } /** + * @return {@code true} if the subscription is from the actively used SIM. + * + * @hide + */ + public boolean isActive() { + return mSimSlotIndex >= 0 || mType == SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM; + } + + /** * Used in scenarios where different subscriptions are bundled as a group. * It's typically a primary and an opportunistic subscription. (see {@link #isOpportunistic()}) * Such that those subscriptions will have some affiliated behaviors such as opportunistic diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 682bd9cb868e..5c1a00604020 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -155,6 +155,10 @@ public class SubscriptionManager { private static final String CACHE_KEY_SLOT_INDEX_PROPERTY = "cache_key.telephony.get_slot_index"; + /** The IPC cache key shared by all subscription manager service cacheable properties. */ + private static final String CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY = + "cache_key.telephony.subscription_manager_service"; + /** @hide */ public static final String GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME = "getSimSpecificSettings"; @@ -246,37 +250,72 @@ public class SubscriptionManager { CACHE_KEY_DEFAULT_SUB_ID_PROPERTY, INVALID_SUBSCRIPTION_ID); + private static VoidPropertyInvalidatedCache<Integer> sGetDefaultSubIdCache = + new VoidPropertyInvalidatedCache<>(ISub::getDefaultSubId, + CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY, + INVALID_SUBSCRIPTION_ID); + private static VoidPropertyInvalidatedCache<Integer> sDefaultDataSubIdCache = new VoidPropertyInvalidatedCache<>(ISub::getDefaultDataSubId, CACHE_KEY_DEFAULT_DATA_SUB_ID_PROPERTY, INVALID_SUBSCRIPTION_ID); + private static VoidPropertyInvalidatedCache<Integer> sGetDefaultDataSubIdCache = + new VoidPropertyInvalidatedCache<>(ISub::getDefaultDataSubId, + CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY, + INVALID_SUBSCRIPTION_ID); + private static VoidPropertyInvalidatedCache<Integer> sDefaultSmsSubIdCache = new VoidPropertyInvalidatedCache<>(ISub::getDefaultSmsSubId, CACHE_KEY_DEFAULT_SMS_SUB_ID_PROPERTY, INVALID_SUBSCRIPTION_ID); + private static VoidPropertyInvalidatedCache<Integer> sGetDefaultSmsSubIdCache = + new VoidPropertyInvalidatedCache<>(ISub::getDefaultSmsSubId, + CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY, + INVALID_SUBSCRIPTION_ID); + private static VoidPropertyInvalidatedCache<Integer> sActiveDataSubIdCache = new VoidPropertyInvalidatedCache<>(ISub::getActiveDataSubscriptionId, CACHE_KEY_ACTIVE_DATA_SUB_ID_PROPERTY, INVALID_SUBSCRIPTION_ID); + private static VoidPropertyInvalidatedCache<Integer> sGetActiveDataSubscriptionIdCache = + new VoidPropertyInvalidatedCache<>(ISub::getActiveDataSubscriptionId, + CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY, + INVALID_SUBSCRIPTION_ID); + private static IntegerPropertyInvalidatedCache<Integer> sSlotIndexCache = new IntegerPropertyInvalidatedCache<>(ISub::getSlotIndex, CACHE_KEY_SLOT_INDEX_PROPERTY, INVALID_SIM_SLOT_INDEX); + private static IntegerPropertyInvalidatedCache<Integer> sGetSlotIndexCache = + new IntegerPropertyInvalidatedCache<>(ISub::getSlotIndex, + CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY, + INVALID_SIM_SLOT_INDEX); + private static IntegerPropertyInvalidatedCache<Integer> sSubIdCache = new IntegerPropertyInvalidatedCache<>(ISub::getSubId, CACHE_KEY_SLOT_INDEX_PROPERTY, INVALID_SUBSCRIPTION_ID); + private static IntegerPropertyInvalidatedCache<Integer> sGetSubIdCache = + new IntegerPropertyInvalidatedCache<>(ISub::getSubId, + CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY, + INVALID_SUBSCRIPTION_ID); + /** Cache depends on getDefaultSubId, so we use the defaultSubId cache key */ private static IntegerPropertyInvalidatedCache<Integer> sPhoneIdCache = new IntegerPropertyInvalidatedCache<>(ISub::getPhoneId, CACHE_KEY_DEFAULT_SUB_ID_PROPERTY, INVALID_PHONE_INDEX); + private static IntegerPropertyInvalidatedCache<Integer> sGetPhoneIdCache = + new IntegerPropertyInvalidatedCache<>(ISub::getPhoneId, + CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY, + INVALID_PHONE_INDEX); + /** * Generates a content {@link Uri} used to receive updates on simInfo change * on the given subscriptionId @@ -1275,6 +1314,8 @@ public class SubscriptionManager { private final Context mContext; + private static boolean sIsSubscriptionManagerServiceEnabled = false; + // Cache of Resource that has been created in getResourcesForSubId. Key is a Pair containing // the Context and subId. private static final Map<Pair<Context, Integer>, Resources> sResourcesCache = @@ -1360,6 +1401,19 @@ public class SubscriptionManager { public SubscriptionManager(Context context) { if (DBG) logd("SubscriptionManager created"); mContext = context; + + sIsSubscriptionManagerServiceEnabled = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_using_subscription_manager_service); + } + + /** + * @return {@code true} if the new subscription manager service is used. This is temporary and + * will be removed before Android 14 release. + * + * @hide + */ + public static boolean isSubscriptionManagerServiceEnabled() { + return sIsSubscriptionManagerServiceEnabled; } private NetworkPolicyManager getNetworkPolicyManager() { @@ -2135,6 +2189,7 @@ public class SubscriptionManager { * subscriptionId doesn't have an associated slot index. */ public static int getSlotIndex(int subscriptionId) { + if (isSubscriptionManagerServiceEnabled()) return sGetSlotIndexCache.query(subscriptionId); return sSlotIndexCache.query(subscriptionId); } @@ -2184,12 +2239,14 @@ public class SubscriptionManager { return SubscriptionManager.INVALID_SUBSCRIPTION_ID; } + if (isSubscriptionManagerServiceEnabled()) return sGetSubIdCache.query(slotIndex); return sSubIdCache.query(slotIndex); } /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) public static int getPhoneId(int subId) { + if (isSubscriptionManagerServiceEnabled()) return sGetPhoneIdCache.query(subId); return sPhoneIdCache.query(subId); } @@ -2211,6 +2268,7 @@ public class SubscriptionManager { * @return the "system" default subscription id. */ public static int getDefaultSubscriptionId() { + if (isSubscriptionManagerServiceEnabled()) return sGetDefaultSubIdCache.query(null); return sDefaultSubIdCache.query(null); } @@ -2299,6 +2357,7 @@ public class SubscriptionManager { * @return the default SMS subscription Id. */ public static int getDefaultSmsSubscriptionId() { + if (isSubscriptionManagerServiceEnabled()) return sGetDefaultSmsSubIdCache.query(null); return sDefaultSmsSubIdCache.query(null); } @@ -2333,6 +2392,7 @@ public class SubscriptionManager { * @return the default data subscription Id. */ public static int getDefaultDataSubscriptionId() { + if (isSubscriptionManagerServiceEnabled()) return sGetDefaultDataSubIdCache.query(null); return sDefaultDataSubIdCache.query(null); } @@ -3795,6 +3855,9 @@ public class SubscriptionManager { * SubscriptionManager.INVALID_SUBSCRIPTION_ID if not. */ public static int getActiveDataSubscriptionId() { + if (isSubscriptionManagerServiceEnabled()) { + return sGetActiveDataSubscriptionIdCache.query(null); + } return sActiveDataSubIdCache.query(null); } @@ -3838,6 +3901,11 @@ public class SubscriptionManager { PropertyInvalidatedCache.invalidateCache(CACHE_KEY_SLOT_INDEX_PROPERTY); } + /** @hide */ + public static void invalidateSubscriptionManagerServiceCaches() { + PropertyInvalidatedCache.invalidateCache(CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY); + } + /** * Allows a test process to disable client-side caching operations. * @@ -3849,7 +3917,16 @@ public class SubscriptionManager { sActiveDataSubIdCache.disableLocal(); sDefaultSmsSubIdCache.disableLocal(); sSlotIndexCache.disableLocal(); + sSubIdCache.disableLocal(); sPhoneIdCache.disableLocal(); + + sGetDefaultSubIdCache.disableLocal(); + sGetDefaultDataSubIdCache.disableLocal(); + sGetActiveDataSubscriptionIdCache.disableLocal(); + sGetDefaultSmsSubIdCache.disableLocal(); + sGetSlotIndexCache.disableLocal(); + sGetSubIdCache.disableLocal(); + sGetPhoneIdCache.disableLocal(); } /** @@ -3862,7 +3939,16 @@ public class SubscriptionManager { sActiveDataSubIdCache.clear(); sDefaultSmsSubIdCache.clear(); sSlotIndexCache.clear(); + sSubIdCache.clear(); sPhoneIdCache.clear(); + + sGetDefaultSubIdCache.clear(); + sGetDefaultDataSubIdCache.clear(); + sGetActiveDataSubscriptionIdCache.clear(); + sGetDefaultSmsSubIdCache.clear(); + sGetSlotIndexCache.clear(); + sGetSubIdCache.clear(); + sGetPhoneIdCache.clear(); } /** |