Merge changes Ia10ca6fd,Ib58f5f38 into main
* changes:
Connected display usb errors notification
Connected display hotplug error notification
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 864caf4..2d164f8 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -14,6 +14,7 @@
aconfig_srcjars = [
":android.app.usage.flags-aconfig-java{.generated_srcjars}",
+ ":android.companion.flags-aconfig-java{.generated_srcjars}",
":android.content.pm.flags-aconfig-java{.generated_srcjars}",
":android.hardware.radio.flags-aconfig-java{.generated_srcjars}",
":android.nfc.flags-aconfig-java{.generated_srcjars}",
@@ -37,6 +38,7 @@
":android.permission.flags-aconfig-java{.generated_srcjars}",
":hwui_flags_java_lib{.generated_srcjars}",
":display_flags_lib{.generated_srcjars}",
+ ":com.android.internal.foldables.flags-aconfig-java{.generated_srcjars}",
":android.multiuser.flags-aconfig-java{.generated_srcjars}",
":android.app.flags-aconfig-java{.generated_srcjars}",
":android.credentials.flags-aconfig-java{.generated_srcjars}",
@@ -292,6 +294,13 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+java_aconfig_library {
+ name: "android.content.pm.flags-aconfig-java-host",
+ aconfig_declarations: "android.content.pm.flags-aconfig",
+ host_supported: true,
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// Media BetterTogether
aconfig_declarations {
name: "com.android.media.flags.bettertogether-aconfig",
@@ -316,6 +325,11 @@
name: "android.permission.flags-aconfig-java",
aconfig_declarations: "android.permission.flags-aconfig",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
+ min_sdk_version: "30",
+ apex_available: [
+ "com.android.permission",
+ ],
+
}
// Biometrics
@@ -345,6 +359,12 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+java_aconfig_library {
+ name: "com.android.internal.foldables.flags-aconfig-java",
+ aconfig_declarations: "fold_lock_setting_flags",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// Multi user
aconfig_declarations {
name: "android.multiuser.flags-aconfig",
@@ -429,7 +449,7 @@
package: "android.service.autofill",
srcs: [
"services/autofill/bugfixes.aconfig",
- "services/autofill/features.aconfig"
+ "services/autofill/features.aconfig",
],
}
@@ -438,3 +458,16 @@
aconfig_declarations: "android.service.autofill.flags-aconfig",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+
+// Companion
+aconfig_declarations {
+ name: "android.companion.flags-aconfig",
+ package: "android.companion",
+ srcs: ["core/java/android/companion/*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.companion.flags-aconfig-java",
+ aconfig_declarations: "android.companion.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/Android.bp b/Android.bp
index b1b332a..a507465a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -64,6 +64,7 @@
srcs: [
// Java/AIDL sources under frameworks/base
":framework-annotations",
+ ":ravenwood-annotations",
":framework-blobstore-sources",
":framework-core-sources",
":framework-drm-sources",
@@ -284,6 +285,7 @@
enforce_permissions_exceptions: [
// Do not add entries to this list.
":framework-annotations",
+ ":ravenwood-annotations",
":framework-blobstore-sources",
":framework-core-sources",
":framework-drm-sources",
@@ -409,7 +411,6 @@
"audiopolicy-aidl-java",
"sounddose-aidl-java",
"modules-utils-expresslog",
- "hoststubgen-annotations",
],
}
@@ -838,4 +839,5 @@
"AconfigFlags.bp",
"ProtoLibraries.bp",
"TestProtoLibraries.bp",
+ "Ravenwood.bp",
]
diff --git a/Android.mk b/Android.mk
index d9e202c..e2c1ed8 100644
--- a/Android.mk
+++ b/Android.mk
@@ -69,9 +69,6 @@
.PHONY: framework-doc-stubs
framework-doc-stubs: $(SDK_METADATA)
-# Run this for checkbuild
-checkbuild: doc-comment-check-docs
-
# Include subdirectory makefiles
# ============================================================
diff --git a/OWNERS b/OWNERS
index 4e5c7d8..023bdef 100644
--- a/OWNERS
+++ b/OWNERS
@@ -34,3 +34,6 @@
per-file ZYGOTE_OWNERS = file:/ZYGOTE_OWNERS
per-file SQLITE_OWNERS = file:/SQLITE_OWNERS
+
+per-file *ravenwood* = file:ravenwood/OWNERS
+per-file *Ravenwood* = file:ravenwood/OWNERS
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index bded26a..015487d 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -25,6 +25,6 @@
hidden_api_txt_exclude_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/exclude.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
-ktfmt_hook = ${REPO_ROOT}/external/ktfmt/ktfmt.py --check -i ${REPO_ROOT}/frameworks/base/packages/SystemUI/ktfmt_includes.txt ${PREUPLOAD_FILES}
+ktfmt_hook = ${REPO_ROOT}/external/ktfmt/ktfmt.py --check -i ${REPO_ROOT}/frameworks/base/ktfmt_includes.txt ${PREUPLOAD_FILES}
ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py --no-verify-format -f ${PREUPLOAD_FILES}
diff --git a/Ravenwood.bp b/Ravenwood.bp
new file mode 100644
index 0000000..9218cc9
--- /dev/null
+++ b/Ravenwood.bp
@@ -0,0 +1,70 @@
+// 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.
+
+// We need this "trampoline" rule to force soong to give a host-side jar to
+// framework-minus-apex.ravenwood. Otherwise, soong would mix up the arch (?) and we'd get
+// a dex jar.
+java_library {
+ name: "framework-minus-apex-for-hoststubgen",
+ installable: false, // host only jar.
+ static_libs: [
+ "framework-minus-apex",
+ ],
+ sdk_version: "core_platform",
+ visibility: ["//visibility:private"],
+}
+
+// Generate the stub/impl from framework-all, with hidden APIs.
+java_genrule_host {
+ name: "framework-minus-apex.ravenwood-base",
+ tools: ["hoststubgen"],
+ cmd: "$(location hoststubgen) " +
+ "@$(location :ravenwood-standard-options) " +
+
+ "--out-stub-jar $(location ravenwood_stub.jar) " +
+ "--out-impl-jar $(location ravenwood.jar) " +
+
+ "--gen-keep-all-file $(location hoststubgen_keep_all.txt) " +
+ "--gen-input-dump-file $(location hoststubgen_dump.txt) " +
+
+ "--in-jar $(location :framework-minus-apex-for-hoststubgen) " +
+ "--policy-override-file $(location framework-minus-apex-ravenwood-policies.txt) ",
+ srcs: [
+ ":framework-minus-apex-for-hoststubgen",
+ "framework-minus-apex-ravenwood-policies.txt",
+ ":ravenwood-standard-options",
+ ],
+ out: [
+ "ravenwood.jar",
+ "ravenwood_stub.jar", // It's not used. TODO: Update hoststubgen to make it optional.
+
+ // Following files are created just as FYI.
+ "hoststubgen_keep_all.txt",
+ "hoststubgen_dump.txt",
+ ],
+ visibility: ["//visibility:private"],
+}
+
+// Extract the impl jar from "framework-minus-apex.ravenwood-base" for subsequent build rules.
+java_genrule_host {
+ name: "framework-minus-apex.ravenwood",
+ cmd: "cp $(in) $(out)",
+ srcs: [
+ ":framework-minus-apex.ravenwood-base{ravenwood.jar}",
+ ],
+ out: [
+ "framework-minus-apex.ravenwood.jar",
+ ],
+ visibility: ["//visibility:public"],
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java
index 2b7438c..fdeb072 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java
@@ -536,10 +536,13 @@
static final String KEY_LAUNCH_TIME_ALLOWANCE_MS =
PC_CONSTANT_PREFIX + "launch_time_allowance_ms";
- private static final long DEFAULT_LAUNCH_TIME_THRESHOLD_MS = 7 * HOUR_IN_MILLIS;
- private static final long DEFAULT_LAUNCH_TIME_ALLOWANCE_MS = 20 * MINUTE_IN_MILLIS;
+ private static final long DEFAULT_LAUNCH_TIME_THRESHOLD_MS = HOUR_IN_MILLIS;
+ private static final long DEFAULT_LAUNCH_TIME_ALLOWANCE_MS = 30 * MINUTE_IN_MILLIS;
- /** How much time each app will have to run jobs within their standby bucket window. */
+ /**
+ * The earliest amount of time before the next estimated app launch time that we may choose
+ * to run a prefetch job for the app.
+ */
public long LAUNCH_TIME_THRESHOLD_MS = DEFAULT_LAUNCH_TIME_THRESHOLD_MS;
/**
diff --git a/api/ApiDocs.bp b/api/ApiDocs.bp
index 8d8fc12..30b4423 100644
--- a/api/ApiDocs.bp
+++ b/api/ApiDocs.bp
@@ -20,41 +20,6 @@
// The API doc generation is done by the various droiddoc modules each of which
// is for different format.
-/////////////////////////////////////////////////////////////////////
-// stub source files are generated using metalava
-/////////////////////////////////////////////////////////////////////
-
-framework_docs_only_libs = [
- "voip-common",
- "android.test.mock",
- "android-support-annotations",
- "android-support-compat",
- "android-support-core-ui",
- "android-support-core-utils",
- "android-support-design",
- "android-support-dynamic-animation",
- "android-support-exifinterface",
- "android-support-fragment",
- "android-support-media-compat",
- "android-support-percent",
- "android-support-transition",
- "android-support-v7-cardview",
- "android-support-v7-gridlayout",
- "android-support-v7-mediarouter",
- "android-support-v7-palette",
- "android-support-v7-preference",
- "android-support-v13",
- "android-support-v14-preference",
- "android-support-v17-leanback",
- "android-support-vectordrawable",
- "android-support-animatedvectordrawable",
- "android-support-v7-appcompat",
- "android-support-v7-recyclerview",
- "android-support-v8-renderscript",
- "android-support-multidex",
- "android-support-multidex-instrumentation",
-]
-
// These defaults enable doc-stub generation, api lint database generation and sdk value generation.
stubs_defaults {
name: "android-non-updatable-doc-stubs-defaults",
@@ -65,7 +30,6 @@
":android-test-mock-sources",
":android-test-runner-sources",
],
- libs: framework_docs_only_libs,
create_doc_stubs: true,
write_sdk_values: true,
}
@@ -197,7 +161,7 @@
name: "framework-docs-default",
sdk_version: "none",
system_modules: "none",
- libs: framework_docs_only_libs + [
+ libs: [
"stub-annotations",
"unsupportedappusage",
],
@@ -236,20 +200,6 @@
},
}
-doc_defaults {
- name: "framework-dokka-docs-default",
-}
-
-droiddoc {
- name: "doc-comment-check-docs",
- defaults: ["framework-docs-default"],
- srcs: [
- ":framework-doc-stubs",
- ],
- args: framework_docs_only_args + " -referenceonly -parsecomments",
- installable: false,
-}
-
droiddoc {
name: "offline-sdk-docs",
defaults: ["framework-docs-default"],
@@ -303,70 +253,6 @@
}
droiddoc {
- name: "online-sdk-docs",
- defaults: ["framework-docs-default"],
- srcs: [
- ":framework-doc-stubs",
- ],
- hdf: [
- "android.whichdoc online",
- "android.hasSamples true",
- ],
- proofread_file: "online-sdk-docs-proofread.txt",
- args: framework_docs_only_args +
- " -toroot / -samplegroup Admin " +
- " -samplegroup Background " +
- " -samplegroup Connectivity " +
- " -samplegroup Content " +
- " -samplegroup Input " +
- " -samplegroup Media " +
- " -samplegroup Notification " +
- " -samplegroup RenderScript " +
- " -samplegroup Security " +
- " -samplegroup Sensors " +
- " -samplegroup System " +
- " -samplegroup Testing " +
- " -samplegroup UI " +
- " -samplegroup Views " +
- " -samplegroup Wearable -samplesdir development/samples/browseable ",
-}
-
-droiddoc {
- name: "online-system-api-sdk-docs",
- defaults: ["framework-docs-default"],
- srcs: [
- ":framework-doc-system-stubs",
- ],
- hdf: [
- "android.whichdoc online",
- "android.hasSamples true",
- ],
- proofread_file: "online-system-api-sdk-docs-proofread.txt",
- args: framework_docs_only_args +
- " -referenceonly " +
- " -title \"Android SDK - Including system APIs.\" " +
- " -hide 101 " +
- " -hide 104 " +
- " -hide 108 " +
- " -toroot / -samplegroup Admin " +
- " -samplegroup Background " +
- " -samplegroup Connectivity " +
- " -samplegroup Content " +
- " -samplegroup Input " +
- " -samplegroup Media " +
- " -samplegroup Notification " +
- " -samplegroup RenderScript " +
- " -samplegroup Security " +
- " -samplegroup Sensors " +
- " -samplegroup System " +
- " -samplegroup Testing " +
- " -samplegroup UI " +
- " -samplegroup Views " +
- " -samplegroup Wearable -samplesdir development/samples/browseable ",
- installable: false,
-}
-
-droiddoc {
name: "ds-docs-java",
defaults: ["framework-docs-default"],
srcs: [
@@ -397,7 +283,6 @@
droiddoc {
name: "ds-docs-kt",
- defaults: ["framework-dokka-docs-default"],
srcs: [
":framework-doc-stubs",
],
@@ -476,44 +361,3 @@
" -atLinksNavtree " +
" -navtreeonly ",
}
-
-droiddoc {
- name: "online-sdk-dev-docs",
- defaults: ["framework-docs-default"],
- srcs: [
- ":framework-doc-stubs",
- ],
- hdf: [
- "android.whichdoc online",
- "android.hasSamples true",
- ],
- proofread_file: "online-sdk-dev-docs-proofread.txt",
- args: framework_docs_only_args +
- " -toroot / -samplegroup Admin " +
- " -samplegroup Background " +
- " -samplegroup Connectivity " +
- " -samplegroup Content " +
- " -samplegroup Input " +
- " -samplegroup Media " +
- " -samplegroup Notification " +
- " -samplegroup RenderScript " +
- " -samplegroup Security " +
- " -samplegroup Sensors " +
- " -samplegroup System " +
- " -samplegroup Testing " +
- " -samplegroup UI " +
- " -samplegroup Views " +
- " -samplegroup Wearable -samplesdir development/samples/browseable ",
-}
-
-droiddoc {
- name: "hidden-docs",
- defaults: ["framework-docs-default"],
- srcs: [
- ":framework-doc-stubs",
- ],
- proofread_file: "hidden-docs-proofread.txt",
- args: framework_docs_only_args +
- " -referenceonly " +
- " -title \"Android SDK - Including hidden APIs.\"",
-}
diff --git a/api/javadoc-lint-baseline b/api/javadoc-lint-baseline
index 2cc5078..1f023bd 100644
--- a/api/javadoc-lint-baseline
+++ b/api/javadoc-lint-baseline
@@ -96,13 +96,6 @@
android/content/Intent.java:4760: lint: Unresolved link/see tag "android.content.pm.UserInfo#isProfile()" in android.content.Intent [101]
android/content/Intent.java:4778: lint: Unresolved link/see tag "android.content.pm.UserInfo#isProfile()" in android.content.Intent [101]
android/content/Intent.java:4802: lint: Unresolved link/see tag "android.content.pm.UserInfo#isProfile()" in android.content.Intent [101]
-android/content/om/OverlayIdentifier.java:20: lint: Unresolved link/see tag "android.content.om.OverlayManagerTransaction.Builder#unregisterFabricatedOverlay(OverlayIdentifier)" in android.content.om.OverlayIdentifier [101]
-android/content/om/OverlayInfo.java:78: lint: Unresolved link/see tag "android.content.om.OverlayManagerTransaction.Builder#unregisterFabricatedOverlay(OverlayIdentifier)" in android.content.om.OverlayInfo [101]
-android/content/om/OverlayManager.java:9: lint: Unresolved link/see tag "android.content.om.OverlayManagerTransaction#commit()" in android.content.om.OverlayManager [101]
-android/content/pm/PackageInstaller.java:2232: lint: Unresolved link/see tag "android.Manifest.permission#INSTALL_GRANT_RUNTIME_PERMISSIONS INSTALL_GRANT_RUNTIME_PERMISSIONS" in android.content.pm.PackageInstaller.SessionParams [101]
-android/content/pm/ServiceInfo.java:176: lint: Unresolved link/see tag "android.app.job.JobInfo.Builder#setDataTransfer" in android.content.pm.ServiceInfo [101]
-android/content/pm/verify/domain/DomainVerificationUserState.java:82: lint: Unresolved link/see tag "android.content.pm.verify.domain.DomainVerificationUserState.DomainState DomainState" in android.content.pm.verify.domain.DomainVerificationUserState [101]
-android/content/res/Resources.java:958: lint: Unresolved link/see tag "android.annotation.UiContext" in android.content.res.Resources [101]
android/credentials/CreateCredentialException.java:22: lint: Unresolved link/see tag "android.credentials.CredentialManager#createCredential(android.credentials.CreateCredentialRequest,android.app.Activity,android.os.CancellationSignal,java.util.concurrent.Executor,android.os.OutcomeReceiver) CredentialManager#createCredential(CreateCredentialRequest, Activity, CancellationSignal, Executor, OutcomeReceiver)" in android.credentials.CreateCredentialException [101]
android/credentials/CreateCredentialException.java:101: lint: Unresolved link/see tag "android.credentials.CredentialManager#createCredential(android.credentials.CreateCredentialRequest,android.app.Activity,android.os.CancellationSignal,java.util.concurrent.Executor,android.os.OutcomeReceiver) CredentialManager#createCredential(CreateCredentialRequest, Activity, CancellationSignal, Executor, OutcomeReceiver)" in android.credentials.CreateCredentialException [101]
android/credentials/CreateCredentialRequest.java:107: lint: Unresolved link/see tag "androidx.credentials.CreateCredentialRequest" in android.credentials.CreateCredentialRequest.Builder [101]
@@ -261,7 +254,6 @@
android/window/BackEvent.java:24: lint: Unresolved link/see tag "android.window.BackMotionEvent BackMotionEvent" in android.window.BackEvent [101]
android/net/wifi/SoftApConfiguration.java:173: lint: Unresolved link/see tag "android.net.wifi.SoftApConfiguration.Builder#setShutdownTimeoutMillis(long)" in android.net.wifi.SoftApConfiguration [101]
-android/content/pm/ActivityInfo.java:1197: lint: Unresolved link/see tag "android.view.ViewRootImpl" in android.content.pm.ActivityInfo [101]
android/os/UserManager.java:2384: lint: Unresolved link/see tag "android.annotation.UserHandleAware @UserHandleAware" in android.os.UserManager [101]
android/os/UserManager.java:2384: lint: Unresolved link/see tag "android.annotation.UserHandleAware#enabledSinceTargetSdkVersion" in android.os.UserManager [101]
android/service/voice/AlwaysOnHotwordDetector.java:269: lint: Unresolved link/see tag "#initialize( PersistableBundle, SharedMemory, SoundTrigger.ModuleProperties)" in android.service.voice.AlwaysOnHotwordDetector [101]
diff --git a/config/preloaded-classes b/config/preloaded-classes
index aa34bad..7f8f5e3 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -6519,12 +6519,6 @@
android.security.attestationverification.AttestationVerificationManager
android.security.keymaster.ExportResult$1
android.security.keymaster.ExportResult
-android.security.keymaster.IKeyAttestationApplicationIdProvider$Stub
-android.security.keymaster.IKeyAttestationApplicationIdProvider
-android.security.keymaster.KeyAttestationApplicationId$1
-android.security.keymaster.KeyAttestationApplicationId
-android.security.keymaster.KeyAttestationPackageInfo$1
-android.security.keymaster.KeyAttestationPackageInfo
android.security.keymaster.KeyCharacteristics$1
android.security.keymaster.KeyCharacteristics
android.security.keymaster.KeymasterArgument$1
@@ -6549,7 +6543,13 @@
android.security.keystore.BackendBusyException
android.security.keystore.DelegatingX509Certificate
android.security.keystore.DeviceIdAttestationException
+android.security.keystore.IKeyAttestationApplicationIdProvider$Stub
+android.security.keystore.IKeyAttestationApplicationIdProvider
+android.security.keystore.KeyAttestationApplicationId$Stub
+android.security.keystore.KeyAttestationApplicationId
android.security.keystore.KeyAttestationException
+android.security.keystore.KeyAttestationPackageInfo$Stub
+android.security.keystore.KeyAttestationPackageInfo
android.security.keystore.KeyExpiredException
android.security.keystore.KeyGenParameterSpec$Builder
android.security.keystore.KeyGenParameterSpec
@@ -6572,6 +6572,8 @@
android.security.keystore.KeystoreResponse
android.security.keystore.ParcelableKeyGenParameterSpec$1
android.security.keystore.ParcelableKeyGenParameterSpec
+android.security.keystore.Signature$Stub
+android.security.keystore.Signature
android.security.keystore.SecureKeyImportUnavailableException
android.security.keystore.StrongBoxUnavailableException
android.security.keystore.UserAuthArgs
diff --git a/core/api/current.txt b/core/api/current.txt
index 5a1561a..f908d95 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1,4 +1,6 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
package android {
public final class Manifest {
@@ -9683,7 +9685,7 @@
method @FlaggedApi("android.companion.virtual.flags.vdm_public_apis") @NonNull public int[] getDisplayIds();
method @FlaggedApi("android.companion.virtual.flags.vdm_public_apis") @Nullable public CharSequence getDisplayName();
method @Nullable public String getName();
- method @Nullable public String getPersistentDeviceId();
+ method @FlaggedApi("android.companion.virtual.flags.vdm_public_apis") @Nullable public String getPersistentDeviceId();
method @FlaggedApi("android.companion.virtual.flags.vdm_public_apis") public boolean hasCustomSensorSupport();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.VirtualDevice> CREATOR;
@@ -16088,10 +16090,12 @@
method public String getFontFeatureSettings();
method public float getFontMetrics(android.graphics.Paint.FontMetrics);
method public android.graphics.Paint.FontMetrics getFontMetrics();
+ method @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") public void getFontMetricsForLocale(@NonNull android.graphics.Paint.FontMetrics);
method public void getFontMetricsInt(@NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, boolean, @NonNull android.graphics.Paint.FontMetricsInt);
method public void getFontMetricsInt(@NonNull char[], @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, boolean, @NonNull android.graphics.Paint.FontMetricsInt);
method public int getFontMetricsInt(android.graphics.Paint.FontMetricsInt);
method public android.graphics.Paint.FontMetricsInt getFontMetricsInt();
+ method @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") public void getFontMetricsIntForLocale(@NonNull android.graphics.Paint.FontMetricsInt);
method public float getFontSpacing();
method public String getFontVariationSettings();
method public int getHinting();
@@ -28565,6 +28569,8 @@
method @Nullable public android.nfc.NfcAntennaInfo getNfcAntennaInfo();
method public boolean ignore(android.nfc.Tag, int, android.nfc.NfcAdapter.OnTagRemovedListener, android.os.Handler);
method public boolean isEnabled();
+ method @FlaggedApi("android.nfc.enable_nfc_reader_option") public boolean isReaderOptionEnabled();
+ method @FlaggedApi("android.nfc.enable_nfc_reader_option") public boolean isReaderOptionSupported();
method public boolean isSecureNfcEnabled();
method public boolean isSecureNfcSupported();
field public static final String ACTION_ADAPTER_STATE_CHANGED = "android.nfc.action.ADAPTER_STATE_CHANGED";
@@ -31974,6 +31980,7 @@
field public static final int BATTERY_PROPERTY_CURRENT_AVERAGE = 3; // 0x3
field public static final int BATTERY_PROPERTY_CURRENT_NOW = 2; // 0x2
field public static final int BATTERY_PROPERTY_ENERGY_COUNTER = 5; // 0x5
+ field @FlaggedApi("android.os.state_of_health_public") public static final int BATTERY_PROPERTY_STATE_OF_HEALTH = 10; // 0xa
field public static final int BATTERY_PROPERTY_STATUS = 6; // 0x6
field public static final int BATTERY_STATUS_CHARGING = 2; // 0x2
field public static final int BATTERY_STATUS_DISCHARGING = 3; // 0x3
@@ -41557,7 +41564,7 @@
method public android.telecom.GatewayInfo getGatewayInfo();
method public android.net.Uri getHandle();
method public int getHandlePresentation();
- method @NonNull public String getId();
+ method @FlaggedApi("com.android.server.telecom.flags.call_details_id_changes") @NonNull public String getId();
method public android.os.Bundle getIntentExtras();
method public final int getState();
method public android.telecom.StatusHints getStatusHints();
@@ -44216,7 +44223,7 @@
field public static final int OUT_OF_NETWORK = 11; // 0xb
field public static final int OUT_OF_SERVICE = 18; // 0x12
field public static final int POWER_OFF = 17; // 0x11
- field public static final int SATELLITE_ENABLED = 82; // 0x52
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_ENABLED = 82; // 0x52
field public static final int SERVER_ERROR = 12; // 0xc
field public static final int SERVER_UNREACHABLE = 9; // 0x9
field public static final int TIMED_OUT = 13; // 0xd
@@ -44325,7 +44332,7 @@
method public boolean isNetworkRegistered();
method public boolean isNetworkRoaming();
method public boolean isNetworkSearching();
- method public boolean isNonTerrestrialNetwork();
+ method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public boolean isNonTerrestrialNetwork();
method @Deprecated public boolean isRegistered();
method @Deprecated public boolean isRoaming();
method @Deprecated public boolean isSearching();
@@ -44341,7 +44348,7 @@
field public static final int NR_STATE_RESTRICTED = 1; // 0x1
field public static final int SERVICE_TYPE_DATA = 2; // 0x2
field public static final int SERVICE_TYPE_EMERGENCY = 5; // 0x5
- field public static final int SERVICE_TYPE_MMS = 6; // 0x6
+ field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final int SERVICE_TYPE_MMS = 6; // 0x6
field public static final int SERVICE_TYPE_SMS = 3; // 0x3
field public static final int SERVICE_TYPE_UNKNOWN = 0; // 0x0
field public static final int SERVICE_TYPE_VIDEO = 4; // 0x4
@@ -44556,7 +44563,7 @@
method public boolean getRoaming();
method public int getState();
method public boolean isSearching();
- method public boolean isUsingNonTerrestrialNetwork();
+ method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public boolean isUsingNonTerrestrialNetwork();
method public void setIsManualSelection(boolean);
method public void setOperatorName(String, String, String);
method public void setRoaming(boolean);
@@ -45342,7 +45349,7 @@
field public static final int ERI_FLASH = 2; // 0x2
field public static final int ERI_OFF = 1; // 0x1
field public static final int ERI_ON = 0; // 0x0
- field public static final String EVENT_DISPLAY_SOS_MESSAGE = "android.telephony.event.DISPLAY_SOS_MESSAGE";
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final String EVENT_DISPLAY_SOS_MESSAGE = "android.telephony.event.DISPLAY_SOS_MESSAGE";
field public static final String EXTRA_ACTIVE_SIM_SUPPORTED_COUNT = "android.telephony.extra.ACTIVE_SIM_SUPPORTED_COUNT";
field public static final String EXTRA_APN_PROTOCOL = "android.telephony.extra.APN_PROTOCOL";
field public static final String EXTRA_APN_TYPE = "android.telephony.extra.APN_TYPE";
@@ -54659,7 +54666,6 @@
public final class AutofillManager {
method public void cancel();
- method public void clearAutofillRequestCallback();
method public void commit();
method public void disableAutofillServices();
method @Nullable public android.content.ComponentName getAutofillServiceComponentName();
@@ -54686,7 +54692,6 @@
method public void registerCallback(@Nullable android.view.autofill.AutofillManager.AutofillCallback);
method public void requestAutofill(@NonNull android.view.View);
method public void requestAutofill(@NonNull android.view.View, int, @NonNull android.graphics.Rect);
- method @RequiresPermission(android.Manifest.permission.PROVIDE_OWN_AUTOFILL_SUGGESTIONS) public void setAutofillRequestCallback(@NonNull java.util.concurrent.Executor, @NonNull android.view.autofill.AutofillRequestCallback);
method public void setUserData(@Nullable android.service.autofill.UserData);
method public boolean showAutofillDialog(@NonNull android.view.View);
method public boolean showAutofillDialog(@NonNull android.view.View, int);
@@ -54707,10 +54712,6 @@
field public static final int EVENT_INPUT_UNAVAILABLE = 3; // 0x3
}
- public interface AutofillRequestCallback {
- method public void onFillRequest(@Nullable android.view.inputmethod.InlineSuggestionsRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.FillCallback);
- }
-
public final class AutofillValue implements android.os.Parcelable {
method public int describeContents();
method public static android.view.autofill.AutofillValue forDate(long);
@@ -55162,12 +55163,10 @@
ctor public InlineSuggestionsRequest.Builder(@NonNull java.util.List<android.widget.inline.InlinePresentationSpec>);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder addInlinePresentationSpecs(@NonNull android.widget.inline.InlinePresentationSpec);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest build();
- method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setClientSupported(boolean);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setExtras(@NonNull android.os.Bundle);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(@NonNull java.util.List<android.widget.inline.InlinePresentationSpec>);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setInlineTooltipPresentationSpec(@NonNull android.widget.inline.InlinePresentationSpec);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setMaxSuggestionCount(int);
- method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setServiceSupported(boolean);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setSupportedLocales(@NonNull android.os.LocaleList);
}
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 052d614..500a12c 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -1,4 +1,6 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
package android {
public static final class Manifest.permission {
@@ -6,9 +8,7 @@
field public static final String CONTROL_AUTOMOTIVE_GNSS = "android.permission.CONTROL_AUTOMOTIVE_GNSS";
field public static final String GET_INTENT_SENDER_INTENT = "android.permission.GET_INTENT_SENDER_INTENT";
field public static final String MAKE_UID_VISIBLE = "android.permission.MAKE_UID_VISIBLE";
- field public static final String MANAGE_REMOTE_AUTH = "android.permission.MANAGE_REMOTE_AUTH";
field public static final String USE_COMPANION_TRANSPORTS = "android.permission.USE_COMPANION_TRANSPORTS";
- field public static final String USE_REMOTE_AUTH = "android.permission.USE_REMOTE_AUTH";
}
}
diff --git a/core/api/module-lib-removed.txt b/core/api/module-lib-removed.txt
index d802177..14191eb 100644
--- a/core/api/module-lib-removed.txt
+++ b/core/api/module-lib-removed.txt
@@ -1 +1,3 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
diff --git a/core/api/removed.txt b/core/api/removed.txt
index 5a4be65..e2b4e4d 100644
--- a/core/api/removed.txt
+++ b/core/api/removed.txt
@@ -1,4 +1,6 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
package android.app {
public class Notification implements android.os.Parcelable {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index a99eeb0..7dcc7b2 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1,4 +1,6 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
package android {
public static final class Manifest.permission {
@@ -297,6 +299,7 @@
field public static final String RECEIVE_DATA_ACTIVITY_CHANGE = "android.permission.RECEIVE_DATA_ACTIVITY_CHANGE";
field public static final String RECEIVE_DEVICE_CUSTOMIZATION_READY = "android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY";
field public static final String RECEIVE_EMERGENCY_BROADCAST = "android.permission.RECEIVE_EMERGENCY_BROADCAST";
+ field @FlaggedApi("android.permission.flags.voice_activation_permission_apis") public static final String RECEIVE_SANDBOX_TRIGGER_AUDIO = "android.permission.RECEIVE_SANDBOX_TRIGGER_AUDIO";
field public static final String RECEIVE_WIFI_CREDENTIAL_CHANGE = "android.permission.RECEIVE_WIFI_CREDENTIAL_CHANGE";
field public static final String RECORD_BACKGROUND_AUDIO = "android.permission.RECORD_BACKGROUND_AUDIO";
field public static final String RECOVERY = "android.permission.RECOVERY";
@@ -652,7 +655,6 @@
field public static final String OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO = "android:receive_ambient_trigger_audio";
field public static final String OPSTR_RECEIVE_EMERGENCY_BROADCAST = "android:receive_emergency_broadcast";
field public static final String OPSTR_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO = "android:receive_explicit_user_interaction_audio";
- field public static final String OPSTR_RECEIVE_SANDBOX_TRIGGER_AUDIO = "android:receive_sandbox_trigger_audio";
field public static final String OPSTR_REQUEST_DELETE_PACKAGES = "android:request_delete_packages";
field public static final String OPSTR_REQUEST_INSTALL_PACKAGES = "android:request_install_packages";
field public static final String OPSTR_RUN_ANY_IN_BACKGROUND = "android:run_any_in_background";
@@ -3209,7 +3211,7 @@
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.input.VirtualTouchscreenConfig);
method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
method public int getDeviceId();
- method @Nullable public String getPersistentDeviceId();
+ method @FlaggedApi("android.companion.virtual.flags.vdm_public_apis") @Nullable public String getPersistentDeviceId();
method @NonNull public java.util.List<android.companion.virtual.sensor.VirtualSensor> getVirtualSensorList();
method public void launchPendingIntent(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void registerIntentInterceptor(@NonNull android.content.IntentFilter, @NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback);
@@ -4529,7 +4531,7 @@
method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration);
method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public void setBrightnessConfigurationForDisplay(@NonNull android.hardware.display.BrightnessConfiguration, @NonNull String);
method @Deprecated @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_SATURATION) public void setSaturationLevel(float);
- field public static final int VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT = 128; // 0x80
+ field @FlaggedApi("android.companion.virtual.flags.vdm_public_apis") public static final int VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT = 128; // 0x80
field public static final int VIRTUAL_DISPLAY_FLAG_STEAL_TOP_FOCUS_DISABLED = 65536; // 0x10000
field public static final int VIRTUAL_DISPLAY_FLAG_TRUSTED = 1024; // 0x400
}
@@ -9605,6 +9607,7 @@
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disable();
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disable(boolean);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enable();
+ method @FlaggedApi("android.nfc.enable_nfc_reader_option") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableReaderOption(boolean);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableSecureNfc(boolean);
method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public java.util.Map<java.lang.String,java.lang.Boolean> getTagIntentAppPreferenceForUser(int);
method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOn();
@@ -9704,7 +9707,6 @@
field @RequiresPermission(android.Manifest.permission.BATTERY_STATS) public static final int BATTERY_PROPERTY_CHARGING_POLICY = 9; // 0x9
field @RequiresPermission(android.Manifest.permission.BATTERY_STATS) public static final int BATTERY_PROPERTY_FIRST_USAGE_DATE = 8; // 0x8
field @RequiresPermission(android.Manifest.permission.BATTERY_STATS) public static final int BATTERY_PROPERTY_MANUFACTURING_DATE = 7; // 0x7
- field @RequiresPermission(android.Manifest.permission.BATTERY_STATS) public static final int BATTERY_PROPERTY_STATE_OF_HEALTH = 10; // 0xa
field public static final int CHARGING_POLICY_ADAPTIVE_AC = 3; // 0x3
field public static final int CHARGING_POLICY_ADAPTIVE_AON = 2; // 0x2
field public static final int CHARGING_POLICY_ADAPTIVE_LONGLIFE = 4; // 0x4
@@ -9772,8 +9774,8 @@
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportBleScanResults(@NonNull android.os.WorkSource, int);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportBleScanStarted(@NonNull android.os.WorkSource, boolean);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportBleScanStopped(@NonNull android.os.WorkSource, boolean);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void reportBluetoothOff(int, int, @NonNull String);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void reportBluetoothOn(int, int, @NonNull String);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void reportBluetoothOff(int, int, @NonNull String);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void reportBluetoothOn(int, int, @NonNull String);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportFullWifiLockAcquiredFromSource(@NonNull android.os.WorkSource);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportFullWifiLockReleasedFromSource(@NonNull android.os.WorkSource);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportMobileRadioPowerState(boolean, int);
@@ -13228,7 +13230,7 @@
method public void requestStreamingState(int);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telecom.StreamingCall> CREATOR;
- field public static final String EXTRA_CALL_ID = "android.telecom.extra.CALL_ID";
+ field @FlaggedApi("com.android.server.telecom.flags.call_details_id_changes") public static final String EXTRA_CALL_ID = "android.telecom.extra.CALL_ID";
field public static final int STATE_DISCONNECTED = 3; // 0x3
field public static final int STATE_HOLDING = 2; // 0x2
field public static final int STATE_STREAMING = 1; // 0x1
@@ -13709,7 +13711,7 @@
method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setCellIdentity(@Nullable android.telephony.CellIdentity);
method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setDomain(int);
method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setEmergencyOnly(boolean);
- method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setIsNonTerrestrialNetwork(boolean);
+ method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @NonNull public android.telephony.NetworkRegistrationInfo.Builder setIsNonTerrestrialNetwork(boolean);
method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setRegisteredPlmn(@Nullable String);
method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setRegistrationState(int);
method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setRejectCause(int);
@@ -16679,157 +16681,157 @@
package android.telephony.satellite {
- public final class AntennaDirection implements android.os.Parcelable {
- method public int describeContents();
- method public float getX();
- method public float getY();
- method public float getZ();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.AntennaDirection> CREATOR;
+ @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public final class AntennaDirection implements android.os.Parcelable {
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public int describeContents();
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public float getX();
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public float getY();
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public float getZ();
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.AntennaDirection> CREATOR;
}
- public final class AntennaPosition implements android.os.Parcelable {
- method public int describeContents();
- method @NonNull public android.telephony.satellite.AntennaDirection getAntennaDirection();
- method public int getSuggestedHoldPosition();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.AntennaPosition> CREATOR;
+ @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public final class AntennaPosition implements android.os.Parcelable {
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public int describeContents();
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public android.telephony.satellite.AntennaDirection getAntennaDirection();
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public int getSuggestedHoldPosition();
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.AntennaPosition> CREATOR;
}
- public final class PointingInfo implements android.os.Parcelable {
- method public int describeContents();
- method public float getSatelliteAzimuthDegrees();
- method public float getSatelliteElevationDegrees();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.PointingInfo> CREATOR;
+ @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public final class PointingInfo implements android.os.Parcelable {
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public int describeContents();
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public float getSatelliteAzimuthDegrees();
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public float getSatelliteElevationDegrees();
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.PointingInfo> CREATOR;
}
- public final class SatelliteCapabilities implements android.os.Parcelable {
- method public int describeContents();
- method @NonNull public java.util.Map<java.lang.Integer,android.telephony.satellite.AntennaPosition> getAntennaPositionMap();
- method public int getMaxBytesPerOutgoingDatagram();
- method @NonNull public java.util.Set<java.lang.Integer> getSupportedRadioTechnologies();
- method public boolean isPointingRequired();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.SatelliteCapabilities> CREATOR;
+ @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public final class SatelliteCapabilities implements android.os.Parcelable {
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public int describeContents();
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public java.util.Map<java.lang.Integer,android.telephony.satellite.AntennaPosition> getAntennaPositionMap();
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public int getMaxBytesPerOutgoingDatagram();
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public java.util.Set<java.lang.Integer> getSupportedRadioTechnologies();
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public boolean isPointingRequired();
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.SatelliteCapabilities> CREATOR;
}
- public final class SatelliteDatagram implements android.os.Parcelable {
- method public int describeContents();
- method @NonNull public byte[] getSatelliteDatagram();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.SatelliteDatagram> CREATOR;
+ @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public final class SatelliteDatagram implements android.os.Parcelable {
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public int describeContents();
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public byte[] getSatelliteDatagram();
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.SatelliteDatagram> CREATOR;
}
- public interface SatelliteDatagramCallback {
- method public void onSatelliteDatagramReceived(long, @NonNull android.telephony.satellite.SatelliteDatagram, int, @NonNull java.util.function.Consumer<java.lang.Void>);
+ @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface SatelliteDatagramCallback {
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteDatagramReceived(long, @NonNull android.telephony.satellite.SatelliteDatagram, int, @NonNull java.util.function.Consumer<java.lang.Void>);
}
- public final class SatelliteManager {
+ @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public final class SatelliteManager {
method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void addSatelliteAttachRestrictionForCarrier(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
- method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void deprovisionSatelliteService(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void deprovisionSatelliteService(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public java.util.Set<java.lang.Integer> getSatelliteAttachRestrictionReasonsForCarrier(int);
- method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void pollPendingSatelliteDatagrams(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
- method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void provisionSatelliteService(@NonNull String, @NonNull byte[], @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
- method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteDatagram(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteDatagramCallback);
- method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteModemStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteStateCallback);
- method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteProvisionStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteProvisionStateCallback);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void pollPendingSatelliteDatagrams(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void provisionSatelliteService(@NonNull String, @NonNull byte[], @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteDatagram(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteDatagramCallback);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteModemStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteStateCallback);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteProvisionStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteProvisionStateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void removeSatelliteAttachRestrictionForCarrier(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
- method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsDemoModeEnabled(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsDemoModeEnabled(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteAttachEnabledForCarrier(int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
- method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteCommunicationAllowedForCurrentLocation(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
- method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteEnabled(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
- method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteProvisioned(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
- method public void requestIsSatelliteSupported(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteCommunicationAllowedForCurrentLocation(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteEnabled(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteProvisioned(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void requestIsSatelliteSupported(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteAttachEnabledForCarrier(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
- method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteCapabilities(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.satellite.SatelliteCapabilities,android.telephony.satellite.SatelliteManager.SatelliteException>);
- method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteEnabled(boolean, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
- method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestTimeForNextSatelliteVisibility(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.time.Duration,android.telephony.satellite.SatelliteManager.SatelliteException>);
- method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void sendSatelliteDatagram(int, @NonNull android.telephony.satellite.SatelliteDatagram, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
- method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void setDeviceAlignedWithSatellite(boolean);
- method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void startSatelliteTransmissionUpdates(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull android.telephony.satellite.SatelliteTransmissionUpdateCallback);
- method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void stopSatelliteTransmissionUpdates(@NonNull android.telephony.satellite.SatelliteTransmissionUpdateCallback, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
- method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteDatagram(@NonNull android.telephony.satellite.SatelliteDatagramCallback);
- method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteModemStateChanged(@NonNull android.telephony.satellite.SatelliteStateCallback);
- method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteProvisionStateChanged(@NonNull android.telephony.satellite.SatelliteProvisionStateCallback);
- field public static final int DATAGRAM_TYPE_LOCATION_SHARING = 2; // 0x2
- field public static final int DATAGRAM_TYPE_SOS_MESSAGE = 1; // 0x1
- field public static final int DATAGRAM_TYPE_UNKNOWN = 0; // 0x0
- field public static final int DEVICE_HOLD_POSITION_LANDSCAPE_LEFT = 2; // 0x2
- field public static final int DEVICE_HOLD_POSITION_LANDSCAPE_RIGHT = 3; // 0x3
- field public static final int DEVICE_HOLD_POSITION_PORTRAIT = 1; // 0x1
- field public static final int DEVICE_HOLD_POSITION_UNKNOWN = 0; // 0x0
- field public static final int DISPLAY_MODE_CLOSED = 3; // 0x3
- field public static final int DISPLAY_MODE_FIXED = 1; // 0x1
- field public static final int DISPLAY_MODE_OPENED = 2; // 0x2
- field public static final int DISPLAY_MODE_UNKNOWN = 0; // 0x0
- field public static final int NT_RADIO_TECHNOLOGY_EMTC_NTN = 3; // 0x3
- field public static final int NT_RADIO_TECHNOLOGY_NB_IOT_NTN = 1; // 0x1
- field public static final int NT_RADIO_TECHNOLOGY_NR_NTN = 2; // 0x2
- field public static final int NT_RADIO_TECHNOLOGY_PROPRIETARY = 4; // 0x4
- field public static final int NT_RADIO_TECHNOLOGY_UNKNOWN = 0; // 0x0
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteCapabilities(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.satellite.SatelliteCapabilities,android.telephony.satellite.SatelliteManager.SatelliteException>);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteEnabled(boolean, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestTimeForNextSatelliteVisibility(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.time.Duration,android.telephony.satellite.SatelliteManager.SatelliteException>);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void sendSatelliteDatagram(int, @NonNull android.telephony.satellite.SatelliteDatagram, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void setDeviceAlignedWithSatellite(boolean);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void startSatelliteTransmissionUpdates(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull android.telephony.satellite.SatelliteTransmissionUpdateCallback);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void stopSatelliteTransmissionUpdates(@NonNull android.telephony.satellite.SatelliteTransmissionUpdateCallback, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteDatagram(@NonNull android.telephony.satellite.SatelliteDatagramCallback);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteModemStateChanged(@NonNull android.telephony.satellite.SatelliteStateCallback);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSatelliteProvisionStateChanged(@NonNull android.telephony.satellite.SatelliteProvisionStateCallback);
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int DATAGRAM_TYPE_LOCATION_SHARING = 2; // 0x2
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int DATAGRAM_TYPE_SOS_MESSAGE = 1; // 0x1
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int DATAGRAM_TYPE_UNKNOWN = 0; // 0x0
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int DEVICE_HOLD_POSITION_LANDSCAPE_LEFT = 2; // 0x2
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int DEVICE_HOLD_POSITION_LANDSCAPE_RIGHT = 3; // 0x3
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int DEVICE_HOLD_POSITION_PORTRAIT = 1; // 0x1
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int DEVICE_HOLD_POSITION_UNKNOWN = 0; // 0x0
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int DISPLAY_MODE_CLOSED = 3; // 0x3
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int DISPLAY_MODE_FIXED = 1; // 0x1
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int DISPLAY_MODE_OPENED = 2; // 0x2
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int DISPLAY_MODE_UNKNOWN = 0; // 0x0
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int NT_RADIO_TECHNOLOGY_EMTC_NTN = 3; // 0x3
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int NT_RADIO_TECHNOLOGY_NB_IOT_NTN = 1; // 0x1
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int NT_RADIO_TECHNOLOGY_NR_NTN = 2; // 0x2
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int NT_RADIO_TECHNOLOGY_PROPRIETARY = 4; // 0x4
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int NT_RADIO_TECHNOLOGY_UNKNOWN = 0; // 0x0
field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final int SATELLITE_COMMUNICATION_RESTRICTION_REASON_GEOLOCATION = 1; // 0x1
- field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE = 0; // 0x0
- field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED = 7; // 0x7
- field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_NONE = 6; // 0x6
- field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS = 5; // 0x5
- field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING = 4; // 0x4
- field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING = 1; // 0x1
- field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED = 3; // 0x3
- field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS = 2; // 0x2
- field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_UNKNOWN = -1; // 0xffffffff
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE = 0; // 0x0
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED = 7; // 0x7
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_NONE = 6; // 0x6
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS = 5; // 0x5
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING = 4; // 0x4
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING = 1; // 0x1
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED = 3; // 0x3
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS = 2; // 0x2
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_UNKNOWN = -1; // 0xffffffff
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT = 8; // 0x8
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_MODEM_STATE_CONNECTED = 7; // 0x7
- field public static final int SATELLITE_MODEM_STATE_DATAGRAM_RETRYING = 3; // 0x3
- field public static final int SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING = 2; // 0x2
- field public static final int SATELLITE_MODEM_STATE_IDLE = 0; // 0x0
- field public static final int SATELLITE_MODEM_STATE_LISTENING = 1; // 0x1
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_MODEM_STATE_DATAGRAM_RETRYING = 3; // 0x3
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING = 2; // 0x2
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_MODEM_STATE_IDLE = 0; // 0x0
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_MODEM_STATE_LISTENING = 1; // 0x1
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_MODEM_STATE_NOT_CONNECTED = 6; // 0x6
- field public static final int SATELLITE_MODEM_STATE_OFF = 4; // 0x4
- field public static final int SATELLITE_MODEM_STATE_UNAVAILABLE = 5; // 0x5
- field public static final int SATELLITE_MODEM_STATE_UNKNOWN = -1; // 0xffffffff
- field public static final int SATELLITE_RESULT_ACCESS_BARRED = 16; // 0x10
- field public static final int SATELLITE_RESULT_ERROR = 1; // 0x1
- field public static final int SATELLITE_RESULT_INVALID_ARGUMENTS = 8; // 0x8
- field public static final int SATELLITE_RESULT_INVALID_MODEM_STATE = 7; // 0x7
- field public static final int SATELLITE_RESULT_INVALID_TELEPHONY_STATE = 6; // 0x6
- field public static final int SATELLITE_RESULT_MODEM_BUSY = 22; // 0x16
- field public static final int SATELLITE_RESULT_MODEM_ERROR = 4; // 0x4
- field public static final int SATELLITE_RESULT_NETWORK_ERROR = 5; // 0x5
- field public static final int SATELLITE_RESULT_NETWORK_TIMEOUT = 17; // 0x11
- field public static final int SATELLITE_RESULT_NOT_AUTHORIZED = 19; // 0x13
- field public static final int SATELLITE_RESULT_NOT_REACHABLE = 18; // 0x12
- field public static final int SATELLITE_RESULT_NOT_SUPPORTED = 20; // 0x14
- field public static final int SATELLITE_RESULT_NO_RESOURCES = 12; // 0xc
- field public static final int SATELLITE_RESULT_RADIO_NOT_AVAILABLE = 10; // 0xa
- field public static final int SATELLITE_RESULT_REQUEST_ABORTED = 15; // 0xf
- field public static final int SATELLITE_RESULT_REQUEST_FAILED = 9; // 0x9
- field public static final int SATELLITE_RESULT_REQUEST_IN_PROGRESS = 21; // 0x15
- field public static final int SATELLITE_RESULT_REQUEST_NOT_SUPPORTED = 11; // 0xb
- field public static final int SATELLITE_RESULT_SERVER_ERROR = 2; // 0x2
- field public static final int SATELLITE_RESULT_SERVICE_ERROR = 3; // 0x3
- field public static final int SATELLITE_RESULT_SERVICE_NOT_PROVISIONED = 13; // 0xd
- field public static final int SATELLITE_RESULT_SERVICE_PROVISION_IN_PROGRESS = 14; // 0xe
- field public static final int SATELLITE_RESULT_SUCCESS = 0; // 0x0
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_MODEM_STATE_OFF = 4; // 0x4
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_MODEM_STATE_UNAVAILABLE = 5; // 0x5
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_MODEM_STATE_UNKNOWN = -1; // 0xffffffff
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_ACCESS_BARRED = 16; // 0x10
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_ERROR = 1; // 0x1
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_INVALID_ARGUMENTS = 8; // 0x8
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_INVALID_MODEM_STATE = 7; // 0x7
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_INVALID_TELEPHONY_STATE = 6; // 0x6
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_MODEM_BUSY = 22; // 0x16
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_MODEM_ERROR = 4; // 0x4
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_NETWORK_ERROR = 5; // 0x5
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_NETWORK_TIMEOUT = 17; // 0x11
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_NOT_AUTHORIZED = 19; // 0x13
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_NOT_REACHABLE = 18; // 0x12
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_NOT_SUPPORTED = 20; // 0x14
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_NO_RESOURCES = 12; // 0xc
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_RADIO_NOT_AVAILABLE = 10; // 0xa
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_REQUEST_ABORTED = 15; // 0xf
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_REQUEST_FAILED = 9; // 0x9
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_REQUEST_IN_PROGRESS = 21; // 0x15
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_REQUEST_NOT_SUPPORTED = 11; // 0xb
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_SERVER_ERROR = 2; // 0x2
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_SERVICE_ERROR = 3; // 0x3
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_SERVICE_NOT_PROVISIONED = 13; // 0xd
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_SERVICE_PROVISION_IN_PROGRESS = 14; // 0xe
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_SUCCESS = 0; // 0x0
}
- public static class SatelliteManager.SatelliteException extends java.lang.Exception {
- ctor public SatelliteManager.SatelliteException(int);
- method public int getErrorCode();
+ @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static class SatelliteManager.SatelliteException extends java.lang.Exception {
+ ctor @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public SatelliteManager.SatelliteException(int);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public int getErrorCode();
}
- public interface SatelliteProvisionStateCallback {
- method public void onSatelliteProvisionStateChanged(boolean);
+ @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface SatelliteProvisionStateCallback {
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteProvisionStateChanged(boolean);
}
- public interface SatelliteStateCallback {
- method public void onSatelliteModemStateChanged(int);
+ @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface SatelliteStateCallback {
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteModemStateChanged(int);
}
- public interface SatelliteTransmissionUpdateCallback {
- method public void onReceiveDatagramStateChanged(int, int, int);
- method public void onSatellitePositionChanged(@NonNull android.telephony.satellite.PointingInfo);
- method public void onSendDatagramStateChanged(int, int, int);
+ @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface SatelliteTransmissionUpdateCallback {
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onReceiveDatagramStateChanged(int, int, int);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatellitePositionChanged(@NonNull android.telephony.satellite.PointingInfo);
+ method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSendDatagramStateChanged(int, int, int);
}
}
diff --git a/core/api/system-removed.txt b/core/api/system-removed.txt
index aa17df3..1fa2718 100644
--- a/core/api/system-removed.txt
+++ b/core/api/system-removed.txt
@@ -1,4 +1,6 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
package android.app {
public class AppOpsManager {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index b87a640..f6160db 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1,4 +1,6 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
package android {
public static final class Manifest.permission {
@@ -28,7 +30,6 @@
field public static final String MANAGE_APP_OPS_MODES = "android.permission.MANAGE_APP_OPS_MODES";
field public static final String MANAGE_CRATES = "android.permission.MANAGE_CRATES";
field public static final String MANAGE_NOTIFICATION_LISTENERS = "android.permission.MANAGE_NOTIFICATION_LISTENERS";
- field public static final String MANAGE_REMOTE_AUTH = "android.permission.MANAGE_REMOTE_AUTH";
field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
field public static final String MANAGE_TOAST_RATE_LIMITING = "android.permission.MANAGE_TOAST_RATE_LIMITING";
field public static final String MODIFY_HDR_CONVERSION_MODE = "android.permission.MODIFY_HDR_CONVERSION_MODE";
@@ -56,7 +57,6 @@
field public static final String TEST_INPUT_METHOD = "android.permission.TEST_INPUT_METHOD";
field public static final String TEST_MANAGE_ROLLBACKS = "android.permission.TEST_MANAGE_ROLLBACKS";
field public static final String UPGRADE_RUNTIME_PERMISSIONS = "android.permission.UPGRADE_RUNTIME_PERMISSIONS";
- field public static final String USE_REMOTE_AUTH = "android.permission.USE_REMOTE_AUTH";
field public static final String WRITE_ALLOWLISTED_DEVICE_CONFIG = "android.permission.WRITE_ALLOWLISTED_DEVICE_CONFIG";
field public static final String WRITE_DEVICE_CONFIG = "android.permission.WRITE_DEVICE_CONFIG";
field @Deprecated public static final String WRITE_MEDIA_STORAGE = "android.permission.WRITE_MEDIA_STORAGE";
@@ -840,7 +840,7 @@
package android.companion {
- public static final class AssociationInfo.Builder {
+ @FlaggedApi("android.companion.new_association_builder") public static final class AssociationInfo.Builder {
ctor public AssociationInfo.Builder(int, int, @NonNull String);
ctor public AssociationInfo.Builder(@NonNull android.companion.AssociationInfo);
method @NonNull public android.companion.AssociationInfo build();
@@ -2957,10 +2957,6 @@
method @Deprecated public boolean isBound();
}
- public class NotificationRankingUpdate implements android.os.Parcelable {
- method public final boolean isFdNotNullAndClosed();
- }
-
}
package android.service.quickaccesswallet {
@@ -3190,7 +3186,7 @@
field public static final int HAL_SERVICE_MESSAGING = 2; // 0x2
field public static final int HAL_SERVICE_MODEM = 3; // 0x3
field public static final int HAL_SERVICE_NETWORK = 4; // 0x4
- field public static final int HAL_SERVICE_SATELLITE = 8; // 0x8
+ field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int HAL_SERVICE_SATELLITE = 8; // 0x8
field public static final int HAL_SERVICE_SIM = 5; // 0x5
field public static final int HAL_SERVICE_VOICE = 6; // 0x6
field public static final android.util.Pair HAL_VERSION_UNKNOWN;
@@ -3410,7 +3406,7 @@
public final class Choreographer {
method public static long getFrameDelay();
- method public long getFrameTimeNanos();
+ method @FlaggedApi("android.view.flags.expected_presentation_time_api") public long getFrameTimeNanos();
method public void postCallback(int, Runnable, Object);
method public void postCallbackDelayed(int, Runnable, Object, long);
method public void removeCallbacks(int, Runnable, Object);
@@ -3571,8 +3567,8 @@
method public default void holdLock(android.os.IBinder, int);
method public default boolean isGlobalKey(int);
method public default boolean isTaskSnapshotSupported();
- method @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public default boolean replaceContentOnDisplayWithMirror(int, @NonNull android.view.Window);
- method @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public default boolean replaceContentOnDisplayWithSc(int, @NonNull android.view.SurfaceControl);
+ method @FlaggedApi("REPLACE_CONTENT_WITH_MIRROR") @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public default boolean replaceContentOnDisplayWithMirror(int, @NonNull android.view.Window);
+ method @FlaggedApi("REPLACE_CONTENT_WITH_MIRROR") @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public default boolean replaceContentOnDisplayWithSc(int, @NonNull android.view.SurfaceControl);
method public default void setDisplayImePolicy(int, int);
method public default void setShouldShowSystemDecors(int, boolean);
method public default void setShouldShowWithInsecureKeyguard(int, boolean);
@@ -3629,7 +3625,7 @@
package android.view.animation {
public class AnimationUtils {
- method public static void lockAnimationClock(long, long);
+ method @FlaggedApi("android.view.flags.expected_presentation_time_api") public static void lockAnimationClock(long, long);
method public static void unlockAnimationClock();
}
diff --git a/core/api/test-removed.txt b/core/api/test-removed.txt
index d802177..14191eb 100644
--- a/core/api/test-removed.txt
+++ b/core/api/test-removed.txt
@@ -1 +1,3 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
diff --git a/core/java/Android.bp b/core/java/Android.bp
index c3f3d87..0293f66 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -23,11 +23,6 @@
visibility: ["//frameworks/base"],
}
-filegroup {
- name: "IKeyAttestationApplicationIdProvider.aidl",
- srcs: ["android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl"],
-}
-
aidl_library {
name: "IDropBoxManagerService_aidl",
srcs: [
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 6858945..17637df 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2258,7 +2258,6 @@
*
* @hide
*/
- @SystemApi
public static final String OPSTR_RECEIVE_SANDBOX_TRIGGER_AUDIO =
"android:receive_sandbox_trigger_audio";
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 7ee1332..15d692a 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -272,6 +272,10 @@
private boolean mDemoted = false;
private boolean mImportantConvo = false;
private long mDeletedTime = DEFAULT_DELETION_TIME_MS;
+ /** Do not (de)serialize this value: it only affects logic in system_server and that logic
+ * is reset on each boot {@link NotificationAttentionHelper#buzzBeepBlinkLocked}.
+ */
+ private long mLastNotificationUpdateTimeMs = 0;
/**
* Creates a notification channel.
@@ -932,6 +936,23 @@
}
/**
+ * Returns the time of the notification post or last update for this channel.
+ * @return time of post / last update
+ * @hide
+ */
+ public long getLastNotificationUpdateTimeMs() {
+ return mLastNotificationUpdateTimeMs;
+ }
+
+ /**
+ * Sets the time of the notification post or last update for this channel.
+ * @hide
+ */
+ public void setLastNotificationUpdateTimeMs(long updateTimeMs) {
+ mLastNotificationUpdateTimeMs = updateTimeMs;
+ }
+
+ /**
* @hide
*/
public void populateFromXmlForRestore(XmlPullParser parser, boolean pkgInstalled,
@@ -1408,7 +1429,8 @@
+ ", mParent=" + mParentId
+ ", mConversationId=" + mConversationId
+ ", mDemoted=" + mDemoted
- + ", mImportantConvo=" + mImportantConvo;
+ + ", mImportantConvo=" + mImportantConvo
+ + ", mLastNotificationUpdateTimeMs=" + mLastNotificationUpdateTimeMs;
}
/** @hide */
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 79b68c1..b8bea9d 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -25,8 +25,12 @@
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
+import android.annotation.UserHandleAware;
import android.annotation.WorkerThread;
import android.app.Notification.Builder;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
@@ -1659,23 +1663,42 @@
}
/**
+ * For apps targeting {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} and above, the
+ * {@code setNotificationListenerAccessGranted} method will use the user contained within the
+ * context.
+ * For apps targeting an SDK version <em>below</em> this, the user of the calling process will
+ * be used (Process.myUserHandle()).
+ *
+ * @hide
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ public static final long SET_LISTENER_ACCESS_GRANTED_IS_USER_AWARE = 302563478L;
+
+ /**
* Grants/revokes Notification Listener access to the given component for current user.
* To grant access for a particular user, obtain this service by using the {@link Context}
* provided by {@link Context#createPackageContextAsUser}
*
* @param listener Name of component to grant/revoke access
- * @param granted Grant/revoke access
- * @param userSet Whether the action was triggered explicitly by user
+ * @param granted Grant/revoke access
+ * @param userSet Whether the action was triggered explicitly by user
* @hide
*/
@SystemApi
@TestApi
+ @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
@RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS)
public void setNotificationListenerAccessGranted(
@NonNull ComponentName listener, boolean granted, boolean userSet) {
INotificationManager service = getService();
try {
- service.setNotificationListenerAccessGranted(listener, granted, userSet);
+ if (CompatChanges.isChangeEnabled(SET_LISTENER_ACCESS_GRANTED_IS_USER_AWARE)) {
+ service.setNotificationListenerAccessGrantedForUser(listener, mContext.getUserId(),
+ granted, userSet);
+ } else {
+ service.setNotificationListenerAccessGranted(listener, granted, userSet);
+ }
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/admin/DevicePolicyResources.java b/core/java/android/app/admin/DevicePolicyResources.java
index 456c6af..7c69d37 100644
--- a/core/java/android/app/admin/DevicePolicyResources.java
+++ b/core/java/android/app/admin/DevicePolicyResources.java
@@ -1172,6 +1172,12 @@
public static final String WORK_CATEGORY_HEADER = PREFIX + "WORK_CATEGORY_HEADER";
/**
+ * Header for items under the private user
+ */
+ public static final String PRIVATE_CATEGORY_HEADER =
+ PREFIX + "PRIVATE_CATEGORY_HEADER";
+
+ /**
* Header for items under the personal user
*/
public static final String PERSONAL_CATEGORY_HEADER =
@@ -1208,6 +1214,12 @@
public static final String AUTO_SYNC_WORK_DATA = PREFIX + "AUTO_SYNC_WORK_DATA";
/**
+ * Text for toggle to enable auto-sycing private data
+ */
+ public static final String AUTO_SYNC_PRIVATE_DATA = PREFIX
+ + "AUTO_SYNC_PRIVATE_DATA";
+
+ /**
* Summary for "More security settings" section when a work profile is on the device.
*/
public static final String MORE_SECURITY_SETTINGS_WORK_PROFILE_SUMMARY = PREFIX
diff --git a/core/java/android/companion/AssociationInfo.java b/core/java/android/companion/AssociationInfo.java
index 083fa00..6393c45 100644
--- a/core/java/android/companion/AssociationInfo.java
+++ b/core/java/android/companion/AssociationInfo.java
@@ -15,6 +15,7 @@
*/
package android.companion;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
@@ -412,6 +413,7 @@
*
* @hide
*/
+ @FlaggedApi(Flags.FLAG_NEW_ASSOCIATION_BUILDER)
@TestApi
public static final class Builder {
private final int mId;
diff --git a/core/java/android/companion/flags.aconfig b/core/java/android/companion/flags.aconfig
new file mode 100644
index 0000000..b9e5609
--- /dev/null
+++ b/core/java/android/companion/flags.aconfig
@@ -0,0 +1,8 @@
+package: "android.companion"
+
+flag {
+ name: "new_association_builder"
+ namespace: "companion"
+ description: "Controls if the new Builder is exposed to test apis."
+ bug: "296251481"
+}
\ No newline at end of file
diff --git a/core/java/android/companion/virtual/VirtualDevice.java b/core/java/android/companion/virtual/VirtualDevice.java
index ce883cd..0af4c92 100644
--- a/core/java/android/companion/virtual/VirtualDevice.java
+++ b/core/java/android/companion/virtual/VirtualDevice.java
@@ -113,6 +113,7 @@
* <p class="note">This identifier may not be unique across virtual devices, in case there are
* more than one virtual devices corresponding to the same physical device.
*/
+ @FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS)
public @Nullable String getPersistentDeviceId() {
return mPersistentId;
}
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 39800f7..2569366 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -476,6 +476,7 @@
/**
* Returns the persistent ID of this virtual device.
*/
+ @FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS)
public @Nullable String getPersistentDeviceId() {
return mVirtualDeviceInternal.getPersistentDeviceId();
}
diff --git a/core/java/android/content/ContentCaptureOptions.java b/core/java/android/content/ContentCaptureOptions.java
index 36e0529..3fbcd70 100644
--- a/core/java/android/content/ContentCaptureOptions.java
+++ b/core/java/android/content/ContentCaptureOptions.java
@@ -30,6 +30,11 @@
import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
/**
* Content capture options for a given package.
@@ -119,7 +124,10 @@
/* enableReceiver= */ false,
new ContentProtectionOptions(
/* enableReceiver= */ false,
- /* bufferSize= */ 0),
+ /* bufferSize= */ 0,
+ /* requiredGroups= */ Collections.emptyList(),
+ /* optionalGroups= */ Collections.emptyList(),
+ /* optionalGroupsThreshold= */ 0),
/* whitelistedComponents= */ null);
}
@@ -141,9 +149,7 @@
logHistorySize,
ContentCaptureManager.DEFAULT_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING,
ContentCaptureManager.DEFAULT_ENABLE_CONTENT_CAPTURE_RECEIVER,
- new ContentProtectionOptions(
- ContentCaptureManager.DEFAULT_ENABLE_CONTENT_PROTECTION_RECEIVER,
- ContentCaptureManager.DEFAULT_CONTENT_PROTECTION_BUFFER_SIZE),
+ new ContentProtectionOptions(),
whitelistedComponents);
}
@@ -183,9 +189,7 @@
ContentCaptureManager.DEFAULT_LOG_HISTORY_SIZE,
ContentCaptureManager.DEFAULT_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING,
ContentCaptureManager.DEFAULT_ENABLE_CONTENT_CAPTURE_RECEIVER,
- new ContentProtectionOptions(
- ContentCaptureManager.DEFAULT_ENABLE_CONTENT_PROTECTION_RECEIVER,
- ContentCaptureManager.DEFAULT_CONTENT_PROTECTION_BUFFER_SIZE),
+ new ContentProtectionOptions(),
whitelistedComponents);
}
@@ -386,9 +390,58 @@
*/
public final int bufferSize;
- public ContentProtectionOptions(boolean enableReceiver, int bufferSize) {
+ /**
+ * The list of required groups of strings to match.
+ *
+ * @hide
+ */
+ @NonNull public final List<List<String>> requiredGroups;
+
+ /**
+ * The list of optional groups of strings to match.
+ *
+ * @hide
+ */
+ @NonNull public final List<List<String>> optionalGroups;
+
+ /**
+ * The minimal number of optional groups that have to be matched. This is the threshold
+ * value and comparison is done with greater than or equals.
+ *
+ * @hide
+ */
+ public final int optionalGroupsThreshold;
+
+ /**
+ * Empty constructor with default values.
+ *
+ * @hide
+ */
+ public ContentProtectionOptions() {
+ this(
+ ContentCaptureManager.DEFAULT_ENABLE_CONTENT_PROTECTION_RECEIVER,
+ ContentCaptureManager.DEFAULT_CONTENT_PROTECTION_BUFFER_SIZE,
+ ContentCaptureManager.DEFAULT_CONTENT_PROTECTION_REQUIRED_GROUPS,
+ ContentCaptureManager.DEFAULT_CONTENT_PROTECTION_OPTIONAL_GROUPS,
+ ContentCaptureManager.DEFAULT_CONTENT_PROTECTION_OPTIONAL_GROUPS_THRESHOLD);
+ }
+
+ /**
+ * Full primary constructor.
+ *
+ * @hide
+ */
+ public ContentProtectionOptions(
+ boolean enableReceiver,
+ int bufferSize,
+ @NonNull List<List<String>> requiredGroups,
+ @NonNull List<List<String>> optionalGroups,
+ int optionalGroupsThreshold) {
this.enableReceiver = enableReceiver;
this.bufferSize = bufferSize;
+ this.requiredGroups = requiredGroups;
+ this.optionalGroups = optionalGroups;
+ this.optionalGroupsThreshold = optionalGroupsThreshold;
}
@Override
@@ -398,7 +451,14 @@
.append("enableReceiver=")
.append(enableReceiver)
.append(", bufferSize=")
- .append(bufferSize);
+ .append(bufferSize)
+ .append(", requiredGroupsSize=")
+ .append(requiredGroups.size())
+ .append(", optionalGroupsSize=")
+ .append(optionalGroups.size())
+ .append(", optionalGroupsThreshold=")
+ .append(optionalGroupsThreshold);
+
return stringBuilder.append(']').toString();
}
@@ -407,17 +467,50 @@
pw.print(enableReceiver);
pw.print(", bufferSize=");
pw.print(bufferSize);
+ pw.print(", requiredGroupsSize=");
+ pw.print(requiredGroups.size());
+ pw.print(", optionalGroupsSize=");
+ pw.print(optionalGroups.size());
+ pw.print(", optionalGroupsThreshold=");
+ pw.print(optionalGroupsThreshold);
}
- private void writeToParcel(Parcel parcel) {
+ private void writeToParcel(@NonNull Parcel parcel) {
parcel.writeBoolean(enableReceiver);
parcel.writeInt(bufferSize);
+ writeGroupsToParcel(requiredGroups, parcel);
+ writeGroupsToParcel(optionalGroups, parcel);
+ parcel.writeInt(optionalGroupsThreshold);
}
- private static ContentProtectionOptions createFromParcel(Parcel parcel) {
+ @NonNull
+ private static ContentProtectionOptions createFromParcel(@NonNull Parcel parcel) {
boolean enableReceiver = parcel.readBoolean();
int bufferSize = parcel.readInt();
- return new ContentProtectionOptions(enableReceiver, bufferSize);
+ List<List<String>> requiredGroups = createGroupsFromParcel(parcel);
+ List<List<String>> optionalGroups = createGroupsFromParcel(parcel);
+ int optionalGroupsThreshold = parcel.readInt();
+ return new ContentProtectionOptions(
+ enableReceiver,
+ bufferSize,
+ requiredGroups,
+ optionalGroups,
+ optionalGroupsThreshold);
+ }
+
+ private static void writeGroupsToParcel(
+ @NonNull List<List<String>> groups, @NonNull Parcel parcel) {
+ parcel.writeInt(groups.size());
+ groups.forEach(parcel::writeStringList);
+ }
+
+ @NonNull
+ private static List<List<String>> createGroupsFromParcel(@NonNull Parcel parcel) {
+ int size = parcel.readInt();
+ return IntStream.range(0, size)
+ .mapToObj(i -> new ArrayList<String>())
+ .peek(parcel::readStringList)
+ .collect(Collectors.toUnmodifiableList());
}
}
}
diff --git a/core/java/android/content/om/OverlayIdentifier.java b/core/java/android/content/om/OverlayIdentifier.java
index f256372..30875aa 100644
--- a/core/java/android/content/om/OverlayIdentifier.java
+++ b/core/java/android/content/om/OverlayIdentifier.java
@@ -39,7 +39,6 @@
* -->
*
* @see OverlayInfo#getOverlayIdentifier()
- * @see OverlayManagerTransaction.Builder#unregisterFabricatedOverlay(OverlayIdentifier)
*/
@DataClass(genConstructor = false, genBuilder = false, genHiddenBuilder = false,
genEqualsHashCode = true, genToString = false)
diff --git a/core/java/android/content/om/OverlayInfo.java b/core/java/android/content/om/OverlayInfo.java
index ff1c088..2e89856 100644
--- a/core/java/android/content/om/OverlayInfo.java
+++ b/core/java/android/content/om/OverlayInfo.java
@@ -385,7 +385,6 @@
* <p>The return value of this function can be used to unregister the related overlay.
*
* @return an identifier representing the current overlay.
- * @see OverlayManagerTransaction.Builder#unregisterFabricatedOverlay(OverlayIdentifier)
*/
@Override
@NonNull
diff --git a/core/java/android/content/om/OverlayManager.java b/core/java/android/content/om/OverlayManager.java
index 0fcc72a1..ed965b3 100644
--- a/core/java/android/content/om/OverlayManager.java
+++ b/core/java/android/content/om/OverlayManager.java
@@ -50,7 +50,7 @@
* <li>register overlays
* <li>unregister overlays
* <li>execute multiple operations in one commitment by calling {@link
- * OverlayManagerTransaction#commit()}
+ * #commit(OverlayManagerTransaction)}
* </ul>
*
* @see OverlayManagerTransaction
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 036a4eb..aefa55f 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1185,8 +1185,8 @@
* <p>For {@link android.view.View#getWindowVisibleDisplayFrame} and
* {@link android.view.View}#getWindowDisplayFrame this sandboxing is happening indirectly
* through
- * {@link android.view.ViewRootImpl}#getWindowVisibleDisplayFrame,
- * {@link android.view.ViewRootImpl}#getDisplayFrame respectively.
+ * {@code android.view.ViewRootImpl#getWindowVisibleDisplayFrame},
+ * {@code android.view.ViewRootImpl#getDisplayFrame} respectively.
*
* <p>Some applications assume that they occupy the whole screen and therefore use the display
* coordinates in their calculations as if an activity is positioned in the top-left corner of
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 673a8a5..d837aae 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -371,6 +371,13 @@
"android.content.pm.extra.UNARCHIVE_ALL_USERS";
/**
+ * A list of warnings that occurred during installation.
+ *
+ * @hide
+ */
+ public static final String EXTRA_WARNINGS = "android.content.pm.extra.WARNINGS";
+
+ /**
* Streaming installation pending.
* Caller should make sure DataLoader is able to prepare image and reinitiate the operation.
*
@@ -2723,8 +2730,8 @@
* Sets the state of permissions for the package at installation.
* <p/>
* Granting any runtime permissions require the
- * {@link android.Manifest.permission#INSTALL_GRANT_RUNTIME_PERMISSIONS
- * INSTALL_GRANT_RUNTIME_PERMISSIONS} permission to be held by the caller. Revoking runtime
+ * {@code android.Manifest.permission#INSTALL_GRANT_RUNTIME_PERMISSIONS}
+ * permission to be held by the caller. Revoking runtime
* permissions is not allowed, even during app update sessions.
* <p/>
* Holders without the permission are allowed to change the following special permissions:
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index 65f56f6..9869179 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -132,11 +132,6 @@
* the {@link android.R.attr#foregroundServiceType} attribute.
* Data(photo, file, account) upload/download, backup/restore, import/export, fetch,
* transfer over network between device and cloud.
- *
- * <p class="note">
- * Use the {@link android.app.job.JobInfo.Builder#setDataTransfer} API for data transfers
- * that can be deferred until conditions are ideal for the app or device.
- * </p>
*/
@RequiresPermission(
value = Manifest.permission.FOREGROUND_SERVICE_DATA_SYNC,
diff --git a/core/java/android/content/pm/Signature.aidl b/core/java/android/content/pm/Signature.aidl
deleted file mode 100644
index 36c127a..0000000
--- a/core/java/android/content/pm/Signature.aidl
+++ /dev/null
@@ -1,32 +0,0 @@
-/* //device/java/android/android/view/WindowManager.aidl
-**
-** Copyright 2007, 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.content.pm;
-
-/* For the key attestation application id provider service we needed a native implementation
- * of the Signature parcelable because the service is used by the native keystore.
- * The native implementation is now located at
- * system/security/keystore/Signature.cpp
- * and
- * system/security/keystore/include/keystore/Signature.h.
- * and can be used by linking against libkeystore_binder.
- *
- * This is not the best arrangement. If you, dear reader, happen to implement native implementations
- * for the package manager's parcelables, consider moving Signature.cpp/.h to your library and
- * adjust keystore's dependencies accordingly. Thank you.
- */
-parcelable Signature cpp_header "keystore/Signature.h";
diff --git a/core/java/android/content/pm/UserProperties.java b/core/java/android/content/pm/UserProperties.java
index 96af2b6..884d463 100644
--- a/core/java/android/content/pm/UserProperties.java
+++ b/core/java/android/content/pm/UserProperties.java
@@ -49,6 +49,7 @@
private static final String ATTR_SHOW_IN_LAUNCHER = "showInLauncher";
private static final String ATTR_START_WITH_PARENT = "startWithParent";
private static final String ATTR_SHOW_IN_SETTINGS = "showInSettings";
+ private static final String ATTR_HIDE_IN_SETTINGS_IN_QUIET_MODE = "hideInSettingsInQuietMode";
private static final String ATTR_INHERIT_DEVICE_POLICY = "inheritDevicePolicy";
private static final String ATTR_USE_PARENTS_CONTACTS = "useParentsContacts";
private static final String ATTR_UPDATE_CROSS_PROFILE_INTENT_FILTERS_ON_OTA =
@@ -78,6 +79,7 @@
INDEX_CREDENTIAL_SHAREABLE_WITH_PARENT,
INDEX_DELETE_APP_WITH_PARENT,
INDEX_ALWAYS_VISIBLE,
+ INDEX_HIDE_IN_SETTINGS_IN_QUIET_MODE,
})
@Retention(RetentionPolicy.SOURCE)
private @interface PropertyIndex {
@@ -94,6 +96,7 @@
private static final int INDEX_CREDENTIAL_SHAREABLE_WITH_PARENT = 9;
private static final int INDEX_DELETE_APP_WITH_PARENT = 10;
private static final int INDEX_ALWAYS_VISIBLE = 11;
+ private static final int INDEX_HIDE_IN_SETTINGS_IN_QUIET_MODE = 12;
/** A bit set, mapping each PropertyIndex to whether it is present (1) or absent (0). */
private long mPropertiesPresent = 0;
@@ -324,6 +327,7 @@
if (hasManagePermission) {
// Add items that require MANAGE_USERS or stronger.
setShowInSettings(orig.getShowInSettings());
+ setHideInSettingsInQuietMode(orig.getHideInSettingsInQuietMode());
setUseParentsContacts(orig.getUseParentsContacts());
}
if (hasQueryOrManagePermission) {
@@ -409,6 +413,42 @@
private @ShowInSettings int mShowInSettings;
/**
+ * Returns whether a user should be shown in the Settings app depending on the quiet mode.
+ * This is generally inapplicable for non-profile users.
+ *
+ * <p> {@link #getShowInSettings()} returns whether / how a user should be shown in Settings.
+ * However, if this behaviour should be changed based on the quiet mode of the user, then this
+ * property can be used. If the property is not set then the user is shown in the Settings app
+ * irrespective of whether the user is in quiet mode or not. If the property is set, then the
+ * user is shown in the Settings app only if the user is not in the quiet mode. Please note that
+ * this property takes effect only if {@link #getShowInSettings()} does not return
+ * {@link #SHOW_IN_SETTINGS_NO}.
+ *
+ * <p> The caller must have {@link android.Manifest.permission#MANAGE_USERS} to query this
+ * property.
+ *
+ * @return true if a profile should be shown in the Settings only when the user is not in the
+ * quiet mode.
+ *
+ * See also {@link #getShowInSettings()}, {@link #setShowInSettings(int)},
+ * {@link ShowInSettings}
+ *
+ * @hide
+ */
+ public boolean getHideInSettingsInQuietMode() {
+ if (isPresent(INDEX_HIDE_IN_SETTINGS_IN_QUIET_MODE)) return mHideInSettingsInQuietMode;
+ if (mDefaultProperties != null) return mDefaultProperties.mHideInSettingsInQuietMode;
+ throw new SecurityException(
+ "You don't have permission to query HideInSettingsInQuietMode");
+ }
+ /** @hide */
+ public void setHideInSettingsInQuietMode(boolean hideInSettingsInQuietMode) {
+ this.mHideInSettingsInQuietMode = hideInSettingsInQuietMode;
+ setPresent(INDEX_HIDE_IN_SETTINGS_IN_QUIET_MODE);
+ }
+ private boolean mHideInSettingsInQuietMode;
+
+ /**
* Returns whether a profile should be started when its parent starts (unless in quiet mode).
* This only applies for users that have parents (i.e. for profiles).
* @hide
@@ -724,6 +764,9 @@
case ATTR_SHOW_IN_SETTINGS:
setShowInSettings(parser.getAttributeInt(i));
break;
+ case ATTR_HIDE_IN_SETTINGS_IN_QUIET_MODE:
+ setHideInSettingsInQuietMode(parser.getAttributeBoolean(i));
+ break;
case ATTR_INHERIT_DEVICE_POLICY:
setInheritDevicePolicy(parser.getAttributeInt(i));
break;
@@ -777,6 +820,10 @@
if (isPresent(INDEX_SHOW_IN_SETTINGS)) {
serializer.attributeInt(null, ATTR_SHOW_IN_SETTINGS, mShowInSettings);
}
+ if (isPresent(INDEX_HIDE_IN_SETTINGS_IN_QUIET_MODE)) {
+ serializer.attributeBoolean(null, ATTR_HIDE_IN_SETTINGS_IN_QUIET_MODE,
+ mHideInSettingsInQuietMode);
+ }
if (isPresent(INDEX_INHERIT_DEVICE_POLICY)) {
serializer.attributeInt(null, ATTR_INHERIT_DEVICE_POLICY,
mInheritDevicePolicy);
@@ -823,6 +870,7 @@
dest.writeInt(mShowInLauncher);
dest.writeBoolean(mStartWithParent);
dest.writeInt(mShowInSettings);
+ dest.writeBoolean(mHideInSettingsInQuietMode);
dest.writeInt(mInheritDevicePolicy);
dest.writeBoolean(mUseParentsContacts);
dest.writeBoolean(mUpdateCrossProfileIntentFiltersOnOTA);
@@ -845,6 +893,7 @@
mShowInLauncher = source.readInt();
mStartWithParent = source.readBoolean();
mShowInSettings = source.readInt();
+ mHideInSettingsInQuietMode = source.readBoolean();
mInheritDevicePolicy = source.readInt();
mUseParentsContacts = source.readBoolean();
mUpdateCrossProfileIntentFiltersOnOTA = source.readBoolean();
@@ -881,6 +930,7 @@
private @ShowInLauncher int mShowInLauncher = SHOW_IN_LAUNCHER_WITH_PARENT;
private boolean mStartWithParent = false;
private @ShowInSettings int mShowInSettings = SHOW_IN_SETTINGS_WITH_PARENT;
+ private boolean mHideInSettingsInQuietMode = false;
private @InheritDevicePolicy int mInheritDevicePolicy = INHERIT_DEVICE_POLICY_NO;
private boolean mUseParentsContacts = false;
private boolean mUpdateCrossProfileIntentFiltersOnOTA = false;
@@ -910,6 +960,12 @@
return this;
}
+ /** Sets the value for {@link #mHideInSettingsInQuietMode} */
+ public Builder setHideInSettingsInQuietMode(boolean hideInSettingsInQuietMode) {
+ mHideInSettingsInQuietMode = hideInSettingsInQuietMode;
+ return this;
+ }
+
/** Sets the value for {@link #mInheritDevicePolicy}*/
public Builder setInheritDevicePolicy(
@InheritDevicePolicy int inheritRestrictionsDevicePolicy) {
@@ -972,6 +1028,7 @@
mShowInLauncher,
mStartWithParent,
mShowInSettings,
+ mHideInSettingsInQuietMode,
mInheritDevicePolicy,
mUseParentsContacts,
mUpdateCrossProfileIntentFiltersOnOTA,
@@ -989,6 +1046,7 @@
@ShowInLauncher int showInLauncher,
boolean startWithParent,
@ShowInSettings int showInSettings,
+ boolean hideInSettingsInQuietMode,
@InheritDevicePolicy int inheritDevicePolicy,
boolean useParentsContacts, boolean updateCrossProfileIntentFiltersOnOTA,
@CrossProfileIntentFilterAccessControlLevel int crossProfileIntentFilterAccessControl,
@@ -1001,6 +1059,7 @@
setShowInLauncher(showInLauncher);
setStartWithParent(startWithParent);
setShowInSettings(showInSettings);
+ setHideInSettingsInQuietMode(hideInSettingsInQuietMode);
setInheritDevicePolicy(inheritDevicePolicy);
setUseParentsContacts(useParentsContacts);
setUpdateCrossProfileIntentFiltersOnOTA(updateCrossProfileIntentFiltersOnOTA);
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index 89ca915..b2cc070 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -19,6 +19,7 @@
namespace: "package_manager_service"
description: "Feature flag to enable the prevent sdk-library be an application."
bug: "295843617"
+ is_fixed_read_only: true
}
flag {
@@ -42,3 +43,10 @@
description: "Feature flag to enable the feature to retrieve package info without installation."
bug: "269149275"
}
+
+flag {
+ name: "use_art_service_v2"
+ namespace: "package_manager_service"
+ description: "Feature flag to enable the features that rely on new ART Service APIs that are in the VIC version of the ART module."
+ bug: "304741685"
+}
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationUserState.java b/core/java/android/content/pm/verify/domain/DomainVerificationUserState.java
index 1e60abb..7ada946 100644
--- a/core/java/android/content/pm/verify/domain/DomainVerificationUserState.java
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationUserState.java
@@ -231,7 +231,7 @@
}
/**
- * Mapping of domain host to state, as defined by {@link DomainState}.
+ * Mapping of domain host to state, as defined by the {@code DOMAIN_STATE_*} constants
*/
@DataClass.Generated.Member
public @NonNull Map<String,Integer> getHostToStateMap() {
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index ed22284..1b37092 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -2210,8 +2210,7 @@
*
* <p>The best practices is to obtain metrics from
* {@link WindowManager#getCurrentWindowMetrics()} for window bounds. The value obtained from
- * this API may be wrong if {@link Context#getResources()} is from
- * non-{@link android.annotation.UiContext}.
+ * this API may be wrong if {@link Context#getResources()} is not from a {@code UiContext}.
* For example, use the {@link DisplayMetrics} obtained from {@link Application#getResources()}
* to build {@link android.app.Activity} UI elements especially when the
* {@link android.app.Activity} is in the multi-window mode or on the secondary {@link Display}.
diff --git a/core/java/android/credentials/flags.aconfig b/core/java/android/credentials/flags.aconfig
index 0d0305b..9b819a7 100644
--- a/core/java/android/credentials/flags.aconfig
+++ b/core/java/android/credentials/flags.aconfig
@@ -5,4 +5,11 @@
name: "settings_activity_enabled"
description: "Enable the Credential Manager Settings Activity APIs"
bug: "300014059"
-}
\ No newline at end of file
+}
+
+flag {
+ namespace: "credential_manager"
+ name: "instant_apps_enabled"
+ description: "Enables Credential Manager to work with Instant Apps"
+ bug: "302190269"
+}
diff --git a/core/java/android/hardware/biometrics/CryptoObject.java b/core/java/android/hardware/biometrics/CryptoObject.java
index 151f819..6ac1efb 100644
--- a/core/java/android/hardware/biometrics/CryptoObject.java
+++ b/core/java/android/hardware/biometrics/CryptoObject.java
@@ -114,8 +114,8 @@
}
/**
- * Get {@link PresentationSession} object.
- * @return {@link PresentationSession} object or null if this doesn't contain one.
+ * Get {@link KeyAgreement} object.
+ * @return {@link KeyAgreement} object or null if this doesn't contain one.
*/
@FlaggedApi(FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT)
public KeyAgreement getKeyAgreement() {
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 990ebc5..f347909 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -20,6 +20,7 @@
import static android.view.Display.HdrCapabilities.HdrType;
import android.Manifest;
+import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.IntRange;
@@ -27,7 +28,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
-import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
@@ -377,7 +377,7 @@
* @see #createVirtualDisplay
* @hide
*/
- @SuppressLint("UnflaggedApi")
+ @FlaggedApi(android.companion.virtual.flags.Flags.FLAG_VDM_PUBLIC_APIS)
@SystemApi
public static final int VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT = 1 << 7;
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index a6d8caf..0c95c2e 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -79,4 +79,9 @@
Map getTagIntentAppPreferenceForUser(int userId);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)")
int setTagIntentAppPreferenceForUser(int userId, String pkg, boolean allow);
+
+ boolean isReaderOptionEnabled();
+ boolean isReaderOptionSupported();
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)")
+ boolean enableReaderOption(boolean enable);
}
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 1307dfc..4658630 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -17,6 +17,7 @@
package android.nfc;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -1826,6 +1827,97 @@
}
/**
+ * Sets NFC Reader option feature.
+ * <p>This API is for the Settings application.
+ * @return True if successful
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_READER_OPTION)
+ @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+ public boolean enableReaderOption(boolean enable) {
+ if (!sHasNfcFeature) {
+ throw new UnsupportedOperationException();
+ }
+ try {
+ return sService.enableReaderOption(enable);
+ } catch (RemoteException e) {
+ attemptDeadServiceRecovery(e);
+ // Try one more time
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover NFC Service.");
+ return false;
+ }
+ try {
+ return sService.enableReaderOption(enable);
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to recover NFC Service.");
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Checks if the device supports NFC Reader option functionality.
+ *
+ * @return True if device supports NFC Reader option, false otherwise
+ * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_READER_OPTION)
+ public boolean isReaderOptionSupported() {
+ if (!sHasNfcFeature) {
+ throw new UnsupportedOperationException();
+ }
+ try {
+ return sService.isReaderOptionSupported();
+ } catch (RemoteException e) {
+ attemptDeadServiceRecovery(e);
+ // Try one more time
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover NFC Service.");
+ return false;
+ }
+ try {
+ return sService.isReaderOptionSupported();
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to recover NFC Service.");
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Checks NFC Reader option feature is enabled.
+ *
+ * @return True if NFC Reader option is enabled, false otherwise
+ * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
+ * @throws UnsupportedOperationException if device doesn't support
+ * NFC Reader option functionality. {@link #isReaderOptionSupported}
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_READER_OPTION)
+ public boolean isReaderOptionEnabled() {
+ if (!sHasNfcFeature) {
+ throw new UnsupportedOperationException();
+ }
+ try {
+ return sService.isReaderOptionEnabled();
+ } catch (RemoteException e) {
+ attemptDeadServiceRecovery(e);
+ // Try one more time
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover NFC Service.");
+ return false;
+ }
+ try {
+ return sService.isReaderOptionEnabled();
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to recover NFC Service.");
+ }
+ return false;
+ }
+ }
+
+ /**
* Enable NDEF Push feature.
* <p>This API is for the Settings application.
* @hide
diff --git a/core/java/android/nfc/flags.aconfig b/core/java/android/nfc/flags.aconfig
index e3faf39..55b0b42 100644
--- a/core/java/android/nfc/flags.aconfig
+++ b/core/java/android/nfc/flags.aconfig
@@ -6,3 +6,10 @@
description: "Flag for NFC mainline changes"
bug: "292140387"
}
+
+flag {
+ name: "enable_nfc_reader_option"
+ namespace: "nfc"
+ description: "Flag for NFC reader option API changes"
+ bug: "291187960"
+}
diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java
index 092923e..6a4ec9b 100644
--- a/core/java/android/os/BatteryManager.java
+++ b/core/java/android/os/BatteryManager.java
@@ -16,7 +16,10 @@
package android.os;
+import static android.os.Flags.FLAG_STATE_OF_HEALTH_PUBLIC;
+
import android.Manifest.permission;
+import android.annotation.FlaggedApi;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
@@ -354,17 +357,11 @@
public static final int BATTERY_PROPERTY_CHARGING_POLICY = 9;
/**
- *
- * Percentage representing the measured battery state of health (remaining
- * estimated full charge capacity relative to the rated capacity in %).
- *
- * <p class="note">
- * The sender must hold the {@link android.Manifest.permission#BATTERY_STATS} permission.
- *
- * @hide
+ * Percentage representing the measured battery state of health.
+ * This is the remaining estimated full charge capacity relative
+ * to the rated capacity in %.
*/
- @RequiresPermission(permission.BATTERY_STATS)
- @SystemApi
+ @FlaggedApi(FLAG_STATE_OF_HEALTH_PUBLIC)
public static final int BATTERY_PROPERTY_STATE_OF_HEALTH = 10;
private final Context mContext;
diff --git a/core/java/android/os/BatteryStatsManager.java b/core/java/android/os/BatteryStatsManager.java
index 955fad3..3abe9a0 100644
--- a/core/java/android/os/BatteryStatsManager.java
+++ b/core/java/android/os/BatteryStatsManager.java
@@ -520,8 +520,9 @@
* @param uid calling package uid
* @param reason why Bluetooth has been turned on
* @param packageName package responsible for this change
- * @Deprecated Bluetooth self report its state and no longer call this
+ * @deprecated Bluetooth self report its state and no longer call this
*/
+ @Deprecated
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void reportBluetoothOn(int uid, int reason, @NonNull String packageName) {
}
@@ -532,8 +533,9 @@
* @param uid calling package uid
* @param reason why Bluetooth has been turned on
* @param packageName package responsible for this change
- * @Deprecated Bluetooth self report its state and no longer call this
+ * @deprecated Bluetooth self report its state and no longer call this
*/
+ @Deprecated
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void reportBluetoothOff(int uid, int reason, @NonNull String packageName) {
}
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index a95e66d..37559b3 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -1,6 +1,13 @@
package: "android.os"
flag {
+ name: "state_of_health_public"
+ namespace: "system_sw_battery"
+ description: "Feature flag for making state_of_health a public api."
+ bug: "288842045"
+}
+
+flag {
name: "disallow_cellular_null_ciphers_restriction"
namespace: "cellular_security"
description: "Guards a new UserManager user restriction that admins can use to require cellular encryption on their managed devices."
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 5c7a471..b19a034 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5354,6 +5354,37 @@
public static final Uri NOTIFICATION_SOUND_CACHE_URI = getUriFor(NOTIFICATION_SOUND_CACHE);
/**
+ * When enabled, notifications attention effects: sound, vibration, flashing
+ * will have a cooldown timer.
+ *
+ * The value 1 - enable, 0 - disable
+ * @hide
+ */
+ public static final String NOTIFICATION_COOLDOWN_ENABLED =
+ "notification_cooldown_enabled";
+
+ /**
+ * When enabled, notification cooldown will apply to all notifications.
+ * Otherwise cooldown will only apply to conversations.
+ *
+ * The value 1 - enable, 0 - disable
+ * Only valid if {@code NOTIFICATION_COOLDOWN_ENABLED} is enabled.
+ * @hide
+ */
+ public static final String NOTIFICATION_COOLDOWN_ALL =
+ "notification_cooldown_all";
+
+ /**
+ * When enabled, notification attention effects will be restricted to vibration only
+ * as long as the screen is unlocked.
+ *
+ * The value 1 - enable, 0 - disable
+ * @hide
+ */
+ public static final String NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED =
+ "notification_cooldown_vibrate_unlocked";
+
+ /**
* Persistent store for the system-wide default alarm alert.
*
* @see #RINGTONE
@@ -12471,6 +12502,13 @@
"wireless_charging_started_sound";
/**
+ * Whether to auto enable reverse charging once plugged-in.
+ * @hide
+ */
+ public static final String REVERSE_CHARGING_AUTO_ON =
+ "settings_key_reverse_charging_auto_turn_on";
+
+ /**
* URI for "wired charging started" sound.
* @hide
*/
diff --git a/core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl b/core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl
deleted file mode 100644
index dbffd5f..0000000
--- a/core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keymaster;
-
-import android.security.keymaster.KeyAttestationApplicationId;
-import android.security.keymaster.KeyAttestationPackageInfo;
-import android.content.pm.Signature;
-
-/**
- * This must be kept manually in sync with system/security/keystore until AIDL
- * can generate both Java and C++ bindings.
- *
- * @hide
- */
-interface IKeyAttestationApplicationIdProvider {
- /* keep in sync with /system/security/keystore/keystore_attestation_id.cpp */
- KeyAttestationApplicationId getKeyAttestationApplicationId(int uid);
-}
diff --git a/core/java/android/security/keymaster/KeyAttestationApplicationId.aidl b/core/java/android/security/keymaster/KeyAttestationApplicationId.aidl
deleted file mode 100644
index 9f6ff58..0000000
--- a/core/java/android/security/keymaster/KeyAttestationApplicationId.aidl
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keymaster;
-
-/* The cpp_header is relative to system/security/keystore/include
- * Link against libkeystore_binder to make use of the native implementation of this Parcelable.
- */
-parcelable KeyAttestationApplicationId cpp_header "keystore/KeyAttestationApplicationId.h";
diff --git a/core/java/android/security/keymaster/KeyAttestationApplicationId.java b/core/java/android/security/keymaster/KeyAttestationApplicationId.java
deleted file mode 100644
index 670f30e1b..0000000
--- a/core/java/android/security/keymaster/KeyAttestationApplicationId.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keymaster;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * @hide
- * The information aggregated by this class is used by keystore to identify a caller of the
- * keystore API toward a remote party. It aggregates multiple PackageInfos because keystore
- * can only determine a caller by uid granularity, and a uid can be shared by multiple packages.
- * The remote party must decide if it trusts all of the packages enough to consider the
- * confidentiality of the key material in question intact.
- */
-public class KeyAttestationApplicationId implements Parcelable {
- private final KeyAttestationPackageInfo[] mAttestationPackageInfos;
-
- /**
- * @param mAttestationPackageInfos
- */
- public KeyAttestationApplicationId(KeyAttestationPackageInfo[] mAttestationPackageInfos) {
- super();
- this.mAttestationPackageInfos = mAttestationPackageInfos;
- }
-
- /**
- * @return the mAttestationPackageInfos
- */
- public KeyAttestationPackageInfo[] getAttestationPackageInfos() {
- return mAttestationPackageInfos;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeTypedArray(mAttestationPackageInfos, flags);
- }
-
- public static final @android.annotation.NonNull Parcelable.Creator<KeyAttestationApplicationId> CREATOR
- = new Parcelable.Creator<KeyAttestationApplicationId>() {
- @Override
- public KeyAttestationApplicationId createFromParcel(Parcel source) {
- return new KeyAttestationApplicationId(source);
- }
-
- @Override
- public KeyAttestationApplicationId[] newArray(int size) {
- return new KeyAttestationApplicationId[size];
- }
- };
-
- KeyAttestationApplicationId(Parcel source) {
- mAttestationPackageInfos = source.createTypedArray(KeyAttestationPackageInfo.CREATOR);
- }
-}
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
deleted file mode 100644
index f8b843b..0000000
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keymaster;
-
-/* The cpp_header is relative to system/security/keystore/include
- * Link against libkeystore_binder to make use of the native implementation of this Parcelable.
- */
-parcelable KeyAttestationPackageInfo cpp_header "keystore/KeyAttestationPackageInfo.h";
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.java b/core/java/android/security/keymaster/KeyAttestationPackageInfo.java
deleted file mode 100644
index c0b8d8d..0000000
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keymaster;
-
-import android.content.pm.Signature;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * @hide
- * This class constitutes and excerpt from the PackageManager's PackageInfo for the purpose of
- * key attestation. It is part of the KeyAttestationApplicationId, which is used by
- * keystore to identify the caller of the keystore API towards a remote party.
- */
-public class KeyAttestationPackageInfo implements Parcelable {
- private final String mPackageName;
- private final long mPackageVersionCode;
- private final Signature[] mPackageSignatures;
-
- /**
- * @param mPackageName
- * @param mPackageVersionCode
- * @param mPackageSignatures
- */
- public KeyAttestationPackageInfo(
- String mPackageName, long mPackageVersionCode, Signature[] mPackageSignatures) {
- super();
- this.mPackageName = mPackageName;
- this.mPackageVersionCode = mPackageVersionCode;
- this.mPackageSignatures = mPackageSignatures;
- }
- /**
- * @return the mPackageName
- */
- public String getPackageName() {
- return mPackageName;
- }
- /**
- * @return the mPackageVersionCode
- */
- public long getPackageVersionCode() {
- return mPackageVersionCode;
- }
- /**
- * @return the mPackageSignatures
- */
- public Signature[] getPackageSignatures() {
- return mPackageSignatures;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(mPackageName);
- dest.writeLong(mPackageVersionCode);
- dest.writeTypedArray(mPackageSignatures, flags);
- }
-
- public static final @android.annotation.NonNull Parcelable.Creator<KeyAttestationPackageInfo> CREATOR
- = new Parcelable.Creator<KeyAttestationPackageInfo>() {
- @Override
- public KeyAttestationPackageInfo createFromParcel(Parcel source) {
- return new KeyAttestationPackageInfo(source);
- }
-
- @Override
- public KeyAttestationPackageInfo[] newArray(int size) {
- return new KeyAttestationPackageInfo[size];
- }
- };
-
- private KeyAttestationPackageInfo(Parcel source) {
- mPackageName = source.readString();
- mPackageVersionCode = source.readLong();
- mPackageSignatures = source.createTypedArray(Signature.CREATOR);
- }
-}
diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java
index 8cf2ce4..7ec1483 100644
--- a/core/java/android/service/autofill/FillRequest.java
+++ b/core/java/android/service/autofill/FillRequest.java
@@ -97,8 +97,6 @@
*/
public static final @RequestFlags int FLAG_VIEW_NOT_FOCUSED = 0x10;
- // The flag value 0x20 has been defined in AutofillManager.
-
/**
* Indicates the request supports fill dialog presentation for the fields, the
* system will send the request when the activity just started.
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index 0dad96e..c82a4ca 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -17,7 +17,6 @@
import android.annotation.Nullable;
import android.annotation.SuppressLint;
-import android.annotation.TestApi;
import android.app.Notification;
import android.os.Bundle;
import android.os.Parcel;
@@ -27,6 +26,7 @@
import android.system.OsConstants;
import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
@@ -40,7 +40,6 @@
* @hide
*/
@SuppressLint({"ParcelNotFinal", "ParcelCreator"})
-@TestApi
public class NotificationRankingUpdate implements Parcelable {
private final NotificationListenerService.RankingMap mRankingMap;
@@ -101,7 +100,7 @@
throw new RuntimeException(e);
} finally {
mapParcel.recycle();
- if (buffer != null) {
+ if (buffer != null && mRankingMapFd != null) {
mRankingMapFd.unmap(buffer);
mRankingMapFd.close();
}
@@ -140,7 +139,7 @@
*
* @hide
*/
- @TestApi
+ @VisibleForTesting(otherwise = VisibleForTesting.NONE)
public final boolean isFdNotNullAndClosed() {
return mRankingMapFd != null && mRankingMapFd.getFd() == -1;
}
diff --git a/core/java/android/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java
index bb5dd7f..3ed13bb 100644
--- a/core/java/android/speech/SpeechRecognizer.java
+++ b/core/java/android/speech/SpeechRecognizer.java
@@ -536,7 +536,16 @@
@MainThread
public void setRecognitionListener(RecognitionListener listener) {
checkIsCalledFromMainThread();
- putMessage(Message.obtain(mHandler, MSG_CHANGE_LISTENER, listener));
+ if (mListener.mInternalListener == null) {
+ // This shortcut is needed because otherwise, if there's an error connecting, it never
+ // gets delivered. I.e., the onSuccess callback set up in connectToSystemService does
+ // not get called, MSG_CHANGE_LISTENER does not get executed, so the onError in the same
+ // place does not get forwarded anywhere.
+ // Thread-wise, this is safe as both this method and the handler are on the UI thread.
+ handleChangeListener(listener);
+ } else {
+ putMessage(Message.obtain(mHandler, MSG_CHANGE_LISTENER, listener));
+ }
}
/**
diff --git a/core/java/android/text/TextFlags.java b/core/java/android/text/TextFlags.java
index 536e3cc..9edf298 100644
--- a/core/java/android/text/TextFlags.java
+++ b/core/java/android/text/TextFlags.java
@@ -62,6 +62,18 @@
};
/**
+ * List of the default values of the text flags.
+ *
+ * The order must be the same to the TEXT_ACONFIG_FLAGS.
+ */
+ public static final boolean[] TEXT_ACONFIG_DEFAULT_VALUE = {
+ Flags.deprecateFontsXml(),
+ Flags.noBreakNoHyphenationSpan(),
+ Flags.phraseStrictFallback(),
+ Flags.useBoundsForWidth(),
+ };
+
+ /**
* Get a key for the feature flag.
*/
public static String getKeyForFlag(@NonNull String flag) {
diff --git a/core/java/android/text/flags/custom_locale_fallback.aconfig b/core/java/android/text/flags/custom_locale_fallback.aconfig
index 04e64f9..52fe883 100644
--- a/core/java/android/text/flags/custom_locale_fallback.aconfig
+++ b/core/java/android/text/flags/custom_locale_fallback.aconfig
@@ -4,5 +4,6 @@
name: "custom_locale_fallback"
namespace: "text"
description: "A feature flag that adds custom locale fallback to the vendor customization XML. This enables vendors to add their locale specific fonts, e.g. Japanese font."
- bug: ""
+ is_fixed_read_only: true
+ bug: "278768958"
}
diff --git a/core/java/android/text/flags/deprecate_fonts_xml.aconfig b/core/java/android/text/flags/deprecate_fonts_xml.aconfig
index 58dc210..5362138 100644
--- a/core/java/android/text/flags/deprecate_fonts_xml.aconfig
+++ b/core/java/android/text/flags/deprecate_fonts_xml.aconfig
@@ -4,5 +4,7 @@
name: "deprecate_fonts_xml"
namespace: "text"
description: "Feature flag for deprecating fonts.xml. By setting true for this feature flag, the new font configuration XML, /system/etc/font_fallback.xml is used. The new XML has a new syntax and flexibility of variable font declarations, but it is not compatible with the apps that reads fonts.xml. So, fonts.xml is maintained as a subset of the font_fallback.xml"
+ # Make read only, as it could be used before the Settings provider is initialized.
+ is_fixed_read_only: true
bug: "281769620"
}
diff --git a/core/java/android/text/flags/fix_line_height_for_locale.aconfig b/core/java/android/text/flags/fix_line_height_for_locale.aconfig
new file mode 100644
index 0000000..8696bfa
--- /dev/null
+++ b/core/java/android/text/flags/fix_line_height_for_locale.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.text.flags"
+
+flag {
+ name: "fix_line_height_for_locale"
+ namespace: "text"
+ description: "Feature flag that preserve the line height of the TextView and EditText even if the the locale is different from Latin"
+ bug: "303326708"
+}
diff --git a/core/java/android/text/flags/optimized_font_loading.aconfig b/core/java/android/text/flags/optimized_font_loading.aconfig
new file mode 100644
index 0000000..0e4a69f
--- /dev/null
+++ b/core/java/android/text/flags/optimized_font_loading.aconfig
@@ -0,0 +1,11 @@
+package: "com.android.text.flags"
+
+flag {
+ name: "use_optimized_boottime_font_loading"
+ namespace: "text"
+ description: "Feature flag ensuring that font is loaded once and asynchronously."
+ # Make read only, as font loading is in the critical boot path which happens before the read-write
+ # flags propagate to the device.
+ is_fixed_read_only: true
+ bug: "304406888"
+}
diff --git a/core/java/android/util/Base64.java b/core/java/android/util/Base64.java
index 92abd7c..33cc5e3 100644
--- a/core/java/android/util/Base64.java
+++ b/core/java/android/util/Base64.java
@@ -26,6 +26,7 @@
* href="http://www.ietf.org/rfc/rfc2045.txt">2045</a> and <a
* href="http://www.ietf.org/rfc/rfc3548.txt">3548</a>.
*/
+@android.ravenwood.annotations.RavenwoodWholeClassKeep
public class Base64 {
/**
* Default values for encoder/decoder flags.
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 1ab055a..a74cbe4 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -16,9 +16,11 @@
package android.view;
+import static android.view.flags.Flags.FLAG_EXPECTED_PRESENTATION_TIME_API;
import static android.view.DisplayEventReceiver.VSYNC_SOURCE_APP;
import static android.view.DisplayEventReceiver.VSYNC_SOURCE_SURFACE_FLINGER;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
@@ -695,6 +697,7 @@
*/
@TestApi
@UnsupportedAppUsage
+ @FlaggedApi(FLAG_EXPECTED_PRESENTATION_TIME_API)
public long getFrameTimeNanos() {
synchronized (mLock) {
if (!mCallbacksRunning) {
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 139c0be..b080b71 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -1790,7 +1790,12 @@
public float xDpi;
public float yDpi;
+ // Some modes have peak refresh rate lower than the panel vsync rate.
public float refreshRate;
+ // Fixed rate of vsync deadlines for the panel.
+ // This can be higher then the peak refresh rate for some panel technologies
+ // See: VrrConfig.aidl
+ public float vsyncRate;
public long appVsyncOffsetNanos;
public long presentationDeadlineNanos;
public int[] supportedHdrTypes;
@@ -1811,6 +1816,7 @@
+ ", xDpi=" + xDpi
+ ", yDpi=" + yDpi
+ ", refreshRate=" + refreshRate
+ + ", vsyncRate=" + vsyncRate
+ ", appVsyncOffsetNanos=" + appVsyncOffsetNanos
+ ", presentationDeadlineNanos=" + presentationDeadlineNanos
+ ", supportedHdrTypes=" + Arrays.toString(supportedHdrTypes)
@@ -1828,6 +1834,7 @@
&& Float.compare(that.xDpi, xDpi) == 0
&& Float.compare(that.yDpi, yDpi) == 0
&& Float.compare(that.refreshRate, refreshRate) == 0
+ && Float.compare(that.vsyncRate, vsyncRate) == 0
&& appVsyncOffsetNanos == that.appVsyncOffsetNanos
&& presentationDeadlineNanos == that.presentationDeadlineNanos
&& Arrays.equals(supportedHdrTypes, that.supportedHdrTypes)
@@ -1836,8 +1843,9 @@
@Override
public int hashCode() {
- return Objects.hash(id, width, height, xDpi, yDpi, refreshRate, appVsyncOffsetNanos,
- presentationDeadlineNanos, group, Arrays.hashCode(supportedHdrTypes));
+ return Objects.hash(id, width, height, xDpi, yDpi, refreshRate, vsyncRate,
+ appVsyncOffsetNanos, presentationDeadlineNanos, group,
+ Arrays.hashCode(supportedHdrTypes));
}
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index cfde400..16318e0 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -12161,12 +12161,6 @@
* a precision touch gesture in a small area in either the X or Y dimension, such as
* an edge swipe or dragging a <code>SeekBar</code> thumb.</p>
*
- * <p>On Wear OS, these rects control where system-level swipe-to-dismiss gesture can start. If
- * the attribute {@code android:windowSwipeToDismiss} has been set to {@code false}, the system
- * will create an exclusion rect with size equal to the window frame size. In order words, the
- * system swipe-to-dismiss will not apply, and the app must handle gestural input within itself.
- * </p>
- *
* <p>Note: the system will put a limit of <code>200dp</code> on the vertical extent of the
* exclusions it takes into account. The limit does not apply while the navigation
* bar is {@link #SYSTEM_UI_FLAG_IMMERSIVE_STICKY stickily} hidden, nor to the
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index aa47f07..e111dc8 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2232,9 +2232,7 @@
mStopped = stopped;
final ThreadedRenderer renderer = mAttachInfo.mThreadedRenderer;
if (renderer != null) {
- if (DEBUG_DRAW) {
- Log.d(mTag, "WindowStopped on " + getTitle() + " set to " + mStopped);
- }
+ if (DEBUG_DRAW) Log.d(mTag, "WindowStopped on " + getTitle() + " set to " + mStopped);
renderer.setStopped(mStopped);
}
if (!mStopped) {
@@ -3879,8 +3877,8 @@
mPendingTransitions.clear();
}
- handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mPendingTransaction,
- "view not visible");
+ handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mHasPendingTransactions,
+ mPendingTransaction, "view not visible");
} else if (cancelAndRedraw) {
mLastPerformTraversalsSkipDrawReason = cancelDueToPreDrawListener
? "predraw_" + mAttachInfo.mTreeObserver.getLastDispatchOnPreDrawCanceledReason()
@@ -3895,8 +3893,8 @@
mPendingTransitions.clear();
}
if (!performDraw(mActiveSurfaceSyncGroup)) {
- handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mPendingTransaction,
- mLastPerformDrawSkippedReason);
+ handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mHasPendingTransactions,
+ mPendingTransaction, mLastPerformDrawSkippedReason);
}
}
@@ -4774,8 +4772,8 @@
if (mSurfaceHolder != null && mSurface.isValid()) {
usingAsyncReport = true;
SurfaceCallbackHelper sch = new SurfaceCallbackHelper(() -> {
- handleSyncRequestWhenNoAsyncDraw(surfaceSyncGroup, pendingTransaction,
- "SurfaceHolder");
+ handleSyncRequestWhenNoAsyncDraw(surfaceSyncGroup, pendingTransaction != null,
+ pendingTransaction, "SurfaceHolder");
});
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
@@ -4789,8 +4787,8 @@
}
if (!usingAsyncReport) {
- handleSyncRequestWhenNoAsyncDraw(surfaceSyncGroup, pendingTransaction,
- "no async report");
+ handleSyncRequestWhenNoAsyncDraw(surfaceSyncGroup, pendingTransaction != null,
+ pendingTransaction, "no async report");
}
if (mPerformContentCapture) {
@@ -4800,13 +4798,14 @@
}
private void handleSyncRequestWhenNoAsyncDraw(SurfaceSyncGroup surfaceSyncGroup,
- @Nullable Transaction pendingTransaction, String logReason) {
+ boolean hasPendingTransaction, @Nullable Transaction pendingTransaction,
+ String logReason) {
if (surfaceSyncGroup != null) {
- if (pendingTransaction != null) {
+ if (hasPendingTransaction && pendingTransaction != null) {
surfaceSyncGroup.addTransaction(pendingTransaction);
}
surfaceSyncGroup.markSyncReady();
- } else if (pendingTransaction != null) {
+ } else if (hasPendingTransaction && pendingTransaction != null) {
Trace.instant(Trace.TRACE_TAG_VIEW,
"Transaction not synced due to " + logReason + "-" + mTag);
if (DEBUG_BLAST) {
@@ -8606,10 +8605,6 @@
mLastLayoutFrame.set(frame);
}
- if (mOnBackInvokedDispatcher.isSystemGestureExclusionNeeded()) {
- setRootSystemGestureExclusionRects(List.of(frame));
- }
-
final WindowConfiguration winConfig = getCompatWindowConfiguration();
mPendingBackDropFrame.set(mPendingDragResizing && !winConfig.useWindowFrameForBackdrop()
? winConfig.getMaxBounds()
@@ -9054,8 +9049,8 @@
mAdded = false;
AnimationHandler.removeRequestor(this);
}
- handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mPendingTransaction,
- "shutting down VRI");
+ handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mHasPendingTransactions,
+ mPendingTransaction, "shutting down VRI");
WindowManagerGlobal.getInstance().doRemoveView(this);
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 2f4bea0..08f9c8c 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -82,6 +82,7 @@
import android.Manifest.permission;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -5821,6 +5822,7 @@
*
* @hide
*/
+ @FlaggedApi("REPLACE_CONTENT_WITH_MIRROR")
@TestApi
@RequiresPermission(permission.ACCESS_SURFACE_FLINGER)
default boolean replaceContentOnDisplayWithMirror(int displayId, @NonNull Window window) {
@@ -5836,6 +5838,7 @@
*
* @hide
*/
+ @FlaggedApi("REPLACE_CONTENT_WITH_MIRROR")
@TestApi
@RequiresPermission(permission.ACCESS_SURFACE_FLINGER)
default boolean replaceContentOnDisplayWithSc(int displayId, @NonNull SurfaceControl sc) {
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index e31ad82..85dadd4 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -5,4 +5,11 @@
name: "force_invert_color"
description: "Enable force force-dark for smart inversion and dark theme everywhere"
bug: "282821643"
-}
\ No newline at end of file
+}
+
+flag {
+ namespace: "accessibility"
+ name: "allow_shortcut_chooser_on_lockscreen"
+ description: "Allows the a11y shortcut disambig dialog to appear on the lockscreen"
+ bug: "303871725"
+}
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index a07b62f..a76780d 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -108,11 +108,14 @@
* @hide
*/
@TestApi
+ @FlaggedApi(FLAG_EXPECTED_PRESENTATION_TIME_API)
public static void lockAnimationClock(long vsyncMillis, long expectedPresentationTimeNanos) {
AnimationState state = sAnimationState.get();
state.animationClockLocked = true;
state.currentVsyncTimeMillis = vsyncMillis;
- state.mExpectedPresentationTimeNanos = expectedPresentationTimeNanos;
+ if (!expectedPresentationTimeApi()) {
+ state.mExpectedPresentationTimeNanos = expectedPresentationTimeNanos;
+ }
}
/**
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 34e4c37..a40ff64 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -16,7 +16,6 @@
package android.view.autofill;
-import static android.Manifest.permission.PROVIDE_OWN_AUTOFILL_SUGGESTIONS;
import static android.service.autofill.FillRequest.FLAG_IME_SHOWING;
import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE;
@@ -31,12 +30,10 @@
import static android.view.autofill.Helper.toList;
import android.accessibilityservice.AccessibilityServiceInfo;
-import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
-import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
@@ -53,19 +50,15 @@
import android.content.pm.ResolveInfo;
import android.graphics.Rect;
import android.metrics.LogMaker;
-import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
-import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
-import android.os.ICancellationSignal;
import android.os.Looper;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.SystemClock;
import android.service.autofill.AutofillService;
-import android.service.autofill.FillCallback;
import android.service.autofill.FillEventHistory;
import android.service.autofill.Flags;
import android.service.autofill.IFillCallback;
@@ -89,7 +82,6 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeProvider;
import android.view.accessibility.AccessibilityWindowInfo;
-import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.inputmethod.InputMethodManager;
import android.widget.CheckBox;
import android.widget.DatePicker;
@@ -119,7 +111,6 @@
import java.util.List;
import java.util.Objects;
import java.util.Set;
-import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import sun.misc.Cleaner;
@@ -189,12 +180,6 @@
* shows an autofill save UI if the value of savable views have changed. If the user selects the
* option to Save, the current value of the views is then sent to the autofill service.
*
- * <p>There is another choice for the application to provide it's datasets to the Autofill framework
- * by setting an {@link AutofillRequestCallback} through
- * {@link #setAutofillRequestCallback(Executor, AutofillRequestCallback)}. The application can use
- * its callback instead of the default {@link AutofillService}. See
- * {@link AutofillRequestCallback} for more details.
- *
* <h3 id="additional-notes">Additional notes</h3>
*
* <p>It is safe to call <code>AutofillManager</code> methods from any thread.
@@ -334,7 +319,6 @@
/** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;
/** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4;
/** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY = 0x8;
- /** @hide */ public static final int FLAG_ENABLED_CLIENT_SUGGESTIONS = 0x20;
// NOTE: flag below is used by the session start receiver only, hence it can have values above
/** @hide */ public static final int RECEIVER_FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY = 0x1;
@@ -676,11 +660,6 @@
@GuardedBy("mLock")
private boolean mEnabledForAugmentedAutofillOnly;
- @GuardedBy("mLock")
- @Nullable private AutofillRequestCallback mAutofillRequestCallback;
- @GuardedBy("mLock")
- @Nullable private Executor mRequestCallbackExecutor;
-
private boolean mScreenHasCredmanField;
/**
@@ -2434,38 +2413,6 @@
return new AutofillId(parent.getAutofillViewId(), virtualId);
}
- /**
- * Sets the client's suggestions callback for autofill.
- *
- * @see AutofillRequestCallback
- *
- * @param executor specifies the thread upon which the callbacks will be invoked.
- * @param callback which handles autofill request to provide client's suggestions.
- */
- @RequiresPermission(PROVIDE_OWN_AUTOFILL_SUGGESTIONS)
- public void setAutofillRequestCallback(@NonNull @CallbackExecutor Executor executor,
- @NonNull AutofillRequestCallback callback) {
- if (mContext.checkSelfPermission(PROVIDE_OWN_AUTOFILL_SUGGESTIONS)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Requires PROVIDE_OWN_AUTOFILL_SUGGESTIONS permission!");
- }
-
- synchronized (mLock) {
- mRequestCallbackExecutor = executor;
- mAutofillRequestCallback = callback;
- }
- }
-
- /**
- * clears the client's suggestions callback for autofill.
- */
- public void clearAutofillRequestCallback() {
- synchronized (mLock) {
- mRequestCallbackExecutor = null;
- mAutofillRequestCallback = null;
- }
- }
-
@GuardedBy("mLock")
private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
@NonNull AutofillValue value, int flags) {
@@ -2526,13 +2473,6 @@
}
}
- if (mAutofillRequestCallback != null) {
- if (sDebug) {
- Log.d(TAG, "startSession with the client suggestions provider");
- }
- flags |= FLAG_ENABLED_CLIENT_SUGGESTIONS;
- }
-
mService.startSession(client.autofillClientGetActivityToken(),
mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
mCallback != null, flags, clientActivity,
@@ -2890,28 +2830,6 @@
}
}
- private void onFillRequest(InlineSuggestionsRequest request,
- CancellationSignal cancellationSignal, FillCallback callback) {
- final AutofillRequestCallback autofillRequestCallback;
- final Executor executor;
- synchronized (mLock) {
- autofillRequestCallback = mAutofillRequestCallback;
- executor = mRequestCallbackExecutor;
- }
- if (autofillRequestCallback != null && executor != null) {
- final long ident = Binder.clearCallingIdentity();
- try {
- executor.execute(() ->
- autofillRequestCallback.onFillRequest(
- request, cancellationSignal, callback));
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- } else {
- callback.onSuccess(null);
- }
- }
-
/** @hide */
public static final int SET_STATE_FLAG_ENABLED = 0x01;
/** @hide */
@@ -4495,23 +4413,6 @@
}
@Override
- public void requestFillFromClient(int id, InlineSuggestionsRequest request,
- IFillCallback callback) {
- final AutofillManager afm = mAfm.get();
- if (afm != null) {
- ICancellationSignal transport = CancellationSignal.createTransport();
- try {
- callback.onCancellable(transport);
- } catch (RemoteException e) {
- Slog.w(TAG, "Error requesting a cancellation", e);
- }
-
- afm.onFillRequest(request, CancellationSignal.fromTransport(transport),
- new FillCallback(callback, id));
- }
- }
-
- @Override
public void notifyFillDialogTriggerIds(List<AutofillId> ids) {
final AutofillManager afm = mAfm.get();
if (afm != null) {
diff --git a/core/java/android/view/autofill/AutofillRequestCallback.java b/core/java/android/view/autofill/AutofillRequestCallback.java
deleted file mode 100644
index e632a58..0000000
--- a/core/java/android/view/autofill/AutofillRequestCallback.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.autofill;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.CancellationSignal;
-import android.service.autofill.FillCallback;
-import android.view.inputmethod.InlineSuggestionsRequest;
-
-/**
- * <p>This class is used to provide some input suggestions to the Autofill framework.
- *
- * <P>When the user is requested to input something, Autofill will try to query input suggestions
- * for the user choosing. If the application want to provide some internal input suggestions,
- * implements this callback and register via
- * {@link AutofillManager#setAutofillRequestCallback(java.util.concurrent.Executor,
- * AutofillRequestCallback)}. Autofill will callback the
- * {@link #onFillRequest(InlineSuggestionsRequest, CancellationSignal, FillCallback)} to request
- * input suggestions.
- *
- * <P>To make sure the callback to take effect, must register before the autofill session starts.
- * If the autofill session is started, calls {@link AutofillManager#cancel()} to finish current
- * session, and then the callback will be used at the next restarted session.
- *
- * <P>To create a {@link android.service.autofill.FillResponse}, application should fetch
- * {@link AutofillId}s from its view structure. Below is an example:
- * <pre class="prettyprint">
- * AutofillId usernameId = findViewById(R.id.username).getAutofillId();
- * AutofillId passwordId = findViewById(R.id.password).getAutofillId();
- * </pre>
- * To learn more about creating a {@link android.service.autofill.FillResponse}, read
- * <a href="/guide/topics/text/autofill-services#fill">Fill out client views</a>.
- *
- * <P>To fallback to the default {@link android.service.autofill.AutofillService}, just respond
- * a null of the {@link android.service.autofill.FillResponse}. And then Autofill will do a fill
- * request with the default {@link android.service.autofill.AutofillService}. Or clear the callback
- * from {@link AutofillManager} via {@link AutofillManager#clearAutofillRequestCallback()}. If the
- * client would like to keep no suggestions for the field, respond with an empty
- * {@link android.service.autofill.FillResponse} which has no dataset.
- *
- * <P>IMPORTANT: This should not be used for displaying anything other than input suggestions, or
- * the keyboard may choose to block your app from the inline strip.
- */
-public interface AutofillRequestCallback {
- /**
- * Called by the Android system to decide if a screen can be autofilled by the callback.
- *
- * @param inlineSuggestionsRequest the {@link InlineSuggestionsRequest request} to handle if
- * currently inline suggestions are supported and can be displayed.
- * @param cancellationSignal signal for observing cancellation requests. The system will use
- * this to notify you that the fill result is no longer needed and you should stop
- * handling this fill request in order to save resources.
- * @param callback object used to notify the result of the request.
- */
- void onFillRequest(@Nullable InlineSuggestionsRequest inlineSuggestionsRequest,
- @NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback);
-}
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index 6e13097..917a974 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -24,11 +24,9 @@
import android.content.IntentSender;
import android.graphics.Rect;
import android.os.IBinder;
-import android.service.autofill.IFillCallback;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
import android.view.autofill.IAutofillWindowPresenter;
-import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.KeyEvent;
import com.android.internal.os.IResultReceiver;
@@ -149,12 +147,6 @@
void requestShowSoftInput(in AutofillId id);
/**
- * Requests to determine if a screen can be autofilled by the client app.
- */
- void requestFillFromClient(int id, in InlineSuggestionsRequest request,
- in IFillCallback callback);
-
- /**
* Notifies autofill ids that require to show the fill dialog.
*/
void notifyFillDialogTriggerIds(in List<AutofillId> ids);
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 2c7d326..42b3e38 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -60,7 +60,9 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -377,6 +379,30 @@
public static final String DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_BUFFER_SIZE =
"content_protection_buffer_size";
+ /**
+ * Sets the config for content protection required groups.
+ *
+ * @hide
+ */
+ public static final String DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_REQUIRED_GROUPS_CONFIG =
+ "content_protection_required_groups_config";
+
+ /**
+ * Sets the config for content protection optional groups.
+ *
+ * @hide
+ */
+ public static final String DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_OPTIONAL_GROUPS_CONFIG =
+ "content_protection_optional_groups_config";
+
+ /**
+ * Sets the threshold for content protection optional groups.
+ *
+ * @hide
+ */
+ public static final String DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_OPTIONAL_GROUPS_THRESHOLD =
+ "content_protection_optional_groups_threshold";
+
/** @hide */
@TestApi
public static final int LOGGING_LEVEL_OFF = 0;
@@ -417,6 +443,18 @@
public static final int DEFAULT_CONTENT_PROTECTION_APPS_BLOCKLIST_SIZE = 5000;
/** @hide */
public static final int DEFAULT_CONTENT_PROTECTION_BUFFER_SIZE = 150;
+ /** @hide */
+ public static final List<List<String>> DEFAULT_CONTENT_PROTECTION_REQUIRED_GROUPS =
+ Collections.emptyList();
+ /** @hide */
+ public static final String DEFAULT_CONTENT_PROTECTION_REQUIRED_GROUPS_CONFIG = "";
+ /** @hide */
+ public static final List<List<String>> DEFAULT_CONTENT_PROTECTION_OPTIONAL_GROUPS =
+ Collections.emptyList();
+ /** @hide */
+ public static final String DEFAULT_CONTENT_PROTECTION_OPTIONAL_GROUPS_CONFIG = "";
+ /** @hide */
+ public static final int DEFAULT_CONTENT_PROTECTION_OPTIONAL_GROUPS_THRESHOLD = 0;
private final Object mLock = new Object();
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
index 70279cc..c83dfe8 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
@@ -111,22 +111,6 @@
private @Nullable InlinePresentationSpec mInlineTooltipPresentationSpec;
/**
- * Whether the IME supports inline suggestions from the default Autofill service that
- * provides the input view.
- *
- * Note: The default value is {@code true}.
- */
- private boolean mServiceSupported;
-
- /**
- * Whether the IME supports inline suggestions from the application that provides the
- * input view.
- *
- * Note: The default value is {@code true}.
- */
- private boolean mClientSupported;
-
- /**
* @hide
* @see {@link #mHostInputToken}.
*/
@@ -220,14 +204,6 @@
return Bundle.EMPTY;
}
- private static boolean defaultServiceSupported() {
- return true;
- }
-
- private static boolean defaultClientSupported() {
- return true;
- }
-
/** @hide */
abstract static class BaseBuilder {
abstract Builder setInlinePresentationSpecs(
@@ -240,25 +216,13 @@
abstract Builder setHostDisplayId(int value);
}
- /** @hide */
- public boolean isServiceSupported() {
- return mServiceSupported;
- }
-
- /** @hide */
- public boolean isClientSupported() {
- return mClientSupported;
- }
-
-
-
- // Code below generated by codegen v1.0.22.
+ // Code below generated by codegen v1.0.23.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+ // $ codegen $ANDROID_BUILD_TOP/./frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
@@ -274,9 +238,7 @@
@NonNull Bundle extras,
@Nullable IBinder hostInputToken,
int hostDisplayId,
- @Nullable InlinePresentationSpec inlineTooltipPresentationSpec,
- boolean serviceSupported,
- boolean clientSupported) {
+ @Nullable InlinePresentationSpec inlineTooltipPresentationSpec) {
this.mMaxSuggestionCount = maxSuggestionCount;
this.mInlinePresentationSpecs = inlinePresentationSpecs;
com.android.internal.util.AnnotationValidations.validate(
@@ -293,8 +255,6 @@
this.mHostInputToken = hostInputToken;
this.mHostDisplayId = hostDisplayId;
this.mInlineTooltipPresentationSpec = inlineTooltipPresentationSpec;
- this.mServiceSupported = serviceSupported;
- this.mClientSupported = clientSupported;
onConstructed();
}
@@ -378,9 +338,7 @@
}
/**
- * The {@link InlinePresentationSpec} for the inline suggestion tooltip in the response.
- *
- * @see android.service.autofill.InlinePresentation#createTooltipPresentation(Slice, InlinePresentationSpec)
+ * Specifies the UI specification for the inline suggestion tooltip in the response.
*/
@DataClass.Generated.Member
public @Nullable InlinePresentationSpec getInlineTooltipPresentationSpec() {
@@ -401,9 +359,7 @@
"extras = " + mExtras + ", " +
"hostInputToken = " + mHostInputToken + ", " +
"hostDisplayId = " + mHostDisplayId + ", " +
- "inlineTooltipPresentationSpec = " + mInlineTooltipPresentationSpec + ", " +
- "serviceSupported = " + mServiceSupported + ", " +
- "clientSupported = " + mClientSupported +
+ "inlineTooltipPresentationSpec = " + mInlineTooltipPresentationSpec +
" }";
}
@@ -427,9 +383,7 @@
&& extrasEquals(that.mExtras)
&& java.util.Objects.equals(mHostInputToken, that.mHostInputToken)
&& mHostDisplayId == that.mHostDisplayId
- && java.util.Objects.equals(mInlineTooltipPresentationSpec, that.mInlineTooltipPresentationSpec)
- && mServiceSupported == that.mServiceSupported
- && mClientSupported == that.mClientSupported;
+ && java.util.Objects.equals(mInlineTooltipPresentationSpec, that.mInlineTooltipPresentationSpec);
}
@Override
@@ -447,8 +401,6 @@
_hash = 31 * _hash + java.util.Objects.hashCode(mHostInputToken);
_hash = 31 * _hash + mHostDisplayId;
_hash = 31 * _hash + java.util.Objects.hashCode(mInlineTooltipPresentationSpec);
- _hash = 31 * _hash + Boolean.hashCode(mServiceSupported);
- _hash = 31 * _hash + Boolean.hashCode(mClientSupported);
return _hash;
}
@@ -459,8 +411,6 @@
// void parcelFieldName(Parcel dest, int flags) { ... }
int flg = 0;
- if (mServiceSupported) flg |= 0x100;
- if (mClientSupported) flg |= 0x200;
if (mHostInputToken != null) flg |= 0x20;
if (mInlineTooltipPresentationSpec != null) flg |= 0x80;
dest.writeInt(flg);
@@ -486,11 +436,9 @@
// static FieldType unparcelFieldName(Parcel in) { ... }
int flg = in.readInt();
- boolean serviceSupported = (flg & 0x100) != 0;
- boolean clientSupported = (flg & 0x200) != 0;
int maxSuggestionCount = in.readInt();
List<InlinePresentationSpec> inlinePresentationSpecs = new ArrayList<>();
- in.readParcelableList(inlinePresentationSpecs, InlinePresentationSpec.class.getClassLoader(), android.widget.inline.InlinePresentationSpec.class);
+ in.readParcelableList(inlinePresentationSpecs, InlinePresentationSpec.class.getClassLoader());
String hostPackageName = in.readString();
LocaleList supportedLocales = (LocaleList) in.readTypedObject(LocaleList.CREATOR);
Bundle extras = in.readBundle();
@@ -514,8 +462,6 @@
this.mHostInputToken = hostInputToken;
this.mHostDisplayId = hostDisplayId;
this.mInlineTooltipPresentationSpec = inlineTooltipPresentationSpec;
- this.mServiceSupported = serviceSupported;
- this.mClientSupported = clientSupported;
onConstructed();
}
@@ -549,8 +495,6 @@
private @Nullable IBinder mHostInputToken;
private int mHostDisplayId;
private @Nullable InlinePresentationSpec mInlineTooltipPresentationSpec;
- private boolean mServiceSupported;
- private boolean mClientSupported;
private long mBuilderFieldsSet = 0L;
@@ -683,9 +627,7 @@
}
/**
- * The {@link InlinePresentationSpec} for the inline suggestion tooltip in the response.
- *
- * @see android.service.autofill.InlinePresentation#createTooltipPresentation(Slice, InlinePresentationSpec)s
+ * Specifies the UI specification for the inline suggestion tooltip in the response.
*/
@DataClass.Generated.Member
public @NonNull Builder setInlineTooltipPresentationSpec(@NonNull InlinePresentationSpec value) {
@@ -695,38 +637,10 @@
return this;
}
- /**
- * Whether the IME supports inline suggestions from the default Autofill service that
- * provides the input view.
- *
- * Note: The default value is {@code true}.
- */
- @DataClass.Generated.Member
- public @NonNull Builder setServiceSupported(boolean value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x100;
- mServiceSupported = value;
- return this;
- }
-
- /**
- * Whether the IME supports inline suggestions from the application that provides the
- * input view.
- *
- * Note: The default value is {@code true}.
- */
- @DataClass.Generated.Member
- public @NonNull Builder setClientSupported(boolean value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x200;
- mClientSupported = value;
- return this;
- }
-
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull InlineSuggestionsRequest build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x400; // Mark builder used
+ mBuilderFieldsSet |= 0x100; // Mark builder used
if ((mBuilderFieldsSet & 0x1) == 0) {
mMaxSuggestionCount = defaultMaxSuggestionCount();
@@ -749,12 +663,6 @@
if ((mBuilderFieldsSet & 0x80) == 0) {
mInlineTooltipPresentationSpec = defaultInlineTooltipPresentationSpec();
}
- if ((mBuilderFieldsSet & 0x100) == 0) {
- mServiceSupported = defaultServiceSupported();
- }
- if ((mBuilderFieldsSet & 0x200) == 0) {
- mClientSupported = defaultClientSupported();
- }
InlineSuggestionsRequest o = new InlineSuggestionsRequest(
mMaxSuggestionCount,
mInlinePresentationSpecs,
@@ -763,14 +671,12 @@
mExtras,
mHostInputToken,
mHostDisplayId,
- mInlineTooltipPresentationSpec,
- mServiceSupported,
- mClientSupported);
+ mInlineTooltipPresentationSpec);
return o;
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x400) != 0) {
+ if ((mBuilderFieldsSet & 0x100) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -778,10 +684,10 @@
}
@DataClass.Generated(
- time = 1615798784918L,
- codegenVersion = "1.0.22",
+ time = 1696889841006L,
+ codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java",
- inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\nprivate @android.annotation.Nullable android.widget.inline.InlinePresentationSpec mInlineTooltipPresentationSpec\nprivate boolean mServiceSupported\nprivate boolean mClientSupported\nprivate static final @android.compat.annotation.ChangeId @android.compat.annotation.EnabledSince long IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY\npublic void setHostInputToken(android.os.IBinder)\nprivate boolean extrasEquals(android.os.Bundle)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\npublic void filterContentTypes()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.widget.inline.InlinePresentationSpec defaultInlineTooltipPresentationSpec()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nprivate static boolean defaultServiceSupported()\nprivate static boolean defaultClientSupported()\npublic boolean isServiceSupported()\npublic boolean isClientSupported()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
+ inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\nprivate @android.annotation.Nullable android.widget.inline.InlinePresentationSpec mInlineTooltipPresentationSpec\nprivate static final @android.compat.annotation.ChangeId @android.compat.annotation.EnabledSince long IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY\npublic void setHostInputToken(android.os.IBinder)\nprivate boolean extrasEquals(android.os.Bundle)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\npublic void filterContentTypes()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.widget.inline.InlinePresentationSpec defaultInlineTooltipPresentationSpec()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/window/IRemoteTransition.aidl b/core/java/android/window/IRemoteTransition.aidl
index 2efb68a..ec8b66d 100644
--- a/core/java/android/window/IRemoteTransition.aidl
+++ b/core/java/android/window/IRemoteTransition.aidl
@@ -59,4 +59,12 @@
void mergeAnimation(in IBinder transition, in TransitionInfo info,
in SurfaceControl.Transaction t, in IBinder mergeTarget,
in IRemoteTransitionFinishedCallback finishCallback);
+
+ /**
+ * Called when a different handler has consumed the transition
+ *
+ * @param transition An identifier for the transition that was consumed.
+ * @param aborted Whether the transition is aborted or not.
+ */
+ void onTransitionConsumed(in IBinder transition, in boolean aborted);
}
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 0ee07bb..3d4bc2f 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -16,9 +16,6 @@
package android.window;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
@@ -37,8 +34,8 @@
import androidx.annotation.VisibleForTesting;
+
import java.io.PrintWriter;
-import java.lang.annotation.Retention;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
@@ -60,18 +57,6 @@
* @hide
*/
public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
- @Retention(SOURCE)
- @IntDef({
- BACK_CALLBACK_ENABLED,
- BACK_CALLBACK_DISABLED,
- BACK_CALLBACK_DISABLED_LEGACY_WINDOW_SWIPE_TO_DISMISS
- })
- public @interface OnBackInvokedCallbackType {}
-
- public static final int BACK_CALLBACK_ENABLED = 0;
- public static final int BACK_CALLBACK_DISABLED = 1;
- public static final int BACK_CALLBACK_DISABLED_LEGACY_WINDOW_SWIPE_TO_DISMISS = 2;
-
private IWindowSession mWindowSession;
private IWindow mWindow;
private static final String TAG = "WindowOnBackDispatcher";
@@ -283,6 +268,13 @@
}
/**
+ * Returns false if the legacy back behavior should be used.
+ */
+ public boolean isOnBackInvokedCallbackEnabled() {
+ return Checker.isOnBackInvokedCallbackEnabled(mChecker.getContext());
+ }
+
+ /**
* Dump information about this WindowOnBackInvokedDispatcher
* @param prefix the prefix that will be prepended to each line of the produced output
* @param writer the writer that will receive the resulting text
@@ -395,20 +387,6 @@
}
}
- /** Returns false if the legacy back behavior should be used. */
- public boolean isOnBackInvokedCallbackEnabled() {
- return isOnBackInvokedCallbackEnabled(mChecker.getContext());
- }
-
- /**
- * Returns true if system gesture exclusion is needed for global gesture compatibility with
- * windowSwipeToDismiss styleable.
- */
- public boolean isSystemGestureExclusionNeeded() {
- return Checker.getBackCallbackType(mChecker.getContext())
- == BACK_CALLBACK_DISABLED_LEGACY_WINDOW_SWIPE_TO_DISMISS;
- }
-
/**
* Returns false if the legacy back behavior should be used.
* <p>
@@ -416,7 +394,7 @@
* {@link OnBackInvokedCallback}.
*/
public static boolean isOnBackInvokedCallbackEnabled(@NonNull Context context) {
- return Checker.getBackCallbackType(context) == BACK_CALLBACK_ENABLED;
+ return Checker.isOnBackInvokedCallbackEnabled(context);
}
@Override
@@ -468,29 +446,28 @@
return mContext.get();
}
- @OnBackInvokedCallbackType
- private static int getBackCallbackType(@Nullable Context context) {
+ private static boolean isOnBackInvokedCallbackEnabled(@Nullable Context context) {
// new back is enabled if the feature flag is enabled AND the app does not explicitly
// request legacy back.
boolean featureFlagEnabled = ENABLE_PREDICTIVE_BACK;
if (!featureFlagEnabled) {
- return BACK_CALLBACK_DISABLED;
+ return false;
}
if (ALWAYS_ENFORCE_PREDICTIVE_BACK) {
- Log.i(TAG, "getBackCallbackType: always enable");
- return BACK_CALLBACK_ENABLED;
+ return true;
}
// If the context is null, return false to use legacy back.
if (context == null) {
Log.w(TAG, "OnBackInvokedCallback is not enabled because context is null.");
- return BACK_CALLBACK_DISABLED;
+ return false;
}
boolean requestsPredictiveBack = false;
// Check if the context is from an activity.
+ Context originalContext = context;
while ((context instanceof ContextWrapper) && !(context instanceof Activity)) {
context = ((ContextWrapper) context).getBaseContext();
}
@@ -539,8 +516,10 @@
// 3. windowSwipeToDismiss=false should be respected for apps not opted in,
// which disables PB & onBackPressed caused by BackAnimController's
// setTrigger(true)
+ // Use the original context to resolve the styled attribute so that they stay
+ // true to the window.
TypedArray windowAttr =
- context.obtainStyledAttributes(
+ originalContext.obtainStyledAttributes(
new int[] {android.R.attr.windowSwipeToDismiss});
boolean windowSwipeToDismiss = true;
if (windowAttr.getIndexCount() > 0) {
@@ -552,15 +531,11 @@
Log.i(TAG, "falling back to windowSwipeToDismiss: " + windowSwipeToDismiss);
}
- if (!windowSwipeToDismiss) {
- return BACK_CALLBACK_DISABLED_LEGACY_WINDOW_SWIPE_TO_DISMISS;
- } else {
- return BACK_CALLBACK_ENABLED;
- }
+ requestsPredictiveBack = windowSwipeToDismiss;
}
}
- return requestsPredictiveBack ? BACK_CALLBACK_ENABLED : BACK_CALLBACK_DISABLED;
+ return requestsPredictiveBack;
}
}
}
diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig
index 1b98806..ccbf4a9 100644
--- a/core/java/android/window/flags/window_surfaces.aconfig
+++ b/core/java/android/window/flags/window_surfaces.aconfig
@@ -9,3 +9,11 @@
is_fixed_read_only: true
bug: "292032926"
}
+
+flag {
+ namespace: "window_surfaces"
+ name: "explicit_refresh_rate_hints"
+ description: "Performance related hints during transitions"
+ is_fixed_read_only: true
+ bug: "300019131"
+}
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java
index 5dd558a..987c14c 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java
@@ -28,15 +28,19 @@
import android.annotation.Nullable;
import android.app.Activity;
import android.app.AlertDialog;
+import android.app.KeyguardManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
+import android.view.WindowManager;
+import android.view.accessibility.Flags;
import android.widget.AdapterView;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.List;
@@ -207,6 +211,11 @@
isEditMenuMode ? this::onTargetChecked : this::onTargetSelected);
}
+ @VisibleForTesting
+ public AlertDialog getMenuDialog() {
+ return mMenuDialog;
+ }
+
private AlertDialog createMenuDialog() {
final String dialogTitle =
getString(R.string.accessibility_select_shortcut_menu_title);
@@ -216,12 +225,25 @@
.setAdapter(mTargetAdapter, /* listener= */ null)
.setOnDismissListener(dialog -> finish());
- if (isUserSetupCompleted(this)) {
+ boolean allowEditing = isUserSetupCompleted(this);
+ boolean showWhenLocked = false;
+ if (Flags.allowShortcutChooserOnLockscreen()) {
+ final KeyguardManager keyguardManager = getSystemService(KeyguardManager.class);
+ if (keyguardManager != null && keyguardManager.isKeyguardLocked()) {
+ allowEditing = false;
+ showWhenLocked = true;
+ }
+ }
+ if (allowEditing) {
final String positiveButtonText =
getString(R.string.edit_accessibility_shortcut_menu_button);
builder.setPositiveButton(positiveButtonText, /* listener= */ null);
}
- return builder.create();
+ final AlertDialog dialog = builder.create();
+ if (showWhenLocked) {
+ dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+ }
+ return dialog;
}
}
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 2909b6a..e494346 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -414,11 +414,6 @@
"dark_launch_remote_prediction_service_enabled";
/**
- * (boolean) Whether to enable pinch resizing for PIP.
- */
- public static final String PIP_PINCH_RESIZE = "pip_pinch_resize";
-
- /**
* (boolean) Whether to enable stashing for PIP.
*/
public static final String PIP_STASHING = "pip_stashing";
diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
index cb2d934..b1d22e0 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
@@ -86,6 +86,28 @@
public static final Flag ENABLE_ATTENTION_HELPER_REFACTOR = devFlag(
"persist.debug.sysui.notification.enable_attention_helper_refactor");
+ // TODO b/291899544: for released flags, use resource config values
+ /** Value used by polite notif. feature */
+ public static final Flag NOTIF_COOLDOWN_T1 = devFlag(
+ "persist.debug.sysui.notification.notif_cooldown_t1", 5000);
+ /** Value used by polite notif. feature */
+ public static final Flag NOTIF_COOLDOWN_T2 = devFlag(
+ "persist.debug.sysui.notification.notif_cooldown_t2", 3000);
+ /** Value used by polite notif. feature */
+ public static final Flag NOTIF_VOLUME1 = devFlag(
+ "persist.debug.sysui.notification.notif_volume1", 30);
+ public static final Flag NOTIF_VOLUME2 = devFlag(
+ "persist.debug.sysui.notification.notif_volume2", 0);
+ /** Value used by polite notif. feature. -1 to ignore the counter */
+ public static final Flag NOTIF_COOLDOWN_COUNTER_RESET = devFlag(
+ "persist.debug.sysui.notification.notif_cooldown_counter_reset", 10);
+ /**
+ * Value used by polite notif. feature: cooldown behavior/strategy. Valid values: rule1,
+ * rule2
+ */
+ public static final Flag NOTIF_COOLDOWN_RULE = devFlag(
+ "persist.debug.sysui.notification.notif_cooldown_rule", "rule1");
+
/** b/301242692: Visit extra URIs used in notifications to prevent security issues. */
public static final Flag VISIT_RISKY_URIS = devFlag(
"persist.sysui.notification.visit_risky_uris");
@@ -97,6 +119,10 @@
public interface FlagResolver {
/** Is the flag enabled? */
boolean isEnabled(Flag flag);
+ /** Get the flag value (integer) */
+ int getIntValue(Flag flag);
+ /** Get the flag value (string) */
+ String getStringValue(Flag flag);
}
/** The primary, immutable resolver returned by getResolver() */
@@ -134,6 +160,22 @@
}
/**
+ * Creates a flag that with a default integer value in debuggable builds.
+ */
+ @VisibleForTesting
+ public static Flag devFlag(String name, int defaultValue) {
+ return new Flag(name, defaultValue, null);
+ }
+
+ /**
+ * Creates a flag that with a default string value in debuggable builds.
+ */
+ @VisibleForTesting
+ public static Flag devFlag(String name, String defaultValue) {
+ return new Flag(name, defaultValue, null);
+ }
+
+ /**
* Creates a flag that is disabled by default in debuggable builds.
* It can be enabled or force-disabled by setting this flag's SystemProperty to 1 or 0.
* If this flag's SystemProperty is not set, the flag can be enabled by setting the
@@ -161,6 +203,8 @@
public static final class Flag {
public final String mSysPropKey;
public final boolean mDefaultValue;
+ public final int mDefaultIntValue;
+ public final String mDefaultStringValue;
@Nullable
public final Flag mDebugDefault;
@@ -170,6 +214,24 @@
mSysPropKey = sysPropKey;
mDefaultValue = defaultValue;
mDebugDefault = debugDefault;
+ mDefaultIntValue = 0;
+ mDefaultStringValue = null;
+ }
+
+ public Flag(@NonNull String sysPropKey, int defaultValue, @Nullable Flag debugDefault) {
+ mSysPropKey = sysPropKey;
+ mDefaultIntValue = defaultValue;
+ mDebugDefault = debugDefault;
+ mDefaultValue = false;
+ mDefaultStringValue = null;
+ }
+
+ public Flag(@NonNull String sysPropKey, String defaultValue, @Nullable Flag debugDefault) {
+ mSysPropKey = sysPropKey;
+ mDefaultStringValue = defaultValue;
+ mDebugDefault = debugDefault;
+ mDefaultValue = false;
+ mDefaultIntValue = 0;
}
}
@@ -181,6 +243,16 @@
public boolean isEnabled(Flag flag) {
return flag.mDefaultValue;
}
+
+ @Override
+ public int getIntValue(Flag flag) {
+ return flag.mDefaultIntValue;
+ }
+
+ @Override
+ public String getStringValue(Flag flag) {
+ return flag.mDefaultStringValue;
+ }
}
/** Implementation of the interface used in debuggable builds. */
@@ -199,5 +271,23 @@
public boolean getBoolean(String key, boolean defaultValue) {
return SystemProperties.getBoolean(key, defaultValue);
}
+
+ /** Look up the value; overridable for tests to avoid needing to set SystemProperties */
+ @VisibleForTesting
+ public int getIntValue(Flag flag) {
+ if (flag.mDebugDefault == null) {
+ return SystemProperties.getInt(flag.mSysPropKey, flag.mDefaultIntValue);
+ }
+ return SystemProperties.getInt(flag.mSysPropKey, getIntValue(flag.mDebugDefault));
+ }
+
+ /** Look up the value; overridable for tests to avoid needing to set SystemProperties */
+ @VisibleForTesting
+ public String getStringValue(Flag flag) {
+ if (flag.mDebugDefault == null) {
+ return SystemProperties.get(flag.mSysPropKey, flag.mDefaultStringValue);
+ }
+ return SystemProperties.get(flag.mSysPropKey, getStringValue(flag.mDebugDefault));
+ }
}
}
diff --git a/core/java/com/android/internal/foldables/Android.bp b/core/java/com/android/internal/foldables/Android.bp
new file mode 100644
index 0000000..f1d06da
--- /dev/null
+++ b/core/java/com/android/internal/foldables/Android.bp
@@ -0,0 +1,7 @@
+aconfig_declarations {
+ name: "fold_lock_setting_flags",
+ package: "com.android.internal.foldables.flags",
+ srcs: [
+ "fold_lock_setting_flags.aconfig",
+ ],
+}
diff --git a/core/java/com/android/internal/foldables/FoldLockSettingAvailabilityProvider.java b/core/java/com/android/internal/foldables/FoldLockSettingAvailabilityProvider.java
index 4e3888a..a115ecf 100644
--- a/core/java/com/android/internal/foldables/FoldLockSettingAvailabilityProvider.java
+++ b/core/java/com/android/internal/foldables/FoldLockSettingAvailabilityProvider.java
@@ -17,16 +17,23 @@
package com.android.internal.foldables;
import android.content.res.Resources;
+import android.os.Build;
import android.sysprop.FoldLockBehaviorProperties;
+import android.util.Slog;
import com.android.internal.R;
+import com.android.internal.foldables.flags.Flags;
+
+import java.util.function.Supplier;
/**
* Wrapper class to access {@link FoldLockBehaviorProperties} and also assists with testing
*/
public class FoldLockSettingAvailabilityProvider {
- boolean mFoldLockBehaviorResourceValue;
+ private static final String TAG = "FoldLockSettingAvailabilityProvider";
+ private final boolean mFoldLockBehaviorResourceValue;
+ private final Supplier<Boolean> mFoldLockSettingEnabled = Flags::foldLockSettingEnabled;
public FoldLockSettingAvailabilityProvider(Resources resources) {
mFoldLockBehaviorResourceValue = resources.getBoolean(
@@ -35,6 +42,22 @@
public boolean isFoldLockBehaviorAvailable() {
return mFoldLockBehaviorResourceValue
- && FoldLockBehaviorProperties.fold_lock_setting_enabled().orElse(false);
+ && flagOrSystemProperty();
+ }
+
+ private boolean flagOrSystemProperty() {
+ if ((Build.IS_ENG || Build.IS_USERDEBUG)
+ && FoldLockBehaviorProperties.fold_lock_setting_enabled().orElse(false)) {
+ return true;
+ }
+ try {
+ return mFoldLockSettingEnabled.get();
+ } catch (Throwable ex) {
+ Slog.i(TAG,
+ "Flags not ready yet. Return false for "
+ + Flags.FLAG_FOLD_LOCK_SETTING_ENABLED,
+ ex);
+ return false;
+ }
}
}
diff --git a/core/java/com/android/internal/foldables/fold_lock_setting_flags.aconfig b/core/java/com/android/internal/foldables/fold_lock_setting_flags.aconfig
new file mode 100644
index 0000000..44f436ea
--- /dev/null
+++ b/core/java/com/android/internal/foldables/fold_lock_setting_flags.aconfig
@@ -0,0 +1,9 @@
+package: "com.android.internal.foldables.flags"
+
+flag {
+ name: "fold_lock_setting_enabled"
+ namespace: "display_manager"
+ description: "Feature flag for Fold Lock Setting"
+ bug: "274447767"
+ is_fixed_read_only: true
+}
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index f79dbe7..ba644eb 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -127,6 +127,7 @@
jfieldID xDpi;
jfieldID yDpi;
jfieldID refreshRate;
+ jfieldID vsyncRate;
jfieldID appVsyncOffsetNanos;
jfieldID presentationDeadlineNanos;
jfieldID group;
@@ -1231,6 +1232,7 @@
env->SetFloatField(object, gDisplayModeClassInfo.yDpi, config.yDpi);
env->SetFloatField(object, gDisplayModeClassInfo.refreshRate, config.refreshRate);
+ env->SetFloatField(object, gDisplayModeClassInfo.vsyncRate, config.vsyncRate);
env->SetLongField(object, gDisplayModeClassInfo.appVsyncOffsetNanos, config.appVsyncOffset);
env->SetLongField(object, gDisplayModeClassInfo.presentationDeadlineNanos,
config.presentationDeadline);
@@ -2394,6 +2396,7 @@
gDisplayModeClassInfo.xDpi = GetFieldIDOrDie(env, modeClazz, "xDpi", "F");
gDisplayModeClassInfo.yDpi = GetFieldIDOrDie(env, modeClazz, "yDpi", "F");
gDisplayModeClassInfo.refreshRate = GetFieldIDOrDie(env, modeClazz, "refreshRate", "F");
+ gDisplayModeClassInfo.vsyncRate = GetFieldIDOrDie(env, modeClazz, "vsyncRate", "F");
gDisplayModeClassInfo.appVsyncOffsetNanos =
GetFieldIDOrDie(env, modeClazz, "appVsyncOffsetNanos", "J");
gDisplayModeClassInfo.presentationDeadlineNanos =
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a0b8cca..ca768ad 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2108,12 +2108,12 @@
android:protectionLevel="signature" />
<!-- Allows direct access to the <RemoteAuth>Service interfaces.
- @hide @TestApi @SystemApi(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES) -->
+ @hide -->
<permission android:name="android.permission.MANAGE_REMOTE_AUTH"
android:protectionLevel="signature" />
<!-- Allows direct access to the <RemoteAuth>Service authentication methods.
- @hide @TestApi @SystemApi(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES) -->
+ @hide -->
<permission android:name="android.permission.USE_REMOTE_AUTH"
android:protectionLevel="signature" />
@@ -7233,9 +7233,11 @@
android:description="@string/permdesc_fullScreenIntent"
android:protectionLevel="normal|appop" />
- <!-- Required for the assistant apps targeting {@link android.os.Build.VERSION_CODES#V}
+ <!-- @SystemApi Required for the privileged assistant apps targeting
+ {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}
that receive voice trigger from the trusted hotword detection service.
<p>Protection level: signature|privileged|appop
+ @FlaggedApi("android.permission.flags.voice_activation_permission_apis")
@hide -->
<permission android:name="android.permission.RECEIVE_SANDBOX_TRIGGER_AUDIO"
android:protectionLevel="signature|privileged|appop" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 7ef81ab..b211ac2 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1026,6 +1026,12 @@
<!-- Duration, in milliseconds, of the display white balance animated transitions. -->
<integer name="config_displayWhiteBalanceTransitionTime">3000</integer>
+ <!-- Duration, in milliseconds, of the display white balance animated transitions when increasing cct. -->
+ <integer name="config_displayWhiteBalanceTransitionTimeIncrease">1000</integer>
+
+ <!-- Duration, in milliseconds, of the display white balance animated transitions when decreasing cct. -->
+ <integer name="config_displayWhiteBalanceTransitionTimeDecrease">40000</integer>
+
<!-- Device states where the sensor based rotation values should be reversed around the Z axis
for the default display.
TODO(b/265312193): Remove this workaround when this bug is fixed.-->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 3b00682b..c1ecb05 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3487,6 +3487,8 @@
<java-symbol type="array" name="config_displayWhiteBalanceDisplaySteps" />
<java-symbol type="bool" name="config_displayWhiteBalanceLightModeAllowed" />
<java-symbol type="integer" name="config_displayWhiteBalanceTransitionTime" />
+ <java-symbol type="integer" name="config_displayWhiteBalanceTransitionTimeIncrease" />
+ <java-symbol type="integer" name="config_displayWhiteBalanceTransitionTimeDecrease" />
<!-- Device states where the sensor based rotation values should be reversed around the Z axis
for the default display.
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
index 824f591..75a7231 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
@@ -610,8 +610,6 @@
@Test
public void cancel() throws Exception {
openAidlClients(/* numClients= */ 1);
- ProgramSelector initialSel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
- mTunerSessions[0].tune(initialSel);
mTunerSessions[0].cancel();
@@ -621,9 +619,6 @@
@Test
public void cancel_forNonCurrentUser_doesNotCancel() throws Exception {
openAidlClients(/* numClients= */ 1);
- ProgramSelector initialSel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
- mTunerSessions[0].tune(initialSel);
- verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(any());
doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
mTunerSessions[0].cancel();
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
index 3ec44d1..6edfa02 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
@@ -540,8 +540,6 @@
@Test
public void cancel() throws Exception {
openAidlClients(/* numClients= */ 1);
- ProgramSelector initialSel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
- mTunerSessions[0].tune(initialSel);
mTunerSessions[0].cancel();
@@ -551,9 +549,6 @@
@Test
public void cancel_forNonCurrentUser_doesNotCancel() throws Exception {
openAidlClients(/* numClients= */ 1);
- ProgramSelector initialSel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
- mTunerSessions[0].tune(initialSel);
- verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(any());
doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
mTunerSessions[0].cancel();
diff --git a/core/tests/coretests/src/android/content/ContentCaptureOptionsTest.java b/core/tests/coretests/src/android/content/ContentCaptureOptionsTest.java
index f8348d2..eefa6e4 100644
--- a/core/tests/coretests/src/android/content/ContentCaptureOptionsTest.java
+++ b/core/tests/coretests/src/android/content/ContentCaptureOptionsTest.java
@@ -32,6 +32,8 @@
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
+import java.util.List;
+
/**
* Unit test for {@link ContentCaptureOptions}.
*
@@ -44,6 +46,9 @@
private static final ComponentName CONTEXT_COMPONENT = new ComponentName("marco", "polo");
private static final ComponentName COMPONENT1 = new ComponentName("comp", "one");
private static final ComponentName COMPONENT2 = new ComponentName("two", "comp");
+ private static final List<List<String>> CONTENT_PROTECTION_REQUIRED_GROUPS =
+ List.of(List.of("first"), List.of("second", "third"), List.of());
+ private static final List<List<String>> CONTENT_PROTECTION_OPTIONAL_GROUPS = List.of();
private static final ContentCaptureOptions CONTENT_CAPTURE_OPTIONS =
new ContentCaptureOptions(
/* loggingLevel= */ 1000,
@@ -55,7 +60,10 @@
/* enableReceiver= */ false,
new ContentCaptureOptions.ContentProtectionOptions(
/* enableReceiver= */ true,
- /* bufferSize= */ 2001),
+ /* bufferSize= */ 2001,
+ CONTENT_PROTECTION_REQUIRED_GROUPS,
+ CONTENT_PROTECTION_OPTIONAL_GROUPS,
+ /* optionalGroupsThreshold= */ 2002),
/* whitelistedComponents= */ toSet(COMPONENT1, COMPONENT2));
@Mock private Context mContext;
@@ -134,6 +142,19 @@
.append(CONTENT_CAPTURE_OPTIONS.contentProtectionOptions.enableReceiver)
.append(", bufferSize=")
.append(CONTENT_CAPTURE_OPTIONS.contentProtectionOptions.bufferSize)
+ .append(", requiredGroupsSize=")
+ .append(
+ CONTENT_CAPTURE_OPTIONS.contentProtectionOptions.requiredGroups
+ .size())
+ .append(", optionalGroupsSize=")
+ .append(
+ CONTENT_CAPTURE_OPTIONS.contentProtectionOptions.optionalGroups
+ .size())
+ .append(", optionalGroupsThreshold=")
+ .append(
+ CONTENT_CAPTURE_OPTIONS
+ .contentProtectionOptions
+ .optionalGroupsThreshold)
.append("], whitelisted=")
.append(CONTENT_CAPTURE_OPTIONS.whitelistedComponents)
.append(']')
@@ -166,6 +187,15 @@
.isEqualTo(CONTENT_CAPTURE_OPTIONS.contentProtectionOptions.enableReceiver);
assertThat(actual.contentProtectionOptions.bufferSize)
.isEqualTo(CONTENT_CAPTURE_OPTIONS.contentProtectionOptions.bufferSize);
+ assertThat(actual.contentProtectionOptions.requiredGroups)
+ .containsExactlyElementsIn(
+ CONTENT_CAPTURE_OPTIONS.contentProtectionOptions.requiredGroups);
+ assertThat(actual.contentProtectionOptions.optionalGroups)
+ .containsExactlyElementsIn(
+ CONTENT_CAPTURE_OPTIONS.contentProtectionOptions.optionalGroups);
+ assertThat(actual.contentProtectionOptions.optionalGroupsThreshold)
+ .isEqualTo(
+ CONTENT_CAPTURE_OPTIONS.contentProtectionOptions.optionalGroupsThreshold);
assertThat(actual.whitelistedComponents)
.containsExactlyElementsIn(CONTENT_CAPTURE_OPTIONS.whitelistedComponents);
}
diff --git a/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java b/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
index bfdb15b..517aeae 100644
--- a/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
+++ b/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
@@ -48,6 +48,8 @@
import androidx.test.filters.SmallTest;
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
+import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.Flag;
+import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.FlagResolver;
import org.junit.After;
import org.junit.Assert;
@@ -422,11 +424,24 @@
mNotificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "test channel",
NotificationManager.IMPORTANCE_DEFAULT);
- SystemUiSystemPropertiesFlags.TEST_RESOLVER = flag -> {
- if (flag.mSysPropKey.equals(RANKING_UPDATE_ASHMEM.mSysPropKey)) {
- return mRankingUpdateAshmem;
+ SystemUiSystemPropertiesFlags.TEST_RESOLVER = new FlagResolver() {
+ @Override
+ public boolean isEnabled(Flag flag) {
+ if (flag.mSysPropKey.equals(RANKING_UPDATE_ASHMEM.mSysPropKey)) {
+ return mRankingUpdateAshmem;
+ }
+ return new SystemUiSystemPropertiesFlags.DebugResolver().isEnabled(flag);
}
- return new SystemUiSystemPropertiesFlags.DebugResolver().isEnabled(flag);
+
+ @Override
+ public int getIntValue(Flag flag) {
+ return 0;
+ }
+
+ @Override
+ public String getStringValue(Flag flag) {
+ return null;
+ }
};
}
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java
index 101f7c21..5c411d5 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java
@@ -31,6 +31,8 @@
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
+import java.util.Collections;
+
/**
* Unit test for {@link ContentCaptureManager}.
*
@@ -69,7 +71,11 @@
ContentCaptureOptions options =
createOptions(
new ContentCaptureOptions.ContentProtectionOptions(
- /* enableReceiver= */ false, BUFFER_SIZE));
+ /* enableReceiver= */ false,
+ BUFFER_SIZE,
+ /* requiredGroups= */ Collections.emptyList(),
+ /* optionalGroups= */ Collections.emptyList(),
+ /* optionalGroupsThreshold= */ 0));
ContentCaptureManager manager =
new ContentCaptureManager(mMockContext, mMockContentCaptureManager, options);
@@ -82,7 +88,11 @@
ContentCaptureOptions options =
createOptions(
new ContentCaptureOptions.ContentProtectionOptions(
- /* enableReceiver= */ true, /* bufferSize= */ 0));
+ /* enableReceiver= */ true,
+ /* bufferSize= */ 0,
+ /* requiredGroups= */ Collections.emptyList(),
+ /* optionalGroups= */ Collections.emptyList(),
+ /* optionalGroupsThreshold= */ 0));
ContentCaptureManager manager =
new ContentCaptureManager(mMockContext, mMockContentCaptureManager, options);
@@ -95,7 +105,11 @@
ContentCaptureOptions options =
createOptions(
new ContentCaptureOptions.ContentProtectionOptions(
- /* enableReceiver= */ true, BUFFER_SIZE));
+ /* enableReceiver= */ true,
+ BUFFER_SIZE,
+ /* requiredGroups= */ Collections.emptyList(),
+ /* optionalGroups= */ Collections.emptyList(),
+ /* optionalGroupsThreshold= */ 0));
ContentCaptureManager manager =
new ContentCaptureManager(mMockContext, mMockContentCaptureManager, options);
diff --git a/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
index 3373b8b..e76d266 100644
--- a/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
@@ -47,6 +47,7 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
/**
@@ -112,7 +113,11 @@
createOptions(
/* enableContentCaptureReceiver= */ true,
new ContentCaptureOptions.ContentProtectionOptions(
- /* enableReceiver= */ true, -BUFFER_SIZE));
+ /* enableReceiver= */ true,
+ -BUFFER_SIZE,
+ /* requiredGroups= */ Collections.emptyList(),
+ /* optionalGroups= */ Collections.emptyList(),
+ /* optionalGroupsThreshold= */ 0));
MainContentCaptureSession session = createSession(options);
session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
@@ -313,7 +318,11 @@
return createOptions(
enableContentCaptureReceiver,
new ContentCaptureOptions.ContentProtectionOptions(
- enableContentProtectionReceiver, BUFFER_SIZE));
+ enableContentProtectionReceiver,
+ BUFFER_SIZE,
+ /* requiredGroups= */ Collections.emptyList(),
+ /* optionalGroups= */ Collections.emptyList(),
+ /* optionalGroupsThreshold= */ 0));
}
private ContentCaptureManager createManager(ContentCaptureOptions options) {
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutChooserActivityTest.java
index 681ba9c..06b62f8 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutChooserActivityTest.java
@@ -29,6 +29,8 @@
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.endsWith;
import static org.mockito.ArgumentMatchers.any;
@@ -39,6 +41,8 @@
import static org.mockito.Mockito.when;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.app.AlertDialog;
+import android.app.KeyguardManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -47,11 +51,17 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.Handler;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject2;
import android.support.test.uiautomator.Until;
+import android.view.View;
+import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.Flags;
import android.view.accessibility.IAccessibilityManager;
import androidx.lifecycle.Lifecycle;
@@ -90,6 +100,9 @@
private TestAccessibilityShortcutChooserActivity mActivity;
@Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ @Rule
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@Mock
private AccessibilityServiceInfo mAccessibilityServiceInfo;
@@ -101,6 +114,8 @@
private ApplicationInfo mApplicationInfo;
@Mock
private IAccessibilityManager mAccessibilityManagerService;
+ @Mock
+ private KeyguardManager mKeyguardManager;
@Before
public void setUp() throws Exception {
@@ -116,22 +131,19 @@
Collections.singletonList(mAccessibilityServiceInfo)));
when(mAccessibilityManagerService.isAccessibilityTargetAllowed(
anyString(), anyInt(), anyInt())).thenReturn(true);
- TestAccessibilityShortcutChooserActivity.setupForTesting(mAccessibilityManagerService);
- mScenario = ActivityScenario.launch(TestAccessibilityShortcutChooserActivity.class);
- mScenario.onActivity(activity -> mActivity = activity);
- mScenario.moveToState(Lifecycle.State.CREATED);
- mScenario.moveToState(Lifecycle.State.STARTED);
- mScenario.moveToState(Lifecycle.State.RESUMED);
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ when(mKeyguardManager.isKeyguardLocked()).thenReturn(false);
+ TestAccessibilityShortcutChooserActivity.setupForTesting(
+ mAccessibilityManagerService, mKeyguardManager);
}
@After
public void cleanUp() {
- mScenario.moveToState(Lifecycle.State.DESTROYED);
+ mScenario.close();
}
@Test
public void doubleClickTestServiceAndClickDenyButton_permissionDialogDoesNotExist() {
+ launchActivity();
openShortcutsList();
// Performing the double-click is flaky so retry if needed.
@@ -154,6 +166,7 @@
throws Exception {
when(mAccessibilityManagerService.isAccessibilityTargetAllowed(
eq(TEST_COMPONENT_NAME.getPackageName()), anyInt(), anyInt())).thenReturn(false);
+ launchActivity();
openShortcutsList();
onView(withText(TEST_LABEL)).perform(scrollTo(), click());
@@ -165,6 +178,7 @@
@Test
public void popEditShortcutMenuList_oneHandedModeEnabled_shouldBeInListView() {
TestUtils.setOneHandedModeEnabled(this, /* enabled= */ true);
+ launchActivity();
openShortcutsList();
onView(allOf(withClassName(endsWith("ListView")), isDisplayed())).perform(swipeUp());
@@ -176,6 +190,7 @@
@Test
public void popEditShortcutMenuList_oneHandedModeDisabled_shouldNotBeInListView() {
TestUtils.setOneHandedModeEnabled(this, /* enabled= */ false);
+ launchActivity();
openShortcutsList();
onView(allOf(withClassName(endsWith("ListView")), isDisplayed())).perform(swipeUp());
@@ -184,6 +199,30 @@
onView(withText(ONE_HANDED_MODE)).inRoot(isDialog()).check(doesNotExist());
}
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ALLOW_SHORTCUT_CHOOSER_ON_LOCKSCREEN)
+ public void createDialog_onLockscreen_hasExpectedContent() {
+ when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
+ launchActivity();
+
+ final AlertDialog dialog = mActivity.getMenuDialog();
+
+ assertThat(dialog.getButton(AlertDialog.BUTTON_POSITIVE).getVisibility())
+ .isEqualTo(View.GONE);
+ assertThat(dialog.getWindow().getAttributes().flags
+ & WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED)
+ .isEqualTo(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+ }
+
+ private void launchActivity() {
+ mScenario = ActivityScenario.launch(TestAccessibilityShortcutChooserActivity.class);
+ mScenario.onActivity(activity -> mActivity = activity);
+ mScenario.moveToState(Lifecycle.State.CREATED);
+ mScenario.moveToState(Lifecycle.State.STARTED);
+ mScenario.moveToState(Lifecycle.State.RESUMED);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+
private void openShortcutsList() {
UiObject2 editButton = mDevice.findObject(By.text(EDIT_LABEL));
if (editButton != null) {
@@ -198,9 +237,13 @@
public static class TestAccessibilityShortcutChooserActivity extends
AccessibilityShortcutChooserActivity {
private static IAccessibilityManager sAccessibilityManagerService;
+ private static KeyguardManager sKeyguardManager;
- public static void setupForTesting(IAccessibilityManager accessibilityManagerService) {
+ public static void setupForTesting(
+ IAccessibilityManager accessibilityManagerService,
+ KeyguardManager keyguardManager) {
sAccessibilityManagerService = accessibilityManagerService;
+ sKeyguardManager = keyguardManager;
}
@Override
@@ -210,6 +253,9 @@
return new AccessibilityManager(this, new Handler(getMainLooper()),
sAccessibilityManagerService, /* userId= */ 0, /* serviceConnect= */ true);
}
+ if (Context.KEYGUARD_SERVICE.equals(name)) {
+ return sKeyguardManager;
+ }
return super.getSystemService(name);
}
diff --git a/framework-minus-apex-ravenwood-policies.txt b/framework-minus-apex-ravenwood-policies.txt
new file mode 100644
index 0000000..6bac58b
--- /dev/null
+++ b/framework-minus-apex-ravenwood-policies.txt
@@ -0,0 +1 @@
+# Ravenwood "policy" file for framework-minus-apex.
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 9d32272..db1cc44 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -16,8 +16,11 @@
package android.graphics;
+import static com.android.text.flags.Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE;
+
import android.annotation.ColorInt;
import android.annotation.ColorLong;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -2125,7 +2128,7 @@
* @return the font's recommended interline spacing.
*/
public float getFontMetrics(FontMetrics metrics) {
- return nGetFontMetrics(mNativePaint, metrics);
+ return nGetFontMetrics(mNativePaint, metrics, false);
}
/**
@@ -2139,6 +2142,32 @@
}
/**
+ * Get the font metrics used for the locale
+ *
+ * Obtain the metrics of the font that is used for the specified locale by
+ * {@link #setTextLocales(LocaleList)}. If multiple locales are specified, the minimum ascent
+ * and maximum descent will be set.
+ *
+ * This API is useful for determining the default line height of the empty text field, e.g.
+ * {@link android.widget.EditText}.
+ *
+ * Note, if the {@link android.graphics.Typeface} is created from the custom font files, its
+ * metrics are reserved, i.e. the ascent will be the custom font's ascent or smaller, the
+ * descent will be the custom font's descent or larger.
+ *
+ * Note, if the {@link android.graphics.Typeface} is a system fallback, e.g.
+ * {@link android.graphics.Typeface#SERIF}, the default font's metrics are reserved, i.e. the
+ * ascent will be the serif font's ascent or smaller, the descent will be the serif font's
+ * descent or larger.
+ *
+ * @param metrics an output parameter. All members will be set by calling this function.
+ */
+ @FlaggedApi(FLAG_FIX_LINE_HEIGHT_FOR_LOCALE)
+ public void getFontMetricsForLocale(@NonNull FontMetrics metrics) {
+ nGetFontMetrics(mNativePaint, metrics, true);
+ }
+
+ /**
* Returns the font metrics value for the given text.
*
* If the text is rendered with multiple font files, this function returns the large ascent and
@@ -2318,7 +2347,7 @@
* @return the font's interline spacing.
*/
public int getFontMetricsInt(FontMetricsInt fmi) {
- return nGetFontMetricsInt(mNativePaint, fmi);
+ return nGetFontMetricsInt(mNativePaint, fmi, false);
}
public FontMetricsInt getFontMetricsInt() {
@@ -2328,6 +2357,32 @@
}
/**
+ * Get the font metrics used for the locale
+ *
+ * Obtain the metrics of the font that is used for the specified locale by
+ * {@link #setTextLocales(LocaleList)}. If multiple locales are specified, the minimum ascent
+ * and maximum descent will be set.
+ *
+ * This API is useful for determining the default line height of the empty text field, e.g.
+ * {@link android.widget.EditText}.
+ *
+ * Note, if the {@link android.graphics.Typeface} is created from the custom font files, its
+ * metrics are reserved, i.e. the ascent will be the custom font's ascent or smaller, the
+ * descent will be the custom font's descent or larger.
+ *
+ * Note, if the {@link android.graphics.Typeface} is a system fallback, e.g.
+ * {@link android.graphics.Typeface#SERIF}, the default font's metrics are reserved, i.e. the
+ * ascent will be the serif font's ascent or smaller, the descent will be the serif font's
+ * descent or larger.
+ *
+ * @param metrics an output parameter. All members will be set by calling this function.
+ */
+ @FlaggedApi(FLAG_FIX_LINE_HEIGHT_FOR_LOCALE)
+ public void getFontMetricsIntForLocale(@NonNull FontMetricsInt metrics) {
+ nGetFontMetricsInt(mNativePaint, metrics, true);
+ }
+
+ /**
* Return the recommend line spacing based on the current typeface and
* text size.
*
@@ -3446,9 +3501,11 @@
@FastNative
private static native void nSetFontFeatureSettings(long paintPtr, String settings);
@FastNative
- private static native float nGetFontMetrics(long paintPtr, FontMetrics metrics);
+ private static native float nGetFontMetrics(long paintPtr, FontMetrics metrics,
+ boolean useLocale);
@FastNative
- private static native int nGetFontMetricsInt(long paintPtr, FontMetricsInt fmi);
+ private static native int nGetFontMetricsInt(long paintPtr, FontMetricsInt fmi,
+ boolean useLocale);
// ---------------- @CriticalNative ------------------------
diff --git a/keystore/aaid/aidl/Android.bp b/keystore/aaid/aidl/Android.bp
new file mode 100644
index 0000000..97acfb4
--- /dev/null
+++ b/keystore/aaid/aidl/Android.bp
@@ -0,0 +1,31 @@
+// Copyright 2020, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+aidl_interface {
+ name: "android.security.aaid_aidl",
+ srcs: ["android/security/keystore/*.aidl"],
+ unstable: true,
+ backend: {
+ rust: {
+ enabled: true,
+ },
+ cpp: {
+ enabled: true,
+ },
+ },
+}
diff --git a/keystore/aaid/aidl/android/security/keystore/IKeyAttestationApplicationIdProvider.aidl b/keystore/aaid/aidl/android/security/keystore/IKeyAttestationApplicationIdProvider.aidl
new file mode 100644
index 0000000..c360cb8
--- /dev/null
+++ b/keystore/aaid/aidl/android/security/keystore/IKeyAttestationApplicationIdProvider.aidl
@@ -0,0 +1,28 @@
+/**
+ * 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 android.security.keystore;
+
+import android.security.keystore.KeyAttestationApplicationId;
+
+/** @hide */
+interface IKeyAttestationApplicationIdProvider {
+ /**
+ * Provides information describing the possible applications identified by a UID.
+ * @hide
+ */
+ KeyAttestationApplicationId getKeyAttestationApplicationId(int uid);
+}
diff --git a/keystore/aaid/aidl/android/security/keystore/KeyAttestationApplicationId.aidl b/keystore/aaid/aidl/android/security/keystore/KeyAttestationApplicationId.aidl
new file mode 100644
index 0000000..c33e830
--- /dev/null
+++ b/keystore/aaid/aidl/android/security/keystore/KeyAttestationApplicationId.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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 android.security.keystore;
+
+import android.security.keystore.KeyAttestationPackageInfo;
+
+/**
+ * @hide
+ * The information aggregated by this parcelable is used by keystore to identify a caller of the
+ * keystore API toward a remote party. It aggregates multiple PackageInfos because keystore
+ * can only determine a caller by uid granularity, and a uid can be shared by multiple packages.
+ * The remote party must decide if it trusts all of the packages enough to consider the
+ * confidentiality of the key material in question intact.
+ */
+parcelable KeyAttestationApplicationId {
+ KeyAttestationPackageInfo[] packageInfos;
+}
diff --git a/keystore/aaid/aidl/android/security/keystore/KeyAttestationPackageInfo.aidl b/keystore/aaid/aidl/android/security/keystore/KeyAttestationPackageInfo.aidl
new file mode 100644
index 0000000..5f647d0
--- /dev/null
+++ b/keystore/aaid/aidl/android/security/keystore/KeyAttestationPackageInfo.aidl
@@ -0,0 +1,33 @@
+/*
+ * 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 android.security.keystore;
+
+import android.security.keystore.Signature;
+
+/**
+ * @hide
+ * This parcelable constitutes and excerpt from the PackageManager's PackageInfo for the purpose of
+ * key attestation. It is part of the KeyAttestationApplicationId, which is used by
+ * keystore to identify the caller of the keystore API towards a remote party.
+ */
+parcelable KeyAttestationPackageInfo {
+ String packageName;
+
+ long versionCode;
+
+ Signature[] signatures;
+}
diff --git a/keystore/aaid/aidl/android/security/keystore/Signature.aidl b/keystore/aaid/aidl/android/security/keystore/Signature.aidl
new file mode 100644
index 0000000..800499a
--- /dev/null
+++ b/keystore/aaid/aidl/android/security/keystore/Signature.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore;
+
+/**
+ * @hide
+ * Represents a signature data read from the package file. Extracted from from the PackageManager's
+ * PackageInfo for the purpose of key attestation. It is part of the KeyAttestationPackageInfo,
+ * which is used by keystore to identify the caller of the keystore API towards a remote party.
+ */
+parcelable Signature {
+ /**
+ * Represents signing certificate data associated with application package, signatures are
+ * expected to be a hex-encoded ASCII string representing valid X509 certificate.
+ */
+ byte[] data;
+}
diff --git a/packages/SystemUI/ktfmt_includes.txt b/ktfmt_includes.txt
similarity index 99%
rename from packages/SystemUI/ktfmt_includes.txt
rename to ktfmt_includes.txt
index d3254b7..e4bf4c2 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/ktfmt_includes.txt
@@ -1,3 +1,4 @@
++services/permission
+packages/SystemUI
-packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
-packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index d55a41f..7c0d0e3 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -5,4 +5,18 @@
namespace: "multitasking"
description: "An Example Flag"
bug: "300136750"
-}
\ No newline at end of file
+}
+
+flag {
+ name: "enable_app_pairs"
+ namespace: "multitasking"
+ description: "Enables the ability to create and save app pairs to the Home screen"
+ bug: "274835596"
+}
+
+flag {
+ name: "desktop_windowing"
+ namespace: "multitasking"
+ description: "Enables desktop windowing"
+ bug: "304778354"
+}
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index 97a9d48..7436400 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -58,6 +58,9 @@
if a custom action is present before closing it. -->
<integer name="config_pipForceCloseDelay">1000</integer>
+ <!-- Allow PIP to resize via pinch gesture. -->
+ <bool name="config_pipEnablePinchResize">true</bool>
+
<!-- Animation duration when using long press on recents to dock -->
<integer name="long_press_dock_anim_duration">250</integer>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java
index 34bf9e0..2e5448a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java
@@ -18,6 +18,7 @@
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE;
+import android.annotation.SuppressLint;
import android.app.WindowConfiguration;
import android.util.SparseArray;
import android.view.SurfaceControl;
@@ -29,6 +30,7 @@
import androidx.annotation.NonNull;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.sysui.ShellInit;
import java.io.PrintWriter;
import java.util.List;
@@ -44,9 +46,14 @@
/** Display area leashes, which is mapped by display IDs. */
private final SparseArray<SurfaceControl> mLeashes = new SparseArray<>();
- public RootDisplayAreaOrganizer(Executor executor) {
+ public RootDisplayAreaOrganizer(@NonNull Executor executor, @NonNull ShellInit shellInit) {
super(executor);
- List<DisplayAreaAppearedInfo> infos = registerOrganizer(FEATURE_ROOT);
+ shellInit.addInitCallback(this::onInit, this);
+ }
+
+ @SuppressLint("MissingPermission") // Only called by SysUI.
+ private void onInit() {
+ final List<DisplayAreaAppearedInfo> infos = registerOrganizer(FEATURE_ROOT);
for (int i = infos.size() - 1; i >= 0; --i) {
onDisplayAreaAppeared(infos.get(i).getDisplayAreaInfo(), infos.get(i).getLeash());
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
index 38550b4..ab61a48 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
@@ -18,6 +18,7 @@
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
+import android.annotation.SuppressLint;
import android.annotation.UiContext;
import android.app.ResourcesManager;
import android.content.Context;
@@ -38,6 +39,7 @@
import androidx.annotation.Nullable;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.sysui.ShellInit;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -69,10 +71,17 @@
private final Context mContext;
- public RootTaskDisplayAreaOrganizer(Executor executor, Context context) {
+ public RootTaskDisplayAreaOrganizer(@NonNull Executor executor, @NonNull Context context,
+ @NonNull ShellInit shellInit) {
super(executor);
mContext = context;
- List<DisplayAreaAppearedInfo> infos = registerOrganizer(FEATURE_DEFAULT_TASK_CONTAINER);
+ shellInit.addInitCallback(this::onInit, this);
+ }
+
+ @SuppressLint("MissingPermission") // Only called by SysUI.
+ private void onInit() {
+ final List<DisplayAreaAppearedInfo> infos =
+ registerOrganizer(FEATURE_DEFAULT_TASK_CONTAINER);
for (int i = infos.size() - 1; i >= 0; --i) {
onDisplayAreaAppeared(infos.get(i).getDisplayAreaInfo(), infos.get(i).getLeash());
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/common/OWNERS
index 7af0389..6519eee 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/OWNERS
@@ -1 +1,2 @@
madym@google.com
+hwwang@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
index 3b32b6c..d520ff7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
@@ -126,12 +126,22 @@
private @Nullable TriConsumer<Boolean, Integer, Boolean> mOnShelfVisibilityChangeCallback;
private List<Consumer<Rect>> mOnPipExclusionBoundsChangeCallbacks = new ArrayList<>();
+ // the size of the current bounds relative to the max size spec
+ private float mBoundsScale;
+
public PipBoundsState(@NonNull Context context, @NonNull SizeSpecSource sizeSpecSource,
@NonNull PipDisplayLayoutState pipDisplayLayoutState) {
mContext = context;
reloadResources();
mSizeSpecSource = sizeSpecSource;
mPipDisplayLayoutState = pipDisplayLayoutState;
+
+ // Update the relative proportion of the bounds compared to max possible size. Max size
+ // spec takes the aspect ratio of the bounds into account, so both width and height
+ // scale by the same factor.
+ addPipExclusionBoundsChangeCallback((bounds) -> {
+ mBoundsScale = Math.min((float) bounds.width() / mMaxSize.x, 1.0f);
+ });
}
/** Reloads the resources. */
@@ -160,6 +170,15 @@
return new Rect(mBounds);
}
+ /**
+ * Get the scale of the current bounds relative to the maximum size possible.
+ *
+ * @return 1.0 if {@link PipBoundsState#getBounds()} equals {@link PipBoundsState#getMaxSize()}.
+ */
+ public float getBoundsScale() {
+ return mBoundsScale;
+ }
+
/** Returns the current movement bounds. */
@NonNull
public Rect getMovementBounds() {
@@ -622,6 +641,9 @@
pw.println(innerPrefix + "mShelfHeight=" + mShelfHeight);
pw.println(innerPrefix + "mHasUserMovedPip=" + mHasUserMovedPip);
pw.println(innerPrefix + "mHasUserResizedPip=" + mHasUserResizedPip);
+ pw.println(innerPrefix + "mMinSize=" + mMinSize);
+ pw.println(innerPrefix + "mMaxSize=" + mMaxSize);
+ pw.println(innerPrefix + "mBoundsScale" + mBoundsScale);
if (mPipReentryState == null) {
pw.println(innerPrefix + "mPipReentryState=null");
} else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index e6d3abc..c51af46 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -110,13 +110,13 @@
import com.android.wm.shell.unfold.UnfoldTransitionHandler;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
-import java.util.Optional;
-
import dagger.BindsOptionalOf;
import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
+import java.util.Optional;
+
/**
* Provides basic dependencies from {@link com.android.wm.shell}, these dependencies are only
* accessible from components within the WM subcomponent (can be explicitly exposed to the
@@ -658,15 +658,15 @@
@WMSingleton
@Provides
static RootTaskDisplayAreaOrganizer provideRootTaskDisplayAreaOrganizer(
- @ShellMainThread ShellExecutor mainExecutor, Context context) {
- return new RootTaskDisplayAreaOrganizer(mainExecutor, context);
+ @ShellMainThread ShellExecutor mainExecutor, Context context, ShellInit shellInit) {
+ return new RootTaskDisplayAreaOrganizer(mainExecutor, context, shellInit);
}
@WMSingleton
@Provides
static RootDisplayAreaOrganizer provideRootDisplayAreaOrganizer(
- @ShellMainThread ShellExecutor mainExecutor) {
- return new RootDisplayAreaOrganizer(mainExecutor);
+ @ShellMainThread ShellExecutor mainExecutor, ShellInit shellInit) {
+ return new RootDisplayAreaOrganizer(mainExecutor, shellInit);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 5dfba5e..14a040a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -203,6 +203,7 @@
ShellTaskOrganizer taskOrganizer,
DisplayController displayController,
ShellController shellController,
+ DisplayInsetsController displayInsetsController,
SyncTransactionQueue syncQueue,
Transitions transitions,
Optional<DesktopTasksController> desktopTasksController,
@@ -218,6 +219,7 @@
taskOrganizer,
displayController,
shellController,
+ displayInsetsController,
syncQueue,
transitions,
desktopTasksController,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 1064867..63f20fd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -797,21 +797,15 @@
mPipBoundsAlgorithm.getMovementBounds(postChangeBounds),
mPipBoundsState.getStashedState());
- // Scale PiP on density dpi change, so it appears to be the same size physically.
- final boolean densityDpiChanged =
- mPipDisplayLayoutState.getDisplayLayout().densityDpi() != 0
- && (mPipDisplayLayoutState.getDisplayLayout().densityDpi()
- != layout.densityDpi());
- if (densityDpiChanged) {
- final float scale = (float) layout.densityDpi()
- / mPipDisplayLayoutState.getDisplayLayout().densityDpi();
- postChangeBounds.set(0, 0,
- (int) (postChangeBounds.width() * scale),
- (int) (postChangeBounds.height() * scale));
- }
-
updateDisplayLayout.run();
+ // Resize the PiP bounds to be at the same scale relative to the new size spec. For
+ // example, if PiP was resized to 90% of the maximum size on the previous layout,
+ // make sure it is 90% of the new maximum size spec.
+ postChangeBounds.set(0, 0,
+ (int) (mPipBoundsState.getMaxSize().x * mPipBoundsState.getBoundsScale()),
+ (int) (mPipBoundsState.getMaxSize().y * mPipBoundsState.getBoundsScale()));
+
// Calculate the PiP bounds in the new orientation based on same fraction along the
// rotated movement bounds.
final Rect postChangeMovementBounds = mPipBoundsAlgorithm.getMovementBounds(
@@ -822,6 +816,15 @@
mPipDisplayLayoutState.getDisplayBounds(),
mPipDisplayLayoutState.getDisplayLayout().stableInsets());
+ // make sure we user resize to the updated bounds to avoid animating to any outdated
+ // sizes from the previous layout upon double tap CUJ
+ mPipBoundsState.setHasUserResizedPip(true);
+ mTouchHandler.setUserResizeBounds(postChangeBounds);
+
+ final boolean densityDpiChanged =
+ mPipDisplayLayoutState.getDisplayLayout().densityDpi() != 0
+ && (mPipDisplayLayoutState.getDisplayLayout().densityDpi()
+ != layout.densityDpi());
if (densityDpiChanged) {
// Using PipMotionHelper#movePip directly here may cause race condition since
// the app content in PiP mode may or may not be updated for the new density dpi.
@@ -833,15 +836,6 @@
// Directly move PiP to its final destination bounds without animation.
mPipTaskOrganizer.scheduleFinishResizePip(postChangeBounds);
}
-
- // if the pip window size is beyond allowed bounds user resize to normal bounds
- if (mPipBoundsState.getBounds().width() < mPipBoundsState.getMinSize().x
- || mPipBoundsState.getBounds().width() > mPipBoundsState.getMaxSize().x
- || mPipBoundsState.getBounds().height() < mPipBoundsState.getMinSize().y
- || mPipBoundsState.getBounds().height() > mPipBoundsState.getMaxSize().y) {
- mTouchHandler.userResizeTo(mPipBoundsState.getNormalBounds(), snapFraction);
- }
-
} else {
updateDisplayLayout.run();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index e5f9fdc..f175775 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -15,7 +15,6 @@
*/
package com.android.wm.shell.pip.phone;
-import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_PINCH_RESIZE;
import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_BOTTOM;
import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_LEFT;
import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_NONE;
@@ -31,7 +30,6 @@
import android.graphics.Region;
import android.hardware.input.InputManager;
import android.os.Looper;
-import android.provider.DeviceConfig;
import android.view.BatchedInputEventReceiver;
import android.view.Choreographer;
import android.view.InputChannel;
@@ -155,21 +153,8 @@
mContext.getDisplay().getRealSize(mMaxSize);
reloadResources();
- mEnablePinchResize = DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- PIP_PINCH_RESIZE,
- /* defaultValue = */ true);
- DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
- mMainExecutor,
- new DeviceConfig.OnPropertiesChangedListener() {
- @Override
- public void onPropertiesChanged(DeviceConfig.Properties properties) {
- if (properties.getKeyset().contains(PIP_PINCH_RESIZE)) {
- mEnablePinchResize = properties.getBoolean(
- PIP_PINCH_RESIZE, /* defaultValue = */ true);
- }
- }
- });
+ final Resources res = mContext.getResources();
+ mEnablePinchResize = res.getBoolean(R.bool.config_pipEnablePinchResize);
}
public void onConfigurationChanged() {
@@ -579,6 +564,12 @@
resizeRectAboutCenter(mLastResizeBounds, mMaxSize.x, mMaxSize.y);
}
+ // If user resize is smaller than min size, auto resize to min
+ if (mLastResizeBounds.width() < mMinSize.x
+ || mLastResizeBounds.height() < mMinSize.y) {
+ resizeRectAboutCenter(mLastResizeBounds, mMinSize.x, mMinSize.y);
+ }
+
// get the current movement bounds
final Rect movementBounds = mPipBoundsAlgorithm
.getMovementBounds(mLastResizeBounds);
@@ -679,6 +670,8 @@
pw.println(innerPrefix + "mEnablePinchResize=" + mEnablePinchResize);
pw.println(innerPrefix + "mThresholdCrossed=" + mThresholdCrossed);
pw.println(innerPrefix + "mOhmOffset=" + mOhmOffset);
+ pw.println(innerPrefix + "mMinSize=" + mMinSize);
+ pw.println(innerPrefix + "mMaxSize=" + mMaxSize);
}
class PipResizeInputEventReceiver extends BatchedInputEventReceiver {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 2ce4fb9..452a416 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -779,13 +779,10 @@
}
/**
- * Resizes the pip window and updates user resized bounds
- *
- * @param bounds target bounds to resize to
- * @param snapFraction snap fraction to apply after resizing
+ * Sets the user resize bounds tracked by {@link PipResizeGestureHandler}
*/
- void userResizeTo(Rect bounds, float snapFraction) {
- mPipResizeGestureHandler.userResizeTo(bounds, snapFraction);
+ void setUserResizeBounds(Rect bounds) {
+ mPipResizeGestureHandler.setUserResizeBounds(bounds);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index b294866..68ca231 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -2242,6 +2242,25 @@
return SPLIT_POSITION_UNDEFINED;
}
+ /**
+ * Returns the {@link StageType} where {@param token} is being used
+ * {@link SplitScreen#STAGE_TYPE_UNDEFINED} otherwise
+ */
+ @StageType
+ public int getSplitItemStage(@Nullable WindowContainerToken token) {
+ if (token == null) {
+ return STAGE_TYPE_UNDEFINED;
+ }
+
+ if (mMainStage.containsToken(token)) {
+ return STAGE_TYPE_MAIN;
+ } else if (mSideStage.containsToken(token)) {
+ return STAGE_TYPE_SIDE;
+ }
+
+ return STAGE_TYPE_UNDEFINED;
+ }
+
@Override
public void setLayoutOffsetTarget(int offsetX, int offsetY, SplitLayout layout) {
final StageTaskListener topLeftStage =
@@ -2479,7 +2498,16 @@
mRecentTasks.ifPresent(
recentTasks -> recentTasks.removeSplitPair(triggerTask.taskId));
}
- prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, outWCT);
+ @StageType int topStage = STAGE_TYPE_UNDEFINED;
+ if (isSplitScreenVisible()) {
+ // Get the stage where a child exists to keep that stage onTop
+ if (mMainStage.getChildCount() != 0 && mSideStage.getChildCount() == 0) {
+ topStage = STAGE_TYPE_MAIN;
+ } else if (mSideStage.getChildCount() != 0 && mMainStage.getChildCount() == 0) {
+ topStage = STAGE_TYPE_SIDE;
+ }
+ }
+ prepareExitSplitScreen(topStage, outWCT);
}
}
@@ -2693,7 +2721,7 @@
@NonNull Transitions.TransitionFinishCallback finishCallback) {
boolean shouldAnimate = true;
if (mSplitTransitions.isPendingEnter(transition)) {
- shouldAnimate = startPendingEnterAnimation(
+ shouldAnimate = startPendingEnterAnimation(transition,
mSplitTransitions.mPendingEnter, info, startTransaction, finishTransaction);
} else if (mSplitTransitions.isPendingDismiss(transition)) {
final SplitScreenTransitions.DismissSession dismiss = mSplitTransitions.mPendingDismiss;
@@ -2732,7 +2760,7 @@
}
}
- private boolean startPendingEnterAnimation(
+ private boolean startPendingEnterAnimation(@NonNull IBinder transition,
@NonNull SplitScreenTransitions.EnterSession enterTransition,
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t,
@NonNull SurfaceControl.Transaction finishT) {
@@ -2761,21 +2789,22 @@
}
}
- if (mSplitTransitions.mPendingEnter.mExtraTransitType
+ SplitScreenTransitions.EnterSession pendingEnter = mSplitTransitions.mPendingEnter;
+ if (pendingEnter.mExtraTransitType
== TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE) {
// Open to side should only be used when split already active and foregorund.
if (mainChild == null && sideChild == null) {
Log.w(TAG, splitFailureMessage("startPendingEnterAnimation",
"Launched a task in split, but didn't receive any task in transition."));
// This should happen when the target app is already on front, so just cancel.
- mSplitTransitions.mPendingEnter.cancel(null);
+ pendingEnter.cancel(null);
return true;
}
} else {
if (mainChild == null || sideChild == null) {
final int dismissTop = mainChild != null ? STAGE_TYPE_MAIN :
(sideChild != null ? STAGE_TYPE_SIDE : STAGE_TYPE_UNDEFINED);
- mSplitTransitions.mPendingEnter.cancel(
+ pendingEnter.cancel(
(cancelWct, cancelT) -> prepareExitSplitScreen(dismissTop, cancelWct));
Log.w(TAG, splitFailureMessage("startPendingEnterAnimation",
"launched 2 tasks in split, but didn't receive "
@@ -2786,6 +2815,12 @@
if (mRecentTasks.isPresent() && sideChild != null) {
mRecentTasks.get().removeSplitPair(sideChild.getTaskInfo().taskId);
}
+ if (pendingEnter.mRemoteHandler != null) {
+ // Pass false for aborted since WM didn't abort, business logic chose to
+ // terminate/exit early
+ pendingEnter.mRemoteHandler.onTransitionConsumed(transition,
+ false /*aborted*/, finishT);
+ }
mSplitUnsupportedToast.show();
return true;
}
@@ -2896,7 +2931,11 @@
return SPLIT_POSITION_UNDEFINED;
}
- /** Synchronize split-screen state with transition and make appropriate preparations. */
+ /**
+ * Synchronize split-screen state with transition and make appropriate preparations.
+ * @param toStage The stage that will not be dismissed. If set to
+ * {@link SplitScreen#STAGE_TYPE_UNDEFINED} then both stages will be dismissed
+ */
public void prepareDismissAnimation(@StageType int toStage, @ExitReason int dismissReason,
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t,
@NonNull SurfaceControl.Transaction finishT) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index e828eed..226fe08 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -50,6 +50,7 @@
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.recents.RecentsTransitionHandler;
+import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.splitscreen.StageCoordinator;
import com.android.wm.shell.sysui.ShellInit;
@@ -511,8 +512,26 @@
// make a new startTransaction because pip's startEnterAnimation "consumes" it so
// we need a separate one to send over to launcher.
SurfaceControl.Transaction otherStartT = new SurfaceControl.Transaction();
+ @SplitScreen.StageType int topStageToKeep = STAGE_TYPE_UNDEFINED;
+ if (mSplitHandler.isSplitScreenVisible()) {
+ // The non-going home case, we could be pip-ing one of the split stages and keep
+ // showing the other
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ TransitionInfo.Change change = info.getChanges().get(i);
+ if (change == pipChange) {
+ // Ignore the change/task that's going into Pip
+ continue;
+ }
+ @SplitScreen.StageType int splitItemStage =
+ mSplitHandler.getSplitItemStage(change.getLastParent());
+ if (splitItemStage != STAGE_TYPE_UNDEFINED) {
+ topStageToKeep = splitItemStage;
+ break;
+ }
+ }
+ }
// Let split update internal state for dismiss.
- mSplitHandler.prepareDismissAnimation(STAGE_TYPE_UNDEFINED,
+ mSplitHandler.prepareDismissAnimation(topStageToKeep,
EXIT_REASON_CHILD_TASK_ENTER_PIP, everythingElse, otherStartT,
finishTransaction);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
index fab2dd2..8b050e5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
@@ -152,6 +152,16 @@
}
@Override
+ public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+ @Nullable SurfaceControl.Transaction finishTransaction) {
+ try {
+ mRemote.getRemoteTransition().onTransitionConsumed(transition, aborted);
+ } catch (RemoteException e) {
+ Log.e(Transitions.TAG, "Error calling onTransitionConsumed()", e);
+ }
+ }
+
+ @Override
public String toString() {
return "OneShotRemoteHandler:" + mRemote.getDebugName() + ":"
+ mRemote.getRemoteTransition();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index a90edf2..592b22a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -86,7 +86,16 @@
@Override
public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
@Nullable SurfaceControl.Transaction finishT) {
- mRequestedRemotes.remove(transition);
+ RemoteTransition remoteTransition = mRequestedRemotes.remove(transition);
+ if (remoteTransition == null) {
+ return;
+ }
+
+ try {
+ remoteTransition.getRemoteTransition().onTransitionConsumed(transition, aborted);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error delegating onTransitionConsumed()", e);
+ }
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index c74b3f3..0d9a9e9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -277,7 +277,7 @@
@NonNull ShellExecutor animExecutor) {
this(context, shellInit, shellController, organizer, pool, displayController, mainExecutor,
mainHandler, animExecutor, null,
- new RootTaskDisplayAreaOrganizer(mainExecutor, context));
+ new RootTaskDisplayAreaOrganizer(mainExecutor, context, shellInit));
}
public Transitions(@NonNull Context context,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index 82fc0f4..aff35a3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -231,4 +231,9 @@
int getCaptionHeightId(@WindowingMode int windowingMode) {
return R.dimen.freeform_decor_caption_height;
}
+
+ @Override
+ int getCaptionViewId() {
+ return R.id.caption;
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index bf99ab3..ca91d58 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -21,6 +21,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.view.WindowInsets.Type.statusBars;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
@@ -50,6 +51,8 @@
import android.view.InputEvent;
import android.view.InputEventReceiver;
import android.view.InputMonitor;
+import android.view.InsetsSource;
+import android.view.InsetsState;
import android.view.MotionEvent;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
@@ -67,6 +70,7 @@
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
@@ -131,6 +135,8 @@
private final DesktopModeKeyguardChangeListener mDesktopModeKeyguardChangeListener =
new DesktopModeKeyguardChangeListener();
private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
+ private final DisplayInsetsController mDisplayInsetsController;
+ private boolean mInImmersiveMode;
public DesktopModeWindowDecorViewModel(
Context context,
@@ -141,6 +147,7 @@
ShellTaskOrganizer taskOrganizer,
DisplayController displayController,
ShellController shellController,
+ DisplayInsetsController displayInsetsController,
SyncTransactionQueue syncQueue,
Transitions transitions,
Optional<DesktopTasksController> desktopTasksController,
@@ -156,6 +163,7 @@
taskOrganizer,
displayController,
shellController,
+ displayInsetsController,
syncQueue,
transitions,
desktopTasksController,
@@ -176,6 +184,7 @@
ShellTaskOrganizer taskOrganizer,
DisplayController displayController,
ShellController shellController,
+ DisplayInsetsController displayInsetsController,
SyncTransactionQueue syncQueue,
Transitions transitions,
Optional<DesktopTasksController> desktopTasksController,
@@ -191,6 +200,7 @@
mTaskOrganizer = taskOrganizer;
mShellController = shellController;
mDisplayController = displayController;
+ mDisplayInsetsController = displayInsetsController;
mSyncQueue = syncQueue;
mTransitions = transitions;
mDesktopTasksController = desktopTasksController;
@@ -213,6 +223,8 @@
}
});
mShellCommandHandler.addDumpCallback(this::dump, this);
+ mDisplayInsetsController.addInsetsChangedListener(mContext.getDisplayId(),
+ new DesktopModeOnInsetsChangedListener());
}
@Override
@@ -655,9 +667,9 @@
private void handleReceivedMotionEvent(MotionEvent ev, InputMonitor inputMonitor) {
final DesktopModeWindowDecoration relevantDecor = getRelevantWindowDecor(ev);
if (DesktopModeStatus.isEnabled()) {
- if (relevantDecor == null
+ if (!mInImmersiveMode && (relevantDecor == null
|| relevantDecor.mTaskInfo.getWindowingMode() != WINDOWING_MODE_FREEFORM
- || mTransitionDragActive) {
+ || mTransitionDragActive)) {
handleCaptionThroughStatusBar(ev, relevantDecor);
}
}
@@ -1051,6 +1063,35 @@
return mIsKeyguardVisible && mIsKeyguardOccluded;
}
}
+
+ @VisibleForTesting
+ class DesktopModeOnInsetsChangedListener implements
+ DisplayInsetsController.OnInsetsChangedListener {
+ @Override
+ public void insetsChanged(InsetsState insetsState) {
+ for (int i = 0; i < insetsState.sourceSize(); i++) {
+ final InsetsSource source = insetsState.sourceAt(i);
+ if (source.getType() != statusBars()) {
+ continue;
+ }
+
+ final DesktopModeWindowDecoration decor = getFocusedDecor();
+ if (decor == null) {
+ return;
+ }
+ // If status bar inset is visible, top task is not in immersive mode
+ final boolean inImmersiveMode = !source.isVisible();
+ // Calls WindowDecoration#relayout if decoration visibility needs to be updated
+ if (inImmersiveMode != mInImmersiveMode) {
+ decor.relayout(decor.mTaskInfo);
+ mInImmersiveMode = inImmersiveMode;
+ }
+
+ return;
+ }
+ }
+ }
+
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 380b59e..248e837 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -638,6 +638,11 @@
return loadDimensionPixelSize(mContext.getResources(), getCaptionHeightId(windowingMode));
}
+ @Override
+ int getCaptionViewId() {
+ return R.id.desktop_mode_caption;
+ }
+
/**
* Add transition to mTransitionsPausingRelayout
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java
index 09fc3da..368231e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java
@@ -132,6 +132,13 @@
t.setAlpha(mVeilSurface, mVeilAnimator.getAnimatedFraction());
t.apply();
});
+ mVeilAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ t.setAlpha(mVeilSurface, 1);
+ t.apply();
+ }
+ });
final ValueAnimator iconAnimator = new ValueAnimator();
iconAnimator.setFloatValues(0f, 1f);
@@ -192,8 +199,8 @@
*/
public void updateResizeVeil(SurfaceControl.Transaction t, Rect newBounds) {
if (mVeilAnimator != null && mVeilAnimator.isStarted()) {
- // TODO(b/300145351): Investigate why ValueAnimator#end does not work here.
- mVeilAnimator.setCurrentPlayTime(RESIZE_ALPHA_DURATION);
+ mVeilAnimator.removeAllUpdateListeners();
+ mVeilAnimator.end();
}
relayout(newBounds, t);
mViewHost.getView().getViewRootImpl().applyTransactionOnDraw(t);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 335a588..0548a8e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -18,6 +18,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.WindowInsets.Type.statusBars;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.WindowConfiguration.WindowingMode;
@@ -30,6 +31,8 @@
import android.graphics.Rect;
import android.os.Binder;
import android.view.Display;
+import android.view.InsetsSource;
+import android.view.InsetsState;
import android.view.LayoutInflater;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
@@ -119,6 +122,7 @@
private WindowlessWindowManager mCaptionWindowManager;
private SurfaceControlViewHost mViewHost;
private Configuration mWindowDecorConfig;
+ private boolean mIsCaptionVisible;
private final Binder mOwner = new Binder();
private final Rect mCaptionInsetsRect = new Rect();
@@ -225,6 +229,8 @@
.inflate(params.mLayoutResId, null);
}
+ updateCaptionVisibility(outResult.mRootView, mTaskInfo.displayId);
+
final Resources resources = mDecorWindowContext.getResources();
final Configuration taskConfig = mTaskInfo.getConfiguration();
final Rect taskBounds = taskConfig.windowConfiguration.getBounds();
@@ -272,12 +278,20 @@
// Caption insets
mCaptionInsetsRect.set(taskBounds);
- mCaptionInsetsRect.bottom = mCaptionInsetsRect.top + captionHeight + params.mCaptionY;
- wct.addInsetsSource(mTaskInfo.token,
- mOwner, 0 /* index */, WindowInsets.Type.captionBar(), mCaptionInsetsRect);
- wct.addInsetsSource(mTaskInfo.token,
- mOwner, 0 /* index */, WindowInsets.Type.mandatorySystemGestures(),
- mCaptionInsetsRect);
+ if (mIsCaptionVisible) {
+ mCaptionInsetsRect.bottom =
+ mCaptionInsetsRect.top + captionHeight + params.mCaptionY;
+ wct.addInsetsSource(mTaskInfo.token,
+ mOwner, 0 /* index */, WindowInsets.Type.captionBar(), mCaptionInsetsRect);
+ wct.addInsetsSource(mTaskInfo.token,
+ mOwner, 0 /* index */, WindowInsets.Type.mandatorySystemGestures(),
+ mCaptionInsetsRect);
+ } else {
+ wct.removeInsetsSource(mTaskInfo.token, mOwner, 0 /* index */,
+ WindowInsets.Type.captionBar());
+ wct.removeInsetsSource(mTaskInfo.token, mOwner, 0 /* index */,
+ WindowInsets.Type.mandatorySystemGestures());
+ }
} else {
startT.hide(mCaptionContainerSurface);
}
@@ -348,10 +362,41 @@
}
}
+ /**
+ * Checks if task has entered/exited immersive mode and requires a change in caption visibility.
+ */
+ private void updateCaptionVisibility(View rootView, int displayId) {
+ final InsetsState insetsState = mDisplayController.getInsetsState(displayId);
+ for (int i = 0; i < insetsState.sourceSize(); i++) {
+ final InsetsSource source = insetsState.sourceAt(i);
+ if (source.getType() != statusBars()) {
+ continue;
+ }
+
+ mIsCaptionVisible = source.isVisible();
+ setCaptionVisibility(rootView, mIsCaptionVisible);
+
+ return;
+ }
+ }
+
+ private void setCaptionVisibility(View rootView, boolean visible) {
+ if (rootView == null) {
+ return;
+ }
+ final int v = visible ? View.VISIBLE : View.GONE;
+ final View captionView = rootView.findViewById(getCaptionViewId());
+ captionView.setVisibility(v);
+ }
+
int getCaptionHeightId(@WindowingMode int windowingMode) {
return Resources.ID_NULL;
}
+ int getCaptionViewId() {
+ return Resources.ID_NULL;
+ }
+
/**
* Obtains the {@link Display} instance for the display ID in {@link #mTaskInfo} if it exists or
* registers {@link #mOnDisplaysChangedListener} if it doesn't.
@@ -466,7 +511,8 @@
*/
public void addCaptionInset(WindowContainerTransaction wct) {
final int captionHeightId = getCaptionHeightId(mTaskInfo.getWindowingMode());
- if (!ViewRootImpl.CAPTION_ON_SHELL || captionHeightId == Resources.ID_NULL) {
+ if (!ViewRootImpl.CAPTION_ON_SHELL || captionHeightId == Resources.ID_NULL
+ || !mIsCaptionVisible) {
return;
}
diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp
index 0058d11..7f02072 100644
--- a/libs/WindowManager/Shell/tests/flicker/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/Android.bp
@@ -116,6 +116,7 @@
"wm-flicker-common-assertions",
"launcher-helper-lib",
"launcher-aosp-tapl",
+ "com_android_wm_shell_flags_lib",
],
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
index 6748626..0fd1b2c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
@@ -18,6 +18,7 @@
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.common.flicker.subject.exceptions.IncorrectRegionException
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
@@ -40,14 +41,26 @@
transitions { pipApp.pinchInPipWindow(wmHelper, 0.4f, 30) }
}
- /** Checks that the visible region area of [pipApp] always decreases during the animation. */
+ /**
+ * Checks that the visible region area of [pipApp] decreases
+ * and then increases during the animation.
+ */
@Presubmit
@Test
- fun pipLayerAreaDecreases() {
+ fun pipLayerAreaDecreasesThenIncreases() {
+ val isAreaDecreasing = arrayOf(true)
flicker.assertLayers {
val pipLayerList = this.layers { pipApp.layerMatchesAnyOf(it) && it.isVisible }
pipLayerList.zipWithNext { previous, current ->
- current.visibleRegion.notBiggerThan(previous.visibleRegion.region)
+ if (isAreaDecreasing[0]) {
+ try {
+ current.visibleRegion.notBiggerThan(previous.visibleRegion.region)
+ } catch (e: IncorrectRegionException) {
+ isAreaDecreasing[0] = false
+ }
+ } else {
+ previous.visibleRegion.notBiggerThan(current.visibleRegion.region)
+ }
}
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp
index d09a90c..aadadd6 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -35,6 +35,7 @@
static_libs: [
"WindowManager-Shell",
"junit",
+ "flag-junit-base",
"androidx.test.runner",
"androidx.test.rules",
"androidx.test.ext.junit",
@@ -49,6 +50,7 @@
"testables",
"platform-test-annotations",
"servicestests-utils",
+ "com_android_wm_shell_flags_lib",
],
libs: [
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
index d34e27b..db98abb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
@@ -24,6 +24,7 @@
import static org.mockito.Mockito.verify;
import android.content.ComponentName;
+import android.graphics.Point;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -60,6 +61,9 @@
/** The minimum possible size of the override min size's width or height */
private static final int OVERRIDABLE_MIN_SIZE = 40;
+ /** The margin of error for floating point results. */
+ private static final float MARGIN_OF_ERROR = 0.05f;
+
private PipBoundsState mPipBoundsState;
private SizeSpecSource mSizeSpecSource;
private ComponentName mTestComponentName1;
@@ -88,6 +92,27 @@
}
@Test
+ public void testBoundsScale() {
+ mPipBoundsState.setMaxSize(300, 300);
+ mPipBoundsState.setBounds(new Rect(0, 0, 100, 100));
+
+ final int currentWidth = mPipBoundsState.getBounds().width();
+ final Point maxSize = mPipBoundsState.getMaxSize();
+ final float expectedBoundsScale = Math.min((float) currentWidth / maxSize.x, 1.0f);
+
+ // test for currentWidth < maxWidth
+ assertEquals(expectedBoundsScale, mPipBoundsState.getBoundsScale(), MARGIN_OF_ERROR);
+
+ // reset the bounds to be at the maximum size spec
+ mPipBoundsState.setBounds(new Rect(0, 0, maxSize.x, maxSize.y));
+ assertEquals(1.0f, mPipBoundsState.getBoundsScale(), /* delta */ 0f);
+
+ // reset the bounds to be over the maximum size spec
+ mPipBoundsState.setBounds(new Rect(0, 0, maxSize.x * 2, maxSize.y * 2));
+ assertEquals(1.0f, mPipBoundsState.getBoundsScale(), /* delta */ 0f);
+ }
+
+ @Test
public void testSetReentryState() {
final Size size = new Size(100, 100);
final float snapFraction = 0.5f;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
index 6777a5b..9719ba8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
@@ -28,11 +28,13 @@
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.testing.TestableResources;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import androidx.test.filters.SmallTest;
+import com.android.wm.shell.R;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
@@ -98,6 +100,9 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ final TestableResources res = mContext.getOrCreateTestableResources();
+ res.addOverride(R.bool.config_pipEnablePinchResize, true);
+
mPipDisplayLayoutState = new PipDisplayLayoutState(mContext);
mSizeSpecSource = new PhoneSizeSpecSource(mContext, mPipDisplayLayoutState);
mPipBoundsState = new PipBoundsState(mContext, mSizeSpecSource, mPipDisplayLayoutState);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index ebc284b..befc702 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -208,6 +208,30 @@
@Test
@UiThreadTest
+ public void testRemoteTransitionConsumed() {
+ // Omit side child change
+ TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0)
+ .addChange(TRANSIT_OPEN, mMainChild)
+ .build();
+ TestRemoteTransition testRemote = new TestRemoteTransition();
+
+ IBinder transition = mSplitScreenTransitions.startEnterTransition(
+ TRANSIT_OPEN, new WindowContainerTransaction(),
+ new RemoteTransition(testRemote, "Test"), mStageCoordinator,
+ TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false);
+ mMainStage.onTaskAppeared(mMainChild, createMockSurface());
+ boolean accepted = mStageCoordinator.startAnimation(transition, info,
+ mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
+ mock(Transitions.TransitionFinishCallback.class));
+ assertTrue(accepted);
+
+ assertTrue(testRemote.isConsumed());
+
+ }
+
+ @Test
+ @UiThreadTest
public void testMonitorInSplit() {
enterSplit();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index d598678..b9c9049 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -50,6 +50,7 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
@@ -92,6 +93,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayController;
@@ -145,7 +147,9 @@
final Transitions t = new Transitions(mContext, shellInit, mock(ShellController.class),
mOrganizer, mTransactionPool, createTestDisplayController(), mMainExecutor,
mMainHandler, mAnimExecutor);
- verify(shellInit, times(1)).addInitCallback(any(), eq(t));
+ // One from Transitions, one from RootTaskDisplayAreaOrganizer
+ verify(shellInit).addInitCallback(any(), eq(t));
+ verify(shellInit).addInitCallback(any(), isA(RootTaskDisplayAreaOrganizer.class));
}
@Test
@@ -285,6 +289,10 @@
SurfaceControl.Transaction t, IBinder mergeTarget,
IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
}
+
+ @Override
+ public void onTransitionConsumed(IBinder iBinder, boolean b) throws RemoteException {
+ }
};
IBinder transitToken = new Binder();
transitions.requestStartTransition(transitToken,
@@ -427,6 +435,10 @@
SurfaceControl.Transaction t, IBinder mergeTarget,
IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
}
+
+ @Override
+ public void onTransitionConsumed(IBinder iBinder, boolean b) throws RemoteException {
+ }
};
TransitionFilter filter = new TransitionFilter();
@@ -473,6 +485,10 @@
SurfaceControl.Transaction t, IBinder mergeTarget,
IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
}
+
+ @Override
+ public void onTransitionConsumed(IBinder iBinder, boolean b) throws RemoteException {
+ }
};
final int transitType = TRANSIT_FIRST_CUSTOM + 1;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/TestRemoteTransition.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/TestRemoteTransition.java
index 39ab238..87330d2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/TestRemoteTransition.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/TestRemoteTransition.java
@@ -31,6 +31,7 @@
*/
public class TestRemoteTransition extends IRemoteTransition.Stub {
private boolean mCalled = false;
+ private boolean mConsumed = false;
final WindowContainerTransaction mRemoteFinishWCT = new WindowContainerTransaction();
@Override
@@ -48,6 +49,11 @@
IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
}
+ @Override
+ public void onTransitionConsumed(IBinder iBinder, boolean b) throws RemoteException {
+ mConsumed = true;
+ }
+
/**
* Check whether this remote transition
* {@link #startAnimation(IBinder, TransitionInfo, SurfaceControl.Transaction,
@@ -56,4 +62,12 @@
public boolean isCalled() {
return mCalled;
}
+
+ /**
+ * Check whether this remote transition's {@link #onTransitionConsumed(IBinder, boolean)}
+ * is called
+ */
+ public boolean isConsumed() {
+ return mConsumed;
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index 8eaf5a0..57aa47e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -33,8 +33,12 @@
import android.view.Display.DEFAULT_DISPLAY
import android.view.InputChannel
import android.view.InputMonitor
+import android.view.InsetsSource
+import android.view.InsetsState
import android.view.SurfaceControl
import android.view.SurfaceView
+import android.view.WindowInsets.Type.navigationBars
+import android.view.WindowInsets.Type.statusBars
import androidx.core.content.getSystemService
import androidx.test.filters.SmallTest
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
@@ -42,6 +46,7 @@
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestRunningTaskInfoBuilder
import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.DisplayInsetsController
import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
@@ -53,10 +58,12 @@
import com.android.wm.shell.sysui.ShellController
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel.DesktopModeOnInsetsChangedListener
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
+import org.mockito.Mockito.anyInt
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
@@ -68,6 +75,7 @@
import java.util.Optional
import java.util.function.Supplier
+
/** Tests of [DesktopModeWindowDecorViewModel] */
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -80,6 +88,7 @@
@Mock private lateinit var mockTaskOrganizer: ShellTaskOrganizer
@Mock private lateinit var mockDisplayController: DisplayController
@Mock private lateinit var mockDisplayLayout: DisplayLayout
+ @Mock private lateinit var displayInsetsController: DisplayInsetsController
@Mock private lateinit var mockSyncQueue: SyncTransactionQueue
@Mock private lateinit var mockDesktopTasksController: DesktopTasksController
@Mock private lateinit var mockInputMonitor: InputMonitor
@@ -97,6 +106,7 @@
}
private lateinit var shellInit: ShellInit
+ private lateinit var desktopModeOnInsetsChangedListener: DesktopModeOnInsetsChangedListener
private lateinit var desktopModeWindowDecorViewModel: DesktopModeWindowDecorViewModel
@Before
@@ -111,6 +121,7 @@
mockTaskOrganizer,
mockDisplayController,
mockShellController,
+ displayInsetsController,
mockSyncQueue,
mockTransitions,
Optional.of(mockDesktopTasksController),
@@ -131,6 +142,11 @@
whenever(mockInputMonitor.inputChannel).thenReturn(inputChannels[1])
shellInit.init()
+
+ val listenerCaptor =
+ argumentCaptor<DesktopModeWindowDecorViewModel.DesktopModeOnInsetsChangedListener>()
+ verify(displayInsetsController).addInsetsChangedListener(anyInt(), listenerCaptor.capture())
+ desktopModeOnInsetsChangedListener = listenerCaptor.firstValue
}
@Test
@@ -274,6 +290,67 @@
verify(decoration).addTransitionPausingRelayout(transition)
}
+ @Test
+ fun testRelayoutRunsWhenStatusBarsInsetsSourceVisibilityChanges() {
+ val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM, focused = true)
+ val decoration = setUpMockDecorationForTask(task)
+
+ onTaskOpening(task)
+
+ // Add status bar insets source
+ val insetsState = InsetsState()
+ val statusBarInsetsSourceId = 0
+ val statusBarInsetsSource = InsetsSource(statusBarInsetsSourceId, statusBars())
+ statusBarInsetsSource.isVisible = false
+ insetsState.addSource(statusBarInsetsSource)
+
+ desktopModeOnInsetsChangedListener.insetsChanged(insetsState)
+
+ // Verify relayout occurs when status bar inset visibility changes
+ verify(decoration, times(1)).relayout(task)
+ }
+
+ @Test
+ fun testRelayoutDoesNotRunWhenNonStatusBarsInsetsSourceVisibilityChanges() {
+ val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM, focused = true)
+ val decoration = setUpMockDecorationForTask(task)
+
+ onTaskOpening(task)
+
+ // Add navigation bar insets source
+ val insetsState = InsetsState()
+ val navigationBarInsetsSourceId = 1
+ val navigationBarInsetsSource = InsetsSource(navigationBarInsetsSourceId, navigationBars())
+ navigationBarInsetsSource.isVisible = false
+ insetsState.addSource(navigationBarInsetsSource)
+
+ desktopModeOnInsetsChangedListener.insetsChanged(insetsState)
+
+ // Verify relayout does not occur when non-status bar inset changes visibility
+ verify(decoration, never()).relayout(task)
+ }
+
+ @Test
+ fun testRelayoutDoesNotRunWhenNonStatusBarsInsetSourceVisibilityDoesNotChange() {
+ val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM, focused = true)
+ val decoration = setUpMockDecorationForTask(task)
+
+ onTaskOpening(task)
+
+ // Add status bar insets source
+ val insetsState = InsetsState()
+ val statusBarInsetsSourceId = 0
+ val statusBarInsetsSource = InsetsSource(statusBarInsetsSourceId, statusBars())
+ statusBarInsetsSource.isVisible = false
+ insetsState.addSource(statusBarInsetsSource)
+
+ desktopModeOnInsetsChangedListener.insetsChanged(insetsState)
+ desktopModeOnInsetsChangedListener.insetsChanged(insetsState)
+
+ // Verify relayout runs only once when status bar inset visibility changes.
+ verify(decoration, times(1)).relayout(task)
+ }
+
private fun onTaskOpening(task: RunningTaskInfo, leash: SurfaceControl = SurfaceControl()) {
desktopModeWindowDecorViewModel.onTaskOpening(
task,
@@ -313,6 +390,8 @@
whenever(mockDesktopModeWindowDecorFactory.create(
any(), any(), any(), eq(task), any(), any(), any(), any(), any())
).thenReturn(decoration)
+ decoration.mTaskInfo = task
+ whenever(decoration.isFocused).thenReturn(task.isFocused)
return decoration
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index fcb7863..8061aa3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -18,6 +18,9 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.WindowInsets.Type.captionBar;
+import static android.view.WindowInsets.Type.mandatorySystemGestures;
+import static android.view.WindowInsets.Type.statusBars;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.wm.shell.MockSurfaceControlHelper.createMockSurfaceControlBuilder;
@@ -25,6 +28,8 @@
import static com.google.common.truth.Truth.assertThat;
+import static junit.framework.Assert.assertTrue;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -51,6 +56,7 @@
import android.util.DisplayMetrics;
import android.view.AttachedSurfaceControl;
import android.view.Display;
+import android.view.InsetsState;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.View;
@@ -94,6 +100,7 @@
private static final Rect TASK_BOUNDS = new Rect(100, 300, 400, 400);
private static final Point TASK_POSITION_IN_PARENT = new Point(40, 60);
private static final int CORNER_RADIUS = 20;
+ private static final int STATUS_BAR_INSET_SOURCE_ID = 0;
private final WindowDecoration.RelayoutResult<TestView> mRelayoutResult =
new WindowDecoration.RelayoutResult<>();
@@ -118,6 +125,7 @@
private final List<SurfaceControl.Transaction> mMockSurfaceControlTransactions =
new ArrayList<>();
private final List<SurfaceControl.Builder> mMockSurfaceControlBuilders = new ArrayList<>();
+ private final InsetsState mInsetsState = new InsetsState();
private SurfaceControl.Transaction mMockSurfaceControlStartT;
private SurfaceControl.Transaction mMockSurfaceControlFinishT;
private SurfaceControl.Transaction mMockSurfaceControlAddWindowT;
@@ -141,6 +149,11 @@
.create(any(), any(), any());
when(mMockSurfaceControlViewHost.getRootSurfaceControl())
.thenReturn(mMockRootSurfaceControl);
+ when(mMockView.findViewById(anyInt())).thenReturn(mMockView);
+
+ // Add status bar inset so that WindowDecoration does not think task is in immersive mode
+ mInsetsState.getOrCreateSource(STATUS_BAR_INSET_SOURCE_ID, statusBars()).setVisible(true);
+ doReturn(mInsetsState).when(mMockDisplayController).getInsetsState(anyInt());
}
@Test
@@ -537,12 +550,41 @@
windowDecor.relayout(taskInfo);
- verify(mMockSurfaceControlStartT).setColor(taskSurface, new float[] {1.f, 1.f, 0.f});
+ verify(mMockSurfaceControlStartT).setColor(taskSurface, new float[]{1.f, 1.f, 0.f});
mockitoSession.finishMocking();
}
@Test
+ public void testInsetsAddedWhenCaptionIsVisible() {
+ final Display defaultDisplay = mock(Display.class);
+ doReturn(defaultDisplay).when(mMockDisplayController)
+ .getDisplay(Display.DEFAULT_DISPLAY);
+
+ final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
+ new ActivityManager.TaskDescription.Builder();
+ final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setDisplayId(Display.DEFAULT_DISPLAY)
+ .setTaskDescriptionBuilder(taskDescriptionBuilder)
+ .setVisible(true)
+ .build();
+ final SurfaceControl taskSurface = mock(SurfaceControl.class);
+ final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface);
+
+ assertTrue(mInsetsState.getOrCreateSource(STATUS_BAR_INSET_SOURCE_ID, statusBars())
+ .isVisible());
+ assertTrue(mInsetsState.sourceSize() == 1);
+ assertTrue(mInsetsState.sourceAt(0).getType() == statusBars());
+
+ windowDecor.relayout(taskInfo);
+
+ verify(mMockWindowContainerTransaction).addInsetsSource(eq(taskInfo.token), any(),
+ eq(0) /* index */, eq(captionBar()), any());
+ verify(mMockWindowContainerTransaction).addInsetsSource(eq(taskInfo.token), any(),
+ eq(0) /* index */, eq(mandatorySystemGestures()), any());
+ }
+
+ @Test
public void testRelayout_fluidResizeEnabled_fullscreenTask_clearTaskSurfaceColor() {
StaticMockitoSession mockitoSession = mockitoSession().mockStatic(
DesktopModeStatus.class).strictness(LENIENT).startMocking();
@@ -581,6 +623,33 @@
mockitoSession.finishMocking();
}
+
+ @Test
+ public void testInsetsRemovedWhenCaptionIsHidden() {
+ final Display defaultDisplay = mock(Display.class);
+ doReturn(defaultDisplay).when(mMockDisplayController)
+ .getDisplay(Display.DEFAULT_DISPLAY);
+
+ mInsetsState.getOrCreateSource(STATUS_BAR_INSET_SOURCE_ID, captionBar()).setVisible(false);
+
+ final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
+ new ActivityManager.TaskDescription.Builder();
+ final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setDisplayId(Display.DEFAULT_DISPLAY)
+ .setTaskDescriptionBuilder(taskDescriptionBuilder)
+ .setVisible(true)
+ .build();
+ final SurfaceControl taskSurface = mock(SurfaceControl.class);
+ final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface);
+
+ windowDecor.relayout(taskInfo);
+
+ verify(mMockWindowContainerTransaction).removeInsetsSource(eq(taskInfo.token), any(),
+ eq(0) /* index */, eq(captionBar()));
+ verify(mMockWindowContainerTransaction).removeInsetsSource(eq(taskInfo.token), any(),
+ eq(0) /* index */, eq(mandatorySystemGestures()));
+ }
+
private TestWindowDecoration createWindowDecoration(
ActivityManager.RunningTaskInfo taskInfo, SurfaceControl testSurface) {
return new TestWindowDecoration(mContext, mMockDisplayController, mMockShellTaskOrganizer,
diff --git a/libs/hwui/api/current.txt b/libs/hwui/api/current.txt
index c396a20..7940821 100644
--- a/libs/hwui/api/current.txt
+++ b/libs/hwui/api/current.txt
@@ -1,4 +1,6 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
package android.graphics {
public class ColorMatrix {
diff --git a/libs/hwui/api/module-lib-current.txt b/libs/hwui/api/module-lib-current.txt
index d802177..14191eb 100644
--- a/libs/hwui/api/module-lib-current.txt
+++ b/libs/hwui/api/module-lib-current.txt
@@ -1 +1,3 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
diff --git a/libs/hwui/api/module-lib-removed.txt b/libs/hwui/api/module-lib-removed.txt
index d802177..14191eb 100644
--- a/libs/hwui/api/module-lib-removed.txt
+++ b/libs/hwui/api/module-lib-removed.txt
@@ -1 +1,3 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
diff --git a/libs/hwui/api/removed.txt b/libs/hwui/api/removed.txt
index d802177..14191eb 100644
--- a/libs/hwui/api/removed.txt
+++ b/libs/hwui/api/removed.txt
@@ -1 +1,3 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
diff --git a/libs/hwui/api/system-current.txt b/libs/hwui/api/system-current.txt
index d802177..14191eb 100644
--- a/libs/hwui/api/system-current.txt
+++ b/libs/hwui/api/system-current.txt
@@ -1 +1,3 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
diff --git a/libs/hwui/api/system-removed.txt b/libs/hwui/api/system-removed.txt
index d802177..14191eb 100644
--- a/libs/hwui/api/system-removed.txt
+++ b/libs/hwui/api/system-removed.txt
@@ -1 +1,3 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp
index 7aef7a5..1463945 100644
--- a/libs/hwui/jni/Paint.cpp
+++ b/libs/hwui/jni/Paint.cpp
@@ -593,7 +593,7 @@
return result;
}
- static SkScalar getMetricsInternal(jlong paintHandle, SkFontMetrics *metrics) {
+ static SkScalar getMetricsInternal(jlong paintHandle, SkFontMetrics* metrics, bool useLocale) {
const int kElegantTop = 2500;
const int kElegantBottom = -1000;
const int kElegantAscent = 1900;
@@ -622,6 +622,17 @@
metrics->fLeading = size * kElegantLeading / 2048;
spacing = metrics->fDescent - metrics->fAscent + metrics->fLeading;
}
+
+ if (useLocale) {
+ minikin::MinikinPaint minikinPaint = MinikinUtils::prepareMinikinPaint(paint, typeface);
+ minikin::MinikinExtent extent =
+ typeface->fFontCollection->getReferenceExtentForLocale(minikinPaint);
+ metrics->fAscent = std::min(extent.ascent, metrics->fAscent);
+ metrics->fDescent = std::max(extent.descent, metrics->fDescent);
+ metrics->fTop = std::min(metrics->fAscent, metrics->fTop);
+ metrics->fBottom = std::max(metrics->fDescent, metrics->fBottom);
+ }
+
return spacing;
}
@@ -634,7 +645,7 @@
MinikinUtils::getFontExtent(paint, bidiFlags, typeface, buf, start, count, bufSize);
SkFontMetrics metrics;
- getMetricsInternal(paintHandle, &metrics);
+ getMetricsInternal(paintHandle, &metrics, false /* useLocale */);
metrics.fAscent = extent.ascent;
metrics.fDescent = extent.descent;
@@ -686,20 +697,21 @@
}
}
- static jfloat getFontMetrics(JNIEnv* env, jobject, jlong paintHandle, jobject metricsObj) {
+ static jfloat getFontMetrics(JNIEnv* env, jobject, jlong paintHandle, jobject metricsObj,
+ jboolean useLocale) {
SkFontMetrics metrics;
- SkScalar spacing = getMetricsInternal(paintHandle, &metrics);
+ SkScalar spacing = getMetricsInternal(paintHandle, &metrics, useLocale);
GraphicsJNI::set_metrics(env, metricsObj, metrics);
return SkScalarToFloat(spacing);
}
- static jint getFontMetricsInt(JNIEnv* env, jobject, jlong paintHandle, jobject metricsObj) {
+ static jint getFontMetricsInt(JNIEnv* env, jobject, jlong paintHandle, jobject metricsObj,
+ jboolean useLocale) {
SkFontMetrics metrics;
- getMetricsInternal(paintHandle, &metrics);
+ getMetricsInternal(paintHandle, &metrics, useLocale);
return GraphicsJNI::set_metrics_int(env, metricsObj, metrics);
}
-
// ------------------ @CriticalNative ---------------------------
static void reset(CRITICAL_JNI_PARAMS_COMMA jlong objHandle) {
@@ -1002,19 +1014,19 @@
static jfloat ascent(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
SkFontMetrics metrics;
- getMetricsInternal(paintHandle, &metrics);
+ getMetricsInternal(paintHandle, &metrics, false /* useLocale */);
return SkScalarToFloat(metrics.fAscent);
}
static jfloat descent(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
SkFontMetrics metrics;
- getMetricsInternal(paintHandle, &metrics);
+ getMetricsInternal(paintHandle, &metrics, false /* useLocale */);
return SkScalarToFloat(metrics.fDescent);
}
static jfloat getUnderlinePosition(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
SkFontMetrics metrics;
- getMetricsInternal(paintHandle, &metrics);
+ getMetricsInternal(paintHandle, &metrics, false /* useLocale */);
SkScalar position;
if (metrics.hasUnderlinePosition(&position)) {
return SkScalarToFloat(position);
@@ -1026,7 +1038,7 @@
static jfloat getUnderlineThickness(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle) {
SkFontMetrics metrics;
- getMetricsInternal(paintHandle, &metrics);
+ getMetricsInternal(paintHandle, &metrics, false /* useLocale */);
SkScalar thickness;
if (metrics.hasUnderlineThickness(&thickness)) {
return SkScalarToFloat(thickness);
@@ -1121,9 +1133,9 @@
{"nSetTextLocales", "(JLjava/lang/String;)I", (void*)PaintGlue::setTextLocales},
{"nSetFontFeatureSettings", "(JLjava/lang/String;)V",
(void*)PaintGlue::setFontFeatureSettings},
- {"nGetFontMetrics", "(JLandroid/graphics/Paint$FontMetrics;)F",
+ {"nGetFontMetrics", "(JLandroid/graphics/Paint$FontMetrics;Z)F",
(void*)PaintGlue::getFontMetrics},
- {"nGetFontMetricsInt", "(JLandroid/graphics/Paint$FontMetricsInt;)I",
+ {"nGetFontMetricsInt", "(JLandroid/graphics/Paint$FontMetricsInt;Z)I",
(void*)PaintGlue::getFontMetricsInt},
// --------------- @CriticalNative ------------------
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 230fb07..bf9419fe 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -875,18 +875,7 @@
/**
* Sets the attribute describing what is the intended use of the audio signal,
* such as alarm or ringtone.
- * @param usage one of {@link AttributeSdkUsage#USAGE_UNKNOWN},
- * {@link AttributeSdkUsage#USAGE_MEDIA},
- * {@link AttributeSdkUsage#USAGE_VOICE_COMMUNICATION},
- * {@link AttributeSdkUsage#USAGE_VOICE_COMMUNICATION_SIGNALLING},
- * {@link AttributeSdkUsage#USAGE_ALARM}, {@link AudioAttributes#USAGE_NOTIFICATION},
- * {@link AttributeSdkUsage#USAGE_NOTIFICATION_RINGTONE},
- * {@link AttributeSdkUsage#USAGE_NOTIFICATION_EVENT},
- * {@link AttributeSdkUsage#USAGE_ASSISTANT},
- * {@link AttributeSdkUsage#USAGE_ASSISTANCE_ACCESSIBILITY},
- * {@link AttributeSdkUsage#USAGE_ASSISTANCE_NAVIGATION_GUIDANCE},
- * {@link AttributeSdkUsage#USAGE_ASSISTANCE_SONIFICATION},
- * {@link AttributeSdkUsage#USAGE_GAME}.
+ * @param usage the usage to set.
* @return the same Builder instance.
*/
public Builder setUsage(@AttributeSdkUsage int usage) {
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index a311296..ceb3858 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -1284,8 +1284,7 @@
* {@link AudioFormat#CHANNEL_OUT_SIDE_RIGHT}.
* <p> For a valid {@link AudioTrack} channel position mask,
* the following conditions apply:
- * <br> (1) at most {@link AudioSystem#OUT_CHANNEL_COUNT_MAX} channel positions may be
- * used;
+ * <br> (1) at most eight channel positions may be used;
* <br> (2) right/left pairs should be matched.
* <p> For input or {@link AudioRecord}, the mask should be
* {@link AudioFormat#CHANNEL_IN_MONO} or
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index adc0e16..d9ed6a8 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -4659,24 +4659,24 @@
Objects.requireNonNull(afr);
Objects.requireNonNull(clientFakeId);
int status;
- try {
- status = getService().requestAudioFocusForTest(afr.getAudioAttributes(),
- afr.getFocusGain(),
- mICallBack,
- mAudioFocusDispatcher,
- clientFakeId, "com.android.test.fakeclient",
- afr.getFlags() | AudioManager.AUDIOFOCUS_FLAG_TEST,
- clientFakeUid, clientTargetSdk);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- if (status != AudioManager.AUDIOFOCUS_REQUEST_WAITING_FOR_EXT_POLICY) {
- // default path with no external focus policy
- return status;
- }
-
BlockingFocusResultReceiver focusReceiver;
synchronized (mFocusRequestsLock) {
+ try {
+ status = getService().requestAudioFocusForTest(afr.getAudioAttributes(),
+ afr.getFocusGain(),
+ mICallBack,
+ mAudioFocusDispatcher,
+ clientFakeId, "com.android.test.fakeclient",
+ afr.getFlags() | AudioManager.AUDIOFOCUS_FLAG_TEST,
+ clientFakeUid, clientTargetSdk);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ if (status != AudioManager.AUDIOFOCUS_REQUEST_WAITING_FOR_EXT_POLICY) {
+ // default path with no external focus policy
+ return status;
+ }
+
focusReceiver = addClientIdToFocusReceiverLocked(clientFakeId);
}
diff --git a/media/java/android/media/AudioMetadata.java b/media/java/android/media/AudioMetadata.java
index 0f962f9..4e61549 100644
--- a/media/java/android/media/AudioMetadata.java
+++ b/media/java/android/media/AudioMetadata.java
@@ -226,16 +226,15 @@
*
* An Integer value representing presentation content classifier.
*
- * @see AudioPresentation.ContentClassifier
- * One of {@link AudioPresentation#CONTENT_UNKNOWN},
- * {@link AudioPresentation#CONTENT_MAIN},
- * {@link AudioPresentation#CONTENT_MUSIC_AND_EFFECTS},
- * {@link AudioPresentation#CONTENT_VISUALLY_IMPAIRED},
- * {@link AudioPresentation#CONTENT_HEARING_IMPAIRED},
- * {@link AudioPresentation#CONTENT_DIALOG},
- * {@link AudioPresentation#CONTENT_COMMENTARY},
- * {@link AudioPresentation#CONTENT_EMERGENCY},
- * {@link AudioPresentation#CONTENT_VOICEOVER}.
+ * @see AudioPresentation#CONTENT_UNKNOWN
+ * @see AudioPresentation#CONTENT_MAIN
+ * @see AudioPresentation#CONTENT_MUSIC_AND_EFFECTS
+ * @see AudioPresentation#CONTENT_VISUALLY_IMPAIRED
+ * @see AudioPresentation#CONTENT_HEARING_IMPAIRED
+ * @see AudioPresentation#CONTENT_DIALOG
+ * @see AudioPresentation#CONTENT_COMMENTARY
+ * @see AudioPresentation#CONTENT_EMERGENCY
+ * @see AudioPresentation#CONTENT_VOICEOVER
*/
@NonNull public static final Key<Integer> KEY_PRESENTATION_CONTENT_CLASSIFIER =
createKey("presentation-content-classifier", Integer.class);
diff --git a/media/java/android/media/IRingtonePlayer.aidl b/media/java/android/media/IRingtonePlayer.aidl
index 73f15f2..1e57be2 100644
--- a/media/java/android/media/IRingtonePlayer.aidl
+++ b/media/java/android/media/IRingtonePlayer.aidl
@@ -49,7 +49,7 @@
oneway void setHapticGeneratorEnabled(IBinder token, boolean hapticGeneratorEnabled);
/** Used for Notification sound playback. */
- oneway void playAsync(in Uri uri, in UserHandle user, boolean looping, in AudioAttributes aa);
+ oneway void playAsync(in Uri uri, in UserHandle user, boolean looping, in AudioAttributes aa, float volume);
oneway void stopAsync();
/** Return the title of the media. */
diff --git a/media/java/android/media/projection/OWNERS b/media/java/android/media/projection/OWNERS
index cc9be9c..880ec8f 100644
--- a/media/java/android/media/projection/OWNERS
+++ b/media/java/android/media/projection/OWNERS
@@ -4,3 +4,4 @@
santoscordon@google.com
chaviw@google.com
nmusgrave@google.com
+dakinola@google.com
diff --git a/media/java/android/media/tv/SectionRequest.java b/media/java/android/media/tv/SectionRequest.java
index 078e832..ec0d7f7 100644
--- a/media/java/android/media/tv/SectionRequest.java
+++ b/media/java/android/media/tv/SectionRequest.java
@@ -81,7 +81,7 @@
/**
* Gets the version number of requested session. If it is null, value will be -1.
* <p>The consistency of version numbers between request and response depends on
- * {@link BroadcastInfoRequest.RequestOption}. If the request has RequestOption value
+ * {@link BroadcastInfoRequest#getOption()}. If the request has RequestOption value
* REQUEST_OPTION_AUTO_UPDATE, then the response may be set to the latest version which may be
* different from the version of the request. Otherwise, response with a different version from
* its request will be considered invalid.
diff --git a/media/java/android/media/tv/SectionResponse.java b/media/java/android/media/tv/SectionResponse.java
index f38ea9d..10333fe 100644
--- a/media/java/android/media/tv/SectionResponse.java
+++ b/media/java/android/media/tv/SectionResponse.java
@@ -76,7 +76,7 @@
/**
* Gets the Version number of requested session. If it is null, value will be -1.
* <p>The consistency of version numbers between request and response depends on
- * {@link BroadcastInfoRequest.RequestOption}. If the request has RequestOption value
+ * {@link BroadcastInfoRequest#getOption()}. If the request has RequestOption value
* REQUEST_OPTION_AUTO_UPDATE, then the response may be set to the latest version which may be
* different from the version of the request. Otherwise, response with a different version from
* its request will be considered invalid.
diff --git a/media/java/android/media/tv/TableRequest.java b/media/java/android/media/tv/TableRequest.java
index d9587f6..06df07f 100644
--- a/media/java/android/media/tv/TableRequest.java
+++ b/media/java/android/media/tv/TableRequest.java
@@ -129,7 +129,7 @@
/**
* Gets the version number of requested table. If it is null, value will be -1.
* <p>The consistency of version numbers between request and response depends on
- * {@link BroadcastInfoRequest.RequestOption}. If the request has RequestOption value
+ * {@link BroadcastInfoRequest#getOption()}. If the request has RequestOption value
* REQUEST_OPTION_AUTO_UPDATE, then the response may be set to the latest version which may be
* different from the version of the request. Otherwise, response with a different version from
* its request will be considered invalid.
diff --git a/media/java/android/media/tv/TableResponse.java b/media/java/android/media/tv/TableResponse.java
index c4fc26e..1daf452 100644
--- a/media/java/android/media/tv/TableResponse.java
+++ b/media/java/android/media/tv/TableResponse.java
@@ -269,7 +269,7 @@
/**
* Gets the version number of requested table. If it is null, value will be -1.
* <p>The consistency of version numbers between request and response depends on
- * {@link BroadcastInfoRequest.RequestOption}. If the request has RequestOption value
+ * {@link BroadcastInfoRequest#getOption()}. If the request has RequestOption value
* REQUEST_OPTION_AUTO_UPDATE, then the response may be set to the latest version which may be
* different from the version of the request. Otherwise, response with a different version from
* its request will be considered invalid.
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index 667a9ae..13f7743 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -939,9 +939,10 @@
type = TYPE_HDMI;
isHardwareInput = true;
hdmiConnectionRelativePosition = getRelativePosition(mContext, mHdmiDeviceInfo);
- isConnectedToHdmiSwitch =
- hdmiConnectionRelativePosition
- != HdmiUtils.HDMI_RELATIVE_POSITION_DIRECTLY_BELOW;
+ isConnectedToHdmiSwitch = hdmiConnectionRelativePosition
+ != HdmiUtils.HDMI_RELATIVE_POSITION_DIRECTLY_BELOW
+ && hdmiConnectionRelativePosition
+ != HdmiUtils.HDMI_RELATIVE_POSITION_UNKNOWN;
} else if (mTvInputHardwareInfo != null) {
id = generateInputId(componentName, mTvInputHardwareInfo);
type = sHardwareTypeToTvInputType.get(mTvInputHardwareInfo.getType(), TYPE_TUNER);
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index c616b84f..1c25080 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -38,6 +38,8 @@
#include <mediadrm/IDrmMetricsConsumer.h>
#include <mediadrm/IDrm.h>
#include <utils/Vector.h>
+#include <map>
+#include <string>
using ::android::os::PersistableBundle;
namespace drm = ::android::hardware::drm;
@@ -193,6 +195,11 @@
jclass classId;
};
+struct DrmExceptionFields {
+ jmethodID init;
+ jclass classId;
+};
+
struct fields_t {
jfieldID context;
jmethodID post_event;
@@ -215,6 +222,7 @@
jclass parcelCreatorClassId;
KeyStatusFields keyStatus;
LogMessageFields logMessage;
+ std::map<std::string, DrmExceptionFields> exceptionCtors;
};
static fields_t gFields;
@@ -245,18 +253,32 @@
return arrayList;
}
-int drmThrowException(JNIEnv* env, const char *className, const DrmStatus &err, const char *msg) {
+void resolveDrmExceptionCtor(JNIEnv *env, const char *className) {
+ jclass clazz;
+ jmethodID init;
+ FIND_CLASS(clazz, className);
+ GET_METHOD_ID(init, clazz, "<init>", "(Ljava/lang/String;III)V");
+ gFields.exceptionCtors[std::string(className)] = {
+ .init = init,
+ .classId = static_cast<jclass>(env->NewGlobalRef(clazz))
+ };
+}
+
+void drmThrowException(JNIEnv* env, const char *className, const DrmStatus &err, const char *msg) {
using namespace android::jnihelp;
- jstring _detailMessage = CreateExceptionMsg(env, msg);
- int _status = ThrowException(env, className, "(Ljava/lang/String;III)V",
- _detailMessage,
- err.getCdmErr(),
- err.getOemErr(),
- err.getContext());
- if (_detailMessage != NULL) {
- env->DeleteLocalRef(_detailMessage);
+
+ if (gFields.exceptionCtors.count(std::string(className)) == 0) {
+ jniThrowException(env, className, msg);
+ } else {
+ jstring _detailMessage = CreateExceptionMsg(env, msg);
+ jobject exception = env->NewObject(gFields.exceptionCtors[std::string(className)].classId,
+ gFields.exceptionCtors[std::string(className)].init, _detailMessage,
+ err.getCdmErr(), err.getOemErr(), err.getContext());
+ env->Throw(static_cast<jthrowable>(exception));
+ if (_detailMessage != NULL) {
+ env->DeleteLocalRef(_detailMessage);
+ }
}
- return _status;
}
} // namespace anonymous
@@ -952,6 +974,10 @@
FIND_CLASS(clazz, "android/media/MediaDrm$LogMessage");
gFields.logMessage.classId = static_cast<jclass>(env->NewGlobalRef(clazz));
GET_METHOD_ID(gFields.logMessage.init, clazz, "<init>", "(JILjava/lang/String;)V");
+
+ resolveDrmExceptionCtor(env, "android/media/NotProvisionedException");
+ resolveDrmExceptionCtor(env, "android/media/ResourceBusyException");
+ resolveDrmExceptionCtor(env, "android/media/DeniedByServerException");
}
static void android_media_MediaDrm_native_setup(
@@ -2192,4 +2218,4 @@
int register_android_media_Drm(JNIEnv *env) {
return AndroidRuntime::registerNativeMethods(env,
"android/media/MediaDrm", gMethods, NELEM(gMethods));
-}
+}
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/values/styles.xml b/packages/CompanionDeviceManager/res/values/styles.xml
index 222877b..88f12046 100644
--- a/packages/CompanionDeviceManager/res/values/styles.xml
+++ b/packages/CompanionDeviceManager/res/values/styles.xml
@@ -102,6 +102,8 @@
<item name="android:layout_height">36dp</item>
<item name="android:textAllCaps">false</item>
<item name="android:textSize">14sp</item>
+ <item name="android:paddingLeft">6dp</item>
+ <item name="android:paddingRight">6dp</item>
<item name="android:background">@drawable/btn_negative_multiple_devices</item>
<item name="android:textAppearance">@android:style/TextAppearance.DeviceDefault.Medium</item>
</style>
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index 473d7b6..477e61d 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -61,14 +61,20 @@
import androidx.credentials.provider.PublicKeyCredentialEntry
import androidx.credentials.provider.RemoteEntry
import org.json.JSONObject
+import android.credentials.flags.Flags
import java.time.Instant
+
fun getAppLabel(
pm: PackageManager,
appPackageName: String
): String? {
return try {
- val pkgInfo = getPackageInfo(pm, appPackageName)
+ val pkgInfo = if (Flags.instantAppsEnabled()) {
+ getPackageInfo(pm, appPackageName)
+ } else {
+ pm.getPackageInfo(appPackageName, PackageManager.PackageInfoFlags.of(0))
+ }
val applicationInfo = checkNotNull(pkgInfo.applicationInfo)
applicationInfo.loadSafeLabel(
pm, 0f,
@@ -91,7 +97,14 @@
// Test data has only package name not component name.
// For test data usage only.
try {
- val pkgInfo = getPackageInfo(pm, providerFlattenedComponentName)
+ val pkgInfo = if (Flags.instantAppsEnabled()) {
+ getPackageInfo(pm, providerFlattenedComponentName)
+ } else {
+ pm.getPackageInfo(
+ providerFlattenedComponentName,
+ PackageManager.PackageInfoFlags.of(0)
+ )
+ }
val applicationInfo = checkNotNull(pkgInfo.applicationInfo)
providerLabel =
applicationInfo.loadSafeLabel(
@@ -115,7 +128,14 @@
// Added for mdoc use case where the provider may not need to register a service and
// instead only relies on the registration api.
try {
- val pkgInfo = getPackageInfo(pm, providerFlattenedComponentName)
+ val pkgInfo = if (Flags.instantAppsEnabled()) {
+ getPackageInfo(pm, providerFlattenedComponentName)
+ } else {
+ pm.getPackageInfo(
+ component.packageName,
+ PackageManager.PackageInfoFlags.of(0)
+ )
+ }
val applicationInfo = checkNotNull(pkgInfo.applicationInfo)
providerLabel =
applicationInfo.loadSafeLabel(
@@ -143,12 +163,12 @@
pm: PackageManager,
packageName: String
): PackageInfo {
- val flags = PackageManager.MATCH_INSTANT
+ val packageManagerFlags = PackageManager.MATCH_INSTANT
return pm.getPackageInfo(
packageName,
PackageManager.PackageInfoFlags.of(
- (flags).toLong())
+ (packageManagerFlags).toLong())
)
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index 2318bb9..9355517 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -27,14 +27,22 @@
import android.os.CancellationSignal
import android.os.OutcomeReceiver
import android.service.autofill.AutofillService
+import android.service.autofill.Dataset
+import android.service.autofill.Field
import android.service.autofill.FillCallback
import android.service.autofill.FillRequest
import android.service.autofill.FillResponse
+import android.service.autofill.InlinePresentation
+import android.service.autofill.Presentations
import android.service.autofill.SaveCallback
import android.service.autofill.SaveRequest
import android.service.credentials.CredentialProviderService
import android.util.Log
import android.view.autofill.AutofillId
+import org.json.JSONException
+import android.widget.inline.InlinePresentationSpec
+import androidx.autofill.inline.v1.InlineSuggestionUi
+import com.android.credentialmanager.GetFlowUtils
import org.json.JSONObject
import java.util.concurrent.Executors
@@ -49,11 +57,9 @@
private const val SYS_PROVIDER_REQ_KEY = "isSystemProviderRequired"
private const val CRED_OPTIONS_KEY = "credentialOptions"
private const val TYPE_KEY = "type"
+ private const val REQ_TYPE_KEY = "get"
}
- private val credentialManager: CredentialManager =
- getSystemService(Context.CREDENTIAL_SERVICE) as CredentialManager
-
override fun onFillRequest(
request: FillRequest,
cancellationSignal: CancellationSignal,
@@ -66,16 +72,24 @@
val getCredRequest: GetCredentialRequest? = getCredManRequest(structure)
if (getCredRequest == null) {
+ Log.i(TAG, "No credential manager request found")
callback.onFailure("No credential manager request found")
return
}
+ val credentialManager: CredentialManager =
+ getSystemService(Context.CREDENTIAL_SERVICE) as CredentialManager
val outcome = object : OutcomeReceiver<GetCandidateCredentialsResponse,
GetCandidateCredentialsException> {
override fun onResult(result: GetCandidateCredentialsResponse) {
Log.i(TAG, "getCandidateCredentials onResponse")
- val fillResponse: FillResponse? = convertToFillResponse(result, request)
- callback.onSuccess(fillResponse)
+ val fillResponse = convertToFillResponse(result, request)
+ if (fillResponse != null) {
+ callback.onSuccess(fillResponse)
+ } else {
+ Log.e(TAG, "Failed to create a FillResponse from the CredentialResponse.")
+ callback.onFailure("No dataset was created from the CredentialResponse")
+ }
}
override fun onError(error: GetCandidateCredentialsException) {
@@ -97,7 +111,74 @@
getCredResponse: GetCandidateCredentialsResponse,
filLRequest: FillRequest
): FillResponse? {
- TODO("Not yet implemented")
+ val providerList = GetFlowUtils.toProviderList(
+ getCredResponse.candidateProviderDataList,
+ this@CredentialAutofillService)
+ var totalEntryCount = 0
+ providerList.forEach { provider ->
+ totalEntryCount += provider.credentialEntryList.size
+ }
+ val inlineSuggestionsRequest = filLRequest.inlineSuggestionsRequest
+ val inlineMaxSuggestedCount = inlineSuggestionsRequest?.maxSuggestionCount ?: 0
+ val inlinePresentationSpecs = inlineSuggestionsRequest?.inlinePresentationSpecs
+ val inlinePresentationSpecsCount = inlinePresentationSpecs?.size ?: 0
+ var maxItemCount = totalEntryCount
+ if (inlineMaxSuggestedCount > 0) {
+ maxItemCount = maxItemCount.coerceAtMost(inlineMaxSuggestedCount)
+ }
+ var i = 0
+ val fillResponseBuilder = FillResponse.Builder()
+ var emptyFillResponse = true
+ providerList.forEach {provider ->
+ // TODO(b/299321128): Before iterating the list, sort the list so that
+ // the relevant entries don't get truncated
+ provider.credentialEntryList.forEach entryLoop@ {entry ->
+ val autofillId: AutofillId? = entry.fillInIntent?.getParcelableExtra(
+ CredentialProviderService.EXTRA_AUTOFILL_ID,
+ AutofillId::class.java)
+ val pendingIntent = entry.pendingIntent
+ if (autofillId == null || pendingIntent == null) {
+ return@entryLoop
+ }
+ var inlinePresentation: InlinePresentation? = null
+ // Create inline presentation
+ if (inlinePresentationSpecs != null && i < maxItemCount) {
+ val spec: InlinePresentationSpec
+ if (i < inlinePresentationSpecsCount) {
+ spec = inlinePresentationSpecs[i]
+ } else {
+ spec = inlinePresentationSpecs[inlinePresentationSpecsCount - 1]
+ }
+ val sliceBuilder = InlineSuggestionUi
+ .newContentBuilder(pendingIntent)
+ .setTitle(entry.userName)
+ inlinePresentation = InlinePresentation(
+ sliceBuilder.build().slice, spec, /* pinned= */ false)
+ }
+ i++
+
+ val dataSetBuilder = Dataset.Builder()
+ val presentationBuilder = Presentations.Builder()
+ if (inlinePresentation != null) {
+ presentationBuilder.setInlinePresentation(inlinePresentation)
+ }
+ fillResponseBuilder.addDataset(
+ dataSetBuilder
+ .setField(
+ autofillId,
+ Field.Builder().setPresentations(
+ presentationBuilder.build())
+ .build())
+ .setAuthentication(entry.pendingIntent.intentSender)
+ .setAuthenticationExtras(entry.fillInIntent.extras)
+ .build())
+ emptyFillResponse = false
+ }
+ }
+ if (emptyFillResponse) {
+ return null
+ }
+ return fillResponseBuilder.build()
}
override fun onSaveRequest(request: SaveRequest, callback: SaveCallback) {
@@ -165,8 +246,12 @@
val credentialOptions: MutableList<CredentialOption> = mutableListOf()
for (credentialHint in credentialHints) {
- convertJsonToCredentialOption(credentialHint, autofillId)
- .let { credentialOptions.addAll(it) }
+ try {
+ convertJsonToCredentialOption(credentialHint, autofillId)
+ .let { credentialOptions.addAll(it) }
+ } catch (e: JSONException) {
+ Log.i(TAG, "Exception while parsing response: " + e.message)
+ }
}
return credentialOptions
}
@@ -178,7 +263,8 @@
val credentialOptions: MutableList<CredentialOption> = mutableListOf()
val json = JSONObject(jsonString)
- val options = json.getJSONArray(CRED_OPTIONS_KEY)
+ val jsonGet = json.getJSONObject(REQ_TYPE_KEY)
+ val options = jsonGet.getJSONArray(CRED_OPTIONS_KEY)
for (i in 0 until options.length()) {
val option = options.getJSONObject(i)
val candidateBundle = convertJsonToBundle(option.getJSONObject(CANDIDATE_DATA_KEY))
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index d97fb54..c5ae4a3 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -381,7 +381,7 @@
final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID,
-1 /* defaultValue */);
final SessionInfo info = mInstaller.getSessionInfo(sessionId);
- String resolvedPath = info.getResolvedBaseApkPath();
+ String resolvedPath = info != null ? info.getResolvedBaseApkPath() : null;
if (info == null || !info.isSealed() || resolvedPath == null) {
Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring");
finish();
@@ -609,7 +609,7 @@
CharSequence label = mPm.getApplicationLabel(mPkgInfo.applicationInfo);
if (mLocalLOGV) Log.i(TAG, "creating snippet for " + label);
mAppSnippet = new PackageUtil.AppSnippet(label,
- mPm.getApplicationIcon(mPkgInfo.applicationInfo));
+ mPm.getApplicationIcon(mPkgInfo.applicationInfo), getBaseContext());
} break;
case ContentResolver.SCHEME_FILE: {
@@ -647,7 +647,7 @@
mPkgInfo = generateStubPackageInfo(info.getAppPackageName());
mAppSnippet = new PackageUtil.AppSnippet(info.getAppLabel(),
info.getAppIcon() != null ? new BitmapDrawable(getResources(), info.getAppIcon())
- : getPackageManager().getDefaultActivityIcon());
+ : getPackageManager().getDefaultActivityIcon(), getBaseContext());
return true;
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
index 334886f..8de12d0 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
@@ -18,6 +18,7 @@
package com.android.packageinstaller;
import android.app.Activity;
+import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
@@ -135,15 +136,20 @@
static final class AppSnippet implements Parcelable {
@NonNull public CharSequence label;
@Nullable public Drawable icon;
- public AppSnippet(@NonNull CharSequence label, @Nullable Drawable icon) {
+ public int iconSize;
+
+ AppSnippet(@NonNull CharSequence label, @Nullable Drawable icon, Context context) {
this.label = label;
this.icon = icon;
+ final ActivityManager am = context.getSystemService(ActivityManager.class);
+ this.iconSize = am.getLauncherLargeIconSize();
}
private AppSnippet(Parcel in) {
label = in.readString();
Bitmap bmp = in.readParcelable(getClass().getClassLoader(), Bitmap.class);
icon = new BitmapDrawable(Resources.getSystem(), bmp);
+ iconSize = in.readInt();
}
@Override
@@ -161,11 +167,12 @@
dest.writeString(label.toString());
Bitmap bmp = getBitmapFromDrawable(icon);
dest.writeParcelable(bmp, 0);
+ dest.writeInt(iconSize);
}
private Bitmap getBitmapFromDrawable(Drawable drawable) {
// Create an empty bitmap with the dimensions of our drawable
- final Bitmap bmp = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
+ Bitmap bmp = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight(),
Bitmap.Config.ARGB_8888);
// Associate it with a canvas. This canvas will draw the icon on the bitmap
@@ -174,6 +181,10 @@
// bitmap held within
drawable.draw(canvas);
+ // Scale it down if the icon is too large
+ if ((bmp.getWidth() > iconSize * 2) || (bmp.getHeight() > iconSize * 2)) {
+ bmp = Bitmap.createScaledBitmap(bmp, iconSize, iconSize, true);
+ }
return bmp;
}
@@ -241,7 +252,7 @@
} catch (OutOfMemoryError e) {
Log.i(LOG_TAG, "Could not load app icon", e);
}
- return new PackageUtil.AppSnippet(label, icon);
+ return new PackageUtil.AppSnippet(label, icon, pContext);
}
private static String findFilePath(File[] files, String postfix) {
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 2d231f2..8964ada 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -11,29 +11,42 @@
name: "SettingsLib",
static_libs: [
- "androidx.annotation_annotation",
- "androidx.appcompat_appcompat",
- "androidx.coordinatorlayout_coordinatorlayout",
- "androidx.core_core",
- "androidx.fragment_fragment",
- "androidx.lifecycle_lifecycle-runtime",
- "androidx.loader_loader",
"androidx.localbroadcastmanager_localbroadcastmanager",
- "androidx.preference_preference",
- "androidx.recyclerview_recyclerview",
- "com.google.android.material_material",
- "iconloader",
+ "androidx.room_room-runtime",
+ "zxing-core",
"WifiTrackerLibRes",
+ "iconloader",
+ "setupdesign",
+
+ "SettingsLibActionBarShadow",
+ "SettingsLibActionButtonsPreference",
+ "SettingsLibAdaptiveIcon",
+ "SettingsLibAppPreference",
+ "SettingsLibBannerMessagePreference",
+ "SettingsLibBarChartPreference",
+ "SettingsLibButtonPreference",
+ "SettingsLibCollapsingToolbarBaseActivity",
"SettingsLibDeviceStateRotationLock",
"SettingsLibDisplayUtils",
"SettingsLibEmergencyNumber",
+ "SettingsLibEntityHeaderWidgets",
+ "SettingsLibFooterPreference",
+ "SettingsLibHelpUtils",
+ "SettingsLibIllustrationPreference",
+ "SettingsLibLayoutPreference",
+ "SettingsLibMainSwitchPreference",
+ "SettingsLibProfileSelector",
+ "SettingsLibProgressBar",
+ "SettingsLibRestrictedLockUtils",
"SettingsLibSearchWidget",
+ "SettingsLibSelectorWithWidgetPreference",
+ "SettingsLibSettingsSpinner",
+ "SettingsLibSettingsTransition",
+ "SettingsLibTopIntroPreference",
+ "SettingsLibTwoTargetPreference",
+ "SettingsLibUsageProgressBarPreference",
"SettingsLibUtils",
- "SettingsLibWidget",
- "setupdesign",
- "zxing-core-1.7",
- "androidx.room_room-runtime",
"settingslib_flags_lib",
],
@@ -47,56 +60,10 @@
],
}
-// Group all the libraries with namespace "com.android.settingslib.widget", to allow SettingsLib to
-// set use_resource_processor = true.
-// We can remove SettingsLibWidget when all these libraries have its own namespace.
-android_library {
- name: "SettingsLibWidget",
- visibility: ["//visibility:private"],
- manifest: "AndroidManifest-SettingsLibWidget.xml",
- static_libs: [
- "SettingsLibActionBarShadow",
- "SettingsLibActionButtonsPreference",
- "SettingsLibAdaptiveIcon",
- "SettingsLibAppPreference",
- "SettingsLibBannerMessagePreference",
- "SettingsLibBarChartPreference",
- "SettingsLibButtonPreference",
- "SettingsLibCollapsingToolbarBaseActivity",
- "SettingsLibEntityHeaderWidgets",
- "SettingsLibFooterPreference",
- "SettingsLibHelpUtils",
- "SettingsLibIllustrationPreference",
- "SettingsLibLayoutPreference",
- "SettingsLibMainSwitchPreference",
- "SettingsLibProfileSelector",
- "SettingsLibProgressBar",
- "SettingsLibRestrictedLockUtils",
- "SettingsLibSelectorWithWidgetPreference",
- "SettingsLibSettingsSpinner",
- "SettingsLibSettingsTransition",
- "SettingsLibTopIntroPreference",
- "SettingsLibTwoTargetPreference",
- "SettingsLibUsageProgressBarPreference",
- ],
-
- resource_dirs: [],
-}
-
// NOTE: Keep this module in sync with ./common.mk
java_defaults {
name: "SettingsLibDefaults",
static_libs: [
- "androidx.annotation_annotation",
- "androidx.appcompat_appcompat",
- "androidx.coordinatorlayout_coordinatorlayout",
- "androidx.core_core",
- "androidx.fragment_fragment",
- "androidx.lifecycle_lifecycle-runtime",
- "androidx.loader_loader",
- "androidx.localbroadcastmanager_localbroadcastmanager",
- "androidx.preference_preference",
- "androidx.recyclerview_recyclerview",
"SettingsLib",
],
}
diff --git a/packages/SettingsLib/AndroidManifest-SettingsLibWidget.xml b/packages/SettingsLib/AndroidManifest-SettingsLibWidget.xml
deleted file mode 100644
index 38a7d6a..0000000
--- a/packages/SettingsLib/AndroidManifest-SettingsLibWidget.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2023 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest package="com.android.settingslib.widget" />
diff --git a/packages/SettingsLib/MainSwitchPreference/Android.bp b/packages/SettingsLib/MainSwitchPreference/Android.bp
index 33aa985..4871ef3 100644
--- a/packages/SettingsLib/MainSwitchPreference/Android.bp
+++ b/packages/SettingsLib/MainSwitchPreference/Android.bp
@@ -9,6 +9,7 @@
android_library {
name: "SettingsLibMainSwitchPreference",
+ use_resource_processor: true,
srcs: ["src/**/*.java"],
resource_dirs: ["res"],
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
index 56b3eac..6001155 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
@@ -31,12 +31,11 @@
import androidx.annotation.ColorInt;
import com.android.settingslib.utils.BuildCompatUtils;
+import com.android.settingslib.widget.mainswitch.R;
import java.util.ArrayList;
import java.util.List;
-import com.android.settingslib.widget.mainswitch.R;
-
/**
* MainSwitchBar is a View with a customized Switch.
* This component is used as the main switch of the page
@@ -77,7 +76,7 @@
final TypedArray a = context.obtainStyledAttributes(
new int[]{android.R.attr.colorAccent});
mBackgroundActivatedColor = a.getColor(0, 0);
- mBackgroundColor = context.getColor(R.color.material_grey_600);
+ mBackgroundColor = context.getColor(androidx.appcompat.R.color.material_grey_600);
a.recycle();
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt
index 0552c40..1ad075c 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt
@@ -31,7 +31,6 @@
import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Delete
-import androidx.compose.material.icons.outlined.Launch
import androidx.compose.material.icons.outlined.WarningAmber
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.FilledTonalButton
@@ -52,6 +51,7 @@
import com.android.settingslib.spa.framework.theme.SettingsShape
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.framework.theme.divider
+import androidx.compose.material.icons.automirrored.outlined.Launch
data class ActionButton(
val text: String,
@@ -101,7 +101,9 @@
modifier = Modifier.size(SettingsDimension.itemIconSize),
)
Box(
- modifier = Modifier.padding(top = 4.dp).fillMaxHeight(),
+ modifier = Modifier
+ .padding(top = 4.dp)
+ .fillMaxHeight(),
contentAlignment = Alignment.Center,
) {
Text(
@@ -129,7 +131,7 @@
SettingsTheme {
ActionButtons(
listOf(
- ActionButton(text = "Open", imageVector = Icons.Outlined.Launch) {},
+ ActionButton(text = "Open", imageVector = Icons.AutoMirrored.Outlined.Launch) {},
ActionButton(text = "Uninstall", imageVector = Icons.Outlined.Delete) {},
ActionButton(text = "Force stop", imageVector = Icons.Outlined.WarningAmber) {},
)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt
index 62189dc..6ef4590 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt
@@ -18,7 +18,6 @@
import androidx.appcompat.R
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.ArrowBack
import androidx.compose.material.icons.outlined.Clear
import androidx.compose.material.icons.outlined.FindInPage
import androidx.compose.material3.Icon
@@ -31,6 +30,7 @@
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.LayoutDirection
import com.android.settingslib.spa.framework.compose.LocalNavController
+import androidx.compose.material.icons.automirrored.outlined.ArrowBack
/** Action that navigates back to last page. */
@Composable
@@ -53,7 +53,7 @@
private fun BackAction(contentDescription: String, onClick: () -> Unit) {
IconButton(onClick) {
Icon(
- imageVector = Icons.Outlined.ArrowBack,
+ imageVector = Icons.AutoMirrored.Outlined.ArrowBack,
contentDescription = contentDescription,
modifier = Modifier.autoMirrored(),
)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt
index aa148b0..9f7f040 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt
@@ -21,7 +21,7 @@
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
-import androidx.compose.material3.TabRow
+import androidx.compose.material3.PrimaryTabRow
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
@@ -43,7 +43,7 @@
val coroutineScope = rememberCoroutineScope()
val pagerState = rememberPagerState { titles.size }
- TabRow(
+ PrimaryTabRow(
selectedTabIndex = pagerState.currentPage,
modifier = Modifier.padding(horizontal = SettingsDimension.itemPaddingEnd),
containerColor = Color.Transparent,
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/button/ActionButtonsTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/button/ActionButtonsTest.kt
index f59b0de..8d9bac6 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/button/ActionButtonsTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/button/ActionButtonsTest.kt
@@ -17,8 +17,8 @@
package com.android.settingslib.spa.widget.button
import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.outlined.Launch
import androidx.compose.material.icons.outlined.Close
-import androidx.compose.material.icons.outlined.Launch
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
@@ -43,7 +43,10 @@
composeTestRule.setContent {
ActionButtons(
listOf(
- ActionButton(text = "Open", imageVector = Icons.Outlined.Launch) {},
+ ActionButton(
+ text = "Open",
+ imageVector = Icons.AutoMirrored.Outlined.Launch
+ ) {},
)
)
}
@@ -57,7 +60,7 @@
composeTestRule.setContent {
ActionButtons(
listOf(
- ActionButton(text = "Open", imageVector = Icons.Outlined.Launch) {
+ ActionButton(text = "Open", imageVector = Icons.AutoMirrored.Outlined.Launch) {
clicked = true
},
)
@@ -74,7 +77,10 @@
composeTestRule.setContent {
ActionButtons(
listOf(
- ActionButton(text = "Open", imageVector = Icons.Outlined.Launch) {},
+ ActionButton(
+ text = "Open",
+ imageVector = Icons.AutoMirrored.Outlined.Launch
+ ) {},
ActionButton(text = "Close", imageVector = Icons.Outlined.Close) {},
)
)
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/EnterpriseRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/EnterpriseRepository.kt
index dfd8f6b..3525037 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/EnterpriseRepository.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/EnterpriseRepository.kt
@@ -18,8 +18,10 @@
import android.app.admin.DevicePolicyManager
import android.app.admin.DevicePolicyResources.Strings.Settings.PERSONAL_CATEGORY_HEADER
+import android.app.admin.DevicePolicyResources.Strings.Settings.PRIVATE_CATEGORY_HEADER
import android.app.admin.DevicePolicyResources.Strings.Settings.WORK_CATEGORY_HEADER
import android.content.Context
+import android.content.pm.UserInfo
import com.android.settingslib.R
class EnterpriseRepository(private val context: Context) {
@@ -30,8 +32,10 @@
fun getEnterpriseString(updatableStringId: String, resId: Int): String =
checkNotNull(resources.getString(updatableStringId) { context.getString(resId) })
- fun getProfileTitle(isManagedProfile: Boolean): String = if (isManagedProfile) {
+ fun getProfileTitle(userInfo: UserInfo): String = if (userInfo.isManagedProfile) {
getEnterpriseString(WORK_CATEGORY_HEADER, R.string.category_work)
+ } else if (userInfo.isPrivateProfile) {
+ getEnterpriseString(PRIVATE_CATEGORY_HEADER, R.string.category_private)
} else {
getEnterpriseString(PERSONAL_CATEGORY_HEADER, R.string.category_personal)
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt
index 6a76c93..5447f21 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt
@@ -45,7 +45,7 @@
val enterpriseRepository = EnterpriseRepository(context)
userGroups.map { userGroup ->
enterpriseRepository.getProfileTitle(
- isManagedProfile = userGroup.userInfos.first().isManagedProfile,
+ userGroup.userInfos.first(),
)
}
}
diff --git a/packages/SettingsLib/common.mk b/packages/SettingsLib/common.mk
index d959656..431fd44 100644
--- a/packages/SettingsLib/common.mk
+++ b/packages/SettingsLib/common.mk
@@ -18,18 +18,5 @@
# to the corresponding module.
# NOTE: keep this file and ./Android.bp in sync.
-LOCAL_STATIC_JAVA_LIBRARIES += \
- androidx.annotation_annotation
-
LOCAL_STATIC_ANDROID_LIBRARIES += \
- androidx.appcompat_appcompat \
- androidx.coordinatorlayout_coordinatorlayout \
- androidx.core_core \
- androidx.fragment_fragment \
- androidx.lifecycle_lifecycle-runtime \
- androidx.loader_loader \
- androidx.localbroadcastmanager_localbroadcastmanager \
- androidx.preference_preference \
- androidx.recyclerview_recyclerview \
SettingsLib
-
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 49bd9d9..9687674 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -518,6 +518,8 @@
<string name="category_personal">Personal</string>
<!-- Header for items under the work user [CHAR LIMIT=30] -->
<string name="category_work">Work</string>
+ <!-- Header for items under the private profile user [CHAR LIMIT=30] -->
+ <string name="category_private">Private</string>
<!-- Header for items under the clone user [CHAR LIMIT=30] -->
<string name="category_clone">Clone</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 412a342..1bb00b3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -67,8 +67,7 @@
static final String STORAGE_MANAGER_ENABLED_PROPERTY =
"ro.storage_manager.enabled";
- @VisibleForTesting
- static final String INCOMPATIBLE_CHARGER_WARNING_DISABLED =
+ public static final String INCOMPATIBLE_CHARGER_WARNING_DISABLED =
"incompatible_charger_warning_disabled";
private static Signature[] sSystemSignature;
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSimStatusImeiInfoPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSimStatusImeiInfoPreferenceController.java
deleted file mode 100644
index a78440c..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSimStatusImeiInfoPreferenceController.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.deviceinfo;
-
-import android.content.Context;
-import android.os.UserManager;
-
-import com.android.settingslib.Utils;
-import com.android.settingslib.core.AbstractPreferenceController;
-
-public abstract class AbstractSimStatusImeiInfoPreferenceController
- extends AbstractPreferenceController {
- public AbstractSimStatusImeiInfoPreferenceController(Context context) {
- super(context);
- }
-
- @Override
- public boolean isAvailable() {
- return mContext.getSystemService(UserManager.class).isAdminUser()
- && !Utils.isWifiOnly(mContext);
- }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java
index 7f1f3f6..2032328 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java
@@ -110,7 +110,8 @@
}
/**
- * Determine whether the device is plugged in wireless. */
+ * Determine whether the device is plugged in wireless.
+ */
public boolean isPluggedInWireless() {
return plugged == BatteryManager.BATTERY_PLUGGED_WIRELESS;
}
@@ -185,6 +186,22 @@
return status == BATTERY_STATUS_FULL || level >= 100;
}
+ /**
+ * Whether or not the device is charged. Note that some devices never return 100% for battery
+ * level, so this allows either battery level or status to determine if the battery is charged.
+ *
+ * @param status the value from extra {@link BatteryManager.EXTRA_STATUS} of
+ * {@link Intent.ACTION_BATTERY_CHANGED} intent
+ * @param level the value from extra {@link BatteryManager.EXTRA_LEVEL} of
+ * {@link Intent.ACTION_BATTERY_CHANGED} intent
+ * @param scale the value from extra {@link BatteryManager.EXTRA_SCALE} of
+ * {@link Intent.ACTION_BATTERY_CHANGED} intent
+ */
+ public static boolean isCharged(int status, int level, int scale) {
+ var batteryLevel = getBatteryLevel(level, scale);
+ return isCharged(status, batteryLevel);
+ }
+
/** Gets the battery level from the intent. */
public static int getBatteryLevel(Intent batteryChangedIntent) {
if (batteryChangedIntent == null) {
@@ -193,6 +210,14 @@
final int level =
batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, BATTERY_LEVEL_UNKNOWN);
final int scale = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 0);
+ return getBatteryLevel(level, scale);
+ }
+
+ /**
+ * Gets the battery level from the value of {@link Intent.BATTERY_CHANGED_INTENT}'s EXTRA_LEVEL
+ * and EXTRA_SCALE.
+ */
+ public static int getBatteryLevel(int level, int scale) {
return scale == 0
? BATTERY_LEVEL_UNKNOWN
: Math.round((level / (float) scale) * 100f);
@@ -253,11 +278,22 @@
*
* @param batteryChangedIntent the ACTION_BATTERY_CHANGED intent
* @return {@code true} if the battery level is less or equal to {@link
- * SEVERE_LOW_BATTERY_THRESHOLD}
+ * SEVERE_LOW_BATTERY_THRESHOLD}
*/
public static boolean isSevereLowBattery(Intent batteryChangedIntent) {
- int level = getBatteryLevel(batteryChangedIntent);
- return level <= SEVERE_LOW_BATTERY_THRESHOLD;
+ int batteryLevel = getBatteryLevel(batteryChangedIntent);
+ return isSevereLowBattery(batteryLevel);
+ }
+
+ /**
+ * Whether the battery is severe low or not.
+ *
+ * @param batteryLevel the value of battery level
+ * @return {@code true} if the battery level is less or equal to {@link
+ * SEVERE_LOW_BATTERY_THRESHOLD}
+ */
+ public static boolean isSevereLowBattery(int batteryLevel) {
+ return batteryLevel <= SEVERE_LOW_BATTERY_THRESHOLD;
}
/**
@@ -265,11 +301,21 @@
*
* @param batteryChangedIntent the ACTION_BATTERY_CHANGED intent
* @return {@code true} if the battery level is less or equal to {@link
- * EXTREME_LOW_BATTERY_THRESHOLD}
+ * EXTREME_LOW_BATTERY_THRESHOLD}
*/
public static boolean isExtremeLowBattery(Intent batteryChangedIntent) {
int level = getBatteryLevel(batteryChangedIntent);
- return level <= EXTREME_LOW_BATTERY_THRESHOLD;
+ return isExtremeLowBattery(level);
+ }
+
+ /**
+ * Whether the battery is extreme low or not.
+ *
+ * @return {@code true} if the {@code batteryLevel} is less or equal to
+ * {@link EXTREME_LOW_BATTERY_THRESHOLD}
+ */
+ public static boolean isExtremeLowBattery(int batteryLevel) {
+ return batteryLevel <= EXTREME_LOW_BATTERY_THRESHOLD;
}
/**
@@ -277,7 +323,7 @@
*
* @param batteryChangedIntent the ACTION_BATTERY_CHANGED intent
* @return {@code true} if the battery defender is enabled. It could be dock defend, dwell
- * defend, or temp defend
+ * defend, or temp defend
*/
public static boolean isBatteryDefender(Intent batteryChangedIntent) {
int chargingStatus =
@@ -298,9 +344,8 @@
}
/**
- * Gets the max charging current and max charging voltage form {@link
- * Intent.ACTION_BATTERY_CHANGED} and calculates the charging speed based on the {@link
- * R.integer.config_chargingSlowlyThreshold} and {@link R.integer.config_chargingFastThreshold}.
+ * Calculates the charging speed based on the {@link R.integer.config_chargingSlowlyThreshold}
+ * and {@link R.integer.config_chargingFastThreshold}.
*
* @param context the application context
* @param batteryChangedIntent the intent from {@link Intent.ACTION_BATTERY_CHANGED}
@@ -308,7 +353,29 @@
* CHARGING_SLOWLY} or {@link CHARGING_UNKNOWN}
*/
public static int getChargingSpeed(Context context, Intent batteryChangedIntent) {
- final int maxChargingMicroWatt = calculateMaxChargingMicroWatt(batteryChangedIntent);
+ final int maxChargingMicroCurrent =
+ batteryChangedIntent.getIntExtra(EXTRA_MAX_CHARGING_CURRENT, -1);
+ int maxChargingMicroVolt = batteryChangedIntent.getIntExtra(EXTRA_MAX_CHARGING_VOLTAGE, -1);
+
+ return calculateChargingSpeed(context, maxChargingMicroCurrent, maxChargingMicroVolt);
+ }
+
+ /**
+ * Calculates the charging speed based on the {@link R.integer.config_chargingSlowlyThreshold}
+ * and {@link R.integer.config_chargingFastThreshold}.
+ *
+ * @param maxChargingMicroCurrent the max charging micro current that is retrieved form the
+ * extra of {@link Intent.Action_BATTERY_CHANGED}
+ * @param maxChargingMicroVolt the max charging micro voltage that is retrieved form the extra
+ * of {@link Intent.Action_BATTERY_CHANGED}
+ * @return the charging speed. {@link CHARGING_REGULAR}, {@link CHARGING_FAST}, {@link
+ * CHARGING_SLOWLY} or {@link CHARGING_UNKNOWN}
+ */
+ public static int calculateChargingSpeed(
+ Context context, int maxChargingMicroCurrent, int maxChargingMicroVolt) {
+ final int maxChargingMicroWatt =
+ calculateMaxChargingMicroWatt(maxChargingMicroCurrent, maxChargingMicroVolt);
+
if (maxChargingMicroWatt <= 0) {
return CHARGING_UNKNOWN;
} else if (maxChargingMicroWatt
@@ -326,6 +393,12 @@
final int maxChargingMicroAmp =
batteryChangedIntent.getIntExtra(EXTRA_MAX_CHARGING_CURRENT, -1);
int maxChargingMicroVolt = batteryChangedIntent.getIntExtra(EXTRA_MAX_CHARGING_VOLTAGE, -1);
+
+ return calculateMaxChargingMicroWatt(maxChargingMicroAmp, maxChargingMicroVolt);
+ }
+
+ private static int calculateMaxChargingMicroWatt(int maxChargingMicroAmp,
+ int maxChargingMicroVolt) {
if (maxChargingMicroVolt <= 0) {
maxChargingMicroVolt = DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index 41afc7b..a63bbdf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -26,6 +26,7 @@
import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_TRANSFER;
+import android.annotation.NonNull;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.media.MediaRoute2Info;
@@ -51,6 +52,34 @@
private final DeviceIconUtil mDeviceIconUtil;
+ /** Returns the device name for the given {@code routeInfo}. */
+ public static String getSystemRouteNameFromType(
+ @NonNull Context context, @NonNull MediaRoute2Info routeInfo) {
+ CharSequence name;
+ switch (routeInfo.getType()) {
+ case TYPE_WIRED_HEADSET:
+ case TYPE_WIRED_HEADPHONES:
+ case TYPE_USB_DEVICE:
+ case TYPE_USB_HEADSET:
+ case TYPE_USB_ACCESSORY:
+ name = context.getString(R.string.media_transfer_wired_usb_device_name);
+ break;
+ case TYPE_DOCK:
+ name = context.getString(R.string.media_transfer_dock_speaker_device_name);
+ break;
+ case TYPE_BUILTIN_SPEAKER:
+ name = context.getString(R.string.media_transfer_this_device_name);
+ break;
+ case TYPE_HDMI:
+ name = context.getString(R.string.media_transfer_external_device_name);
+ break;
+ default:
+ name = context.getString(R.string.media_transfer_default_device_name);
+ break;
+ }
+ return name.toString();
+ }
+
PhoneMediaDevice(Context context, MediaRoute2Info info, String packageName) {
this(context, info, packageName, null);
}
@@ -69,29 +98,7 @@
@SuppressWarnings("NewApi")
@Override
public String getName() {
- CharSequence name;
- switch (mRouteInfo.getType()) {
- case TYPE_WIRED_HEADSET:
- case TYPE_WIRED_HEADPHONES:
- case TYPE_USB_DEVICE:
- case TYPE_USB_HEADSET:
- case TYPE_USB_ACCESSORY:
- name = mContext.getString(R.string.media_transfer_wired_usb_device_name);
- break;
- case TYPE_DOCK:
- name = mContext.getString(R.string.media_transfer_dock_speaker_device_name);
- break;
- case TYPE_BUILTIN_SPEAKER:
- name = mContext.getString(R.string.media_transfer_this_device_name);
- break;
- case TYPE_HDMI:
- name = mContext.getString(R.string.media_transfer_external_device_name);
- break;
- default:
- name = mContext.getString(R.string.media_transfer_default_device_name);
- break;
- }
- return name.toString();
+ return getSystemRouteNameFromType(mContext, mRouteInfo);
}
@Override
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayoutTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayoutTest.java
index 2b1e808..507dcbc 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayoutTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayoutTest.java
@@ -55,8 +55,7 @@
@Test
public void onCreate_userAddedChildViewsBeMovedToContentFrame() {
CollapsingCoordinatorLayout layout = mActivity.getCollapsingCoordinatorLayout();
- View contentFrameView =
- layout.findViewById(com.android.settingslib.widget.R.id.content_frame);
+ View contentFrameView = layout.findViewById(R.id.content_frame);
TextView textView = contentFrameView.findViewById(com.android.settingslib.robotests.R.id.text_hello_world);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java
deleted file mode 100644
index 52d243a..0000000
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.deviceinfo;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.robolectric.shadow.api.Shadow.extract;
-
-import android.os.UserManager;
-import android.telephony.TelephonyManager;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(shadows = {SimStatusImeiInfoPreferenceControllerTest.ShadowUserManager.class,
- SimStatusImeiInfoPreferenceControllerTest.ShadowTelephonyManager.class})
-public class SimStatusImeiInfoPreferenceControllerTest {
-
- private AbstractSimStatusImeiInfoPreferenceController mController;
-
- @Before
- public void setUp() {
- mController = new AbstractSimStatusImeiInfoPreferenceController(
- RuntimeEnvironment.application) {
- @Override
- public String getPreferenceKey() {
- return null;
- }
- };
- }
-
- @Test
- public void testIsAvailable_isAdminAndHasMobile_shouldReturnTrue() {
- ShadowUserManager userManager =
- extract(RuntimeEnvironment.application.getSystemService(UserManager.class));
- userManager.setIsAdminUser(true);
- ShadowTelephonyManager telephonyManager =
- extract(RuntimeEnvironment.application.getSystemService(TelephonyManager.class));
- telephonyManager.setDataCapable(true);
-
- assertThat(mController.isAvailable()).isTrue();
- }
-
- @Test
- public void testIsAvailable_isAdminButNoMobile_shouldReturnFalse() {
- ShadowUserManager userManager =
- extract(RuntimeEnvironment.application.getSystemService(UserManager.class));
- userManager.setIsAdminUser(true);
- ShadowTelephonyManager telephonyManager =
- extract(RuntimeEnvironment.application.getSystemService(TelephonyManager.class));
- telephonyManager.setDataCapable(false);
-
- assertThat(mController.isAvailable()).isFalse();
- }
-
- @Test
- public void testIsAvailable_isNotAdmin_shouldReturnFalse() {
- ShadowUserManager userManager =
- extract(RuntimeEnvironment.application.getSystemService(UserManager.class));
- userManager.setIsAdminUser(false);
-
- assertThat(mController.isAvailable()).isFalse();
- }
-
- @Implements(UserManager.class)
- public static class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager {
-
- private boolean mAdminUser;
-
- public void setIsAdminUser(boolean isAdminUser) {
- mAdminUser = isAdminUser;
- }
-
- @Implementation
- public boolean isAdminUser() {
- return mAdminUser;
- }
- }
-
- @Implements(TelephonyManager.class)
- public static class ShadowTelephonyManager
- extends org.robolectric.shadows.ShadowTelephonyManager {
- private boolean mDataCapable = false;
- private void setDataCapable(boolean capable) {
- mDataCapable = capable;
- }
-
- @Implementation
- public boolean isDataCapable() {
- return mDataCapable;
- }
- }
-}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java
index 6195d75..71545b7 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java
@@ -36,10 +36,10 @@
import android.graphics.drawable.ShapeDrawable;
import android.os.Bundle;
-import com.android.settingslib.widget.adaptiveicon.R;
import com.android.settingslib.drawer.ActivityTile;
import com.android.settingslib.drawer.CategoryKey;
import com.android.settingslib.drawer.Tile;
+import com.android.settingslib.widget.adaptiveicon.R;
import org.junit.Before;
import org.junit.Test;
@@ -105,15 +105,15 @@
icon.setBackgroundColor(mContext, tile);
- assertThat(icon.mBackgroundColor).isEqualTo(mContext.getColor(
- com.android.settingslib.widget.R.color.homepage_generic_icon_background));
+ assertThat(icon.mBackgroundColor).isEqualTo(
+ mContext.getColor(R.color.homepage_generic_icon_background));
}
@Test
public void onBindTile_externalTileWithBackgroundColorHint_shouldUpdateIcon() {
final Tile tile = spy(new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON_BACKGROUND_HINT,
- com.android.settingslib.widget.R.color.bt_outline_color);
+ R.color.bt_outline_color);
doReturn(Icon.createWithResource(mContext, com.android.settingslib.R.drawable.ic_system_update))
.when(tile).getIcon(mContext);
@@ -121,8 +121,7 @@
new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK));
icon.setBackgroundColor(mContext, tile);
- assertThat(icon.mBackgroundColor).isEqualTo(mContext.getColor(
- com.android.settingslib.widget.R.color.bt_outline_color));
+ assertThat(icon.mBackgroundColor).isEqualTo(mContext.getColor(R.color.bt_outline_color));
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
index ccbe4f0..0ce83c6 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
@@ -58,16 +58,14 @@
@Test
public void setLearnMoreText_shouldSetAsTextInLearnMore() {
final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(
- LayoutInflater.from(mContext)
- .inflate(com.android.settingslib.widget.R.layout.preference_footer, null));
+ LayoutInflater.from(mContext).inflate(R.layout.preference_footer, null));
mFooterPreference.setLearnMoreText("Custom learn more");
mFooterPreference.setLearnMoreAction(view -> { /* do nothing */ } /* listener */);
mFooterPreference.onBindViewHolder(holder);
- assertThat(((TextView) holder.findViewById(
- com.android.settingslib.widget.R.id.settingslib_learn_more)).getText().toString())
- .isEqualTo("Custom learn more");
+ TextView learnMoreView = (TextView) holder.findViewById(R.id.settingslib_learn_more);
+ assertThat(learnMoreView.getText().toString()).isEqualTo("Custom learn more");
}
@Test
@@ -95,8 +93,7 @@
@Test
public void onBindViewHolder_whenTitleIsNull_shouldNotRaiseNpe() {
PreferenceViewHolder viewHolder = spy(PreferenceViewHolder.createInstanceForTests(
- LayoutInflater.from(mContext)
- .inflate(R.layout.preference_footer, null)));
+ LayoutInflater.from(mContext).inflate(R.layout.preference_footer, null)));
when(viewHolder.findViewById(androidx.core.R.id.title)).thenReturn(null);
Throwable actualThrowable = null;
@@ -112,10 +109,8 @@
@Test
public void onBindViewHolder_whenLearnMoreIsNull_shouldNotRaiseNpe() {
PreferenceViewHolder viewHolder = spy(PreferenceViewHolder.createInstanceForTests(
- LayoutInflater.from(mContext)
- .inflate(com.android.settingslib.widget.R.layout.preference_footer, null)));
- when(viewHolder.findViewById(com.android.settingslib.widget.R.id.settingslib_learn_more))
- .thenReturn(null);
+ LayoutInflater.from(mContext).inflate(R.layout.preference_footer, null)));
+ when(viewHolder.findViewById(R.id.settingslib_learn_more)).thenReturn(null);
Throwable actualThrowable = null;
try {
@@ -130,8 +125,7 @@
@Test
public void onBindViewHolder_whenIconFrameIsNull_shouldNotRaiseNpe() {
PreferenceViewHolder viewHolder = spy(PreferenceViewHolder.createInstanceForTests(
- LayoutInflater.from(mContext)
- .inflate(com.android.settingslib.widget.R.layout.preference_footer, null)));
+ LayoutInflater.from(mContext).inflate(R.layout.preference_footer, null)));
when(viewHolder.findViewById(R.id.icon_frame)).thenReturn(null);
Throwable actualThrowable = null;
diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowActivityManager.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowActivityManager.java
deleted file mode 100644
index 0b9ba8d..0000000
--- a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowActivityManager.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.testutils.shadow;
-
-import static android.os.Build.VERSION_CODES.O;
-
-import android.app.ActivityManager;
-import android.app.IActivityManager;
-
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.annotation.Resetter;
-import org.robolectric.shadow.api.Shadow;
-import org.robolectric.util.ReflectionHelpers;
-
-@Implements(ActivityManager.class)
-public class ShadowActivityManager {
- private static int sCurrentUserId = 0;
- private static int sUserSwitchedTo = -1;
-
- @Resetter
- public static void reset() {
- sCurrentUserId = 0;
- sUserSwitchedTo = 0;
- }
-
- @Implementation
- protected static int getCurrentUser() {
- return sCurrentUserId;
- }
-
- @Implementation
- protected boolean switchUser(int userId) {
- sUserSwitchedTo = userId;
- return true;
- }
-
- @Implementation(minSdk = O)
- protected static IActivityManager getService() {
- return ReflectionHelpers.createNullProxy(IActivityManager.class);
- }
-
- public boolean getSwitchUserCalled() {
- return sUserSwitchedTo != -1;
- }
-
- public int getUserSwitchedTo() {
- return sUserSwitchedTo;
- }
-
- public static void setCurrentUser(int userId) {
- sCurrentUserId = userId;
- }
-
- public static ShadowActivityManager getShadow() {
- return (ShadowActivityManager) Shadow.extract(
- RuntimeEnvironment.application.getSystemService(ActivityManager.class));
- }
-}
diff --git a/packages/SettingsProvider/OWNERS b/packages/SettingsProvider/OWNERS
index 5ade971..86ae581 100644
--- a/packages/SettingsProvider/OWNERS
+++ b/packages/SettingsProvider/OWNERS
@@ -1,5 +1 @@
-hackbod@android.com
-hackbod@google.com
-narayan@google.com
-svetoslavganov@google.com
include /PACKAGE_MANAGER_OWNERS
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
index 1d25ac7..e5dbe5f 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
@@ -69,6 +69,7 @@
Settings.Global.PRIVATE_DNS_SPECIFIER,
Settings.Global.SOFT_AP_TIMEOUT_ENABLED,
Settings.Global.ZEN_DURATION,
+ Settings.Global.REVERSE_CHARGING_AUTO_ON,
Settings.Global.CHARGING_VIBRATION_ENABLED,
Settings.Global.AWARE_ALLOWED,
Settings.Global.CUSTOM_BUGREPORT_HANDLER_APP, // moved to secure
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index f5d9475..fe39c4f 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -103,5 +103,8 @@
Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR,
Settings.System.PEAK_REFRESH_RATE,
Settings.System.MIN_REFRESH_RATE,
+ Settings.System.NOTIFICATION_COOLDOWN_ENABLED,
+ Settings.System.NOTIFICATION_COOLDOWN_ALL,
+ Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED,
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index ba06185..7e8fe7e 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -171,6 +171,7 @@
VALIDATORS.put(Global.WIFI_SCAN_THROTTLE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.APP_AUTO_RESTRICTION_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.ZEN_DURATION, ANY_INTEGER_VALIDATOR);
+ VALIDATORS.put(Global.REVERSE_CHARGING_AUTO_ON, ANY_INTEGER_VALIDATOR);
VALIDATORS.put(Global.CHARGING_VIBRATION_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.REQUIRE_PASSWORD_TO_DECRYPT, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 410269f..eba74ab 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -31,6 +31,7 @@
import android.content.ComponentName;
import android.hardware.display.ColorDisplayManager;
import android.os.BatteryManager;
+import android.provider.Settings.Global;
import android.provider.Settings.System;
import android.util.ArrayMap;
@@ -239,5 +240,8 @@
VALIDATORS.put(System.SCREEN_FLASH_NOTIFICATION_COLOR, ANY_INTEGER_VALIDATOR);
VALIDATORS.put(System.PEAK_REFRESH_RATE, NON_NEGATIVE_FLOAT_VALIDATOR);
VALIDATORS.put(System.MIN_REFRESH_RATE, NON_NEGATIVE_FLOAT_VALIDATOR);
+ VALIDATORS.put(System.NOTIFICATION_COOLDOWN_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(System.NOTIFICATION_COOLDOWN_ALL, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index e40fcb2..f65f5a3 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -60,6 +60,7 @@
// except for SystemUI-core.
// Copied from compose/features/Android.bp.
static_libs: [
+ "CommunalLayoutLib",
"PlatformComposeCore",
"androidx.compose.runtime_runtime",
@@ -163,6 +164,7 @@
"SystemUISharedLib",
"SystemUI-statsd",
"SettingsLib",
+ "com_android_systemui_communal_flags_lib",
"com_android_systemui_flags_lib",
"androidx.core_core-ktx",
"androidx.viewpager2_viewpager2",
diff --git a/packages/SystemUI/aconfig/Android.bp b/packages/SystemUI/aconfig/Android.bp
index c1390b2..b18c790 100644
--- a/packages/SystemUI/aconfig/Android.bp
+++ b/packages/SystemUI/aconfig/Android.bp
@@ -11,3 +11,16 @@
name: "com_android_systemui_flags_lib",
aconfig_declarations: "com_android_systemui_flags",
}
+
+aconfig_declarations {
+ name: "com_android_systemui_communal_flags",
+ package: "com.android.systemui.communal",
+ srcs: [
+ "communal.aconfig",
+ ],
+}
+
+java_aconfig_library {
+ name: "com_android_systemui_communal_flags_lib",
+ aconfig_declarations: "com_android_systemui_communal_flags",
+}
diff --git a/packages/SystemUI/aconfig/communal.aconfig b/packages/SystemUI/aconfig/communal.aconfig
new file mode 100644
index 0000000..8ecb984
--- /dev/null
+++ b/packages/SystemUI/aconfig/communal.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.systemui.communal"
+
+flag {
+ name: "communal_hub"
+ namespace: "communal"
+ description: "Enables the communal hub experience"
+ bug: "304584416"
+}
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 18117a8..2509cfd 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -21,3 +21,11 @@
"(containing the \"Clear all\" button). Should not bring any behavior changes"
bug: "293167744"
}
+
+flag {
+ name: "notification_lifetime_extension_refactor"
+ namespace: "systemui"
+ description: "Enables moving notification lifetime extension management from SystemUI to "
+ "Notification Manager Service"
+ bug: "299448097"
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index 4aac279..4ea57a8 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -893,7 +893,7 @@
return
}
- Log.i(TAG, "Remote animation timed out")
+ Log.wtf(TAG, "Remote animation timed out")
timedOut = true
if (DEBUG_LAUNCH_ANIMATION) {
diff --git a/packages/SystemUI/communal/layout/Android.bp b/packages/SystemUI/communal/layout/Android.bp
new file mode 100644
index 0000000..88dad66
--- /dev/null
+++ b/packages/SystemUI/communal/layout/Android.bp
@@ -0,0 +1,35 @@
+// 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 {
+ default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
+}
+
+android_library {
+ name: "CommunalLayoutLib",
+ srcs: [
+ "src/**/*.kt",
+ ],
+ static_libs: [
+ "androidx.arch.core_core-runtime",
+ "androidx.compose.animation_animation-graphics",
+ "androidx.compose.runtime_runtime",
+ "androidx.compose.material3_material3",
+ "jsr330",
+ "kotlinx-coroutines-android",
+ "kotlinx-coroutines-core",
+ ],
+ manifest: "AndroidManifest.xml",
+ kotlincflags: ["-Xjvm-default=all"],
+}
diff --git a/packages/SystemUI/communal/layout/AndroidManifest.xml b/packages/SystemUI/communal/layout/AndroidManifest.xml
new file mode 100644
index 0000000..141be07
--- /dev/null
+++ b/packages/SystemUI/communal/layout/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest package="com.android.systemui.communal.layout" />
diff --git a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/CommunalLayoutEngine.kt b/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/CommunalLayoutEngine.kt
new file mode 100644
index 0000000..df87d19d
--- /dev/null
+++ b/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/CommunalLayoutEngine.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.layout
+
+import com.android.systemui.communal.layout.ui.compose.config.CommunalGridLayoutCard
+
+/** Computes the arrangement of cards. */
+class CommunalLayoutEngine {
+ companion object {
+ /**
+ * Determines the size that each card should be rendered in, and distributes the cards into
+ * columns.
+ *
+ * Returns a nested list where the outer list contains columns, and the inner list contains
+ * cards in each column.
+ *
+ * Currently treats the first supported size as the size to be rendered in, ignoring other
+ * supported sizes.
+ */
+ fun distributeCardsIntoColumns(
+ cards: List<CommunalGridLayoutCard>,
+ ): List<List<CommunalGridLayoutCardInfo>> {
+ val result = ArrayList<ArrayList<CommunalGridLayoutCardInfo>>()
+
+ var capacityOfLastColumn = 0
+ for (card in cards) {
+ val cardSize = card.supportedSizes.first()
+ if (capacityOfLastColumn >= cardSize.value) {
+ // Card fits in last column
+ capacityOfLastColumn -= cardSize.value
+ } else {
+ // Create a new column
+ result.add(arrayListOf())
+ capacityOfLastColumn = CommunalGridLayoutCard.Size.FULL.value - cardSize.value
+ }
+
+ result.last().add(CommunalGridLayoutCardInfo(card, cardSize))
+ }
+
+ return result
+ }
+ }
+
+ /**
+ * A data class that wraps around a [CommunalGridLayoutCard] and also contains the size that the
+ * card should be rendered in.
+ */
+ data class CommunalGridLayoutCardInfo(
+ val card: CommunalGridLayoutCard,
+ val size: CommunalGridLayoutCard.Size,
+ )
+}
diff --git a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/CommunalGridLayout.kt b/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/CommunalGridLayout.kt
new file mode 100644
index 0000000..4ed78b3
--- /dev/null
+++ b/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/CommunalGridLayout.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.layout.ui.compose
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.lazy.LazyRow
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import com.android.systemui.communal.layout.CommunalLayoutEngine
+import com.android.systemui.communal.layout.ui.compose.config.CommunalGridLayoutCard
+import com.android.systemui.communal.layout.ui.compose.config.CommunalGridLayoutConfig
+
+/**
+ * An arrangement of cards with a horizontal scroll, where each card is displayed in the right size
+ * and follows a specific order based on its priority, ensuring a seamless layout without any gaps.
+ */
+@Composable
+fun CommunalGridLayout(
+ modifier: Modifier,
+ layoutConfig: CommunalGridLayoutConfig,
+ communalCards: List<CommunalGridLayoutCard>,
+) {
+ val columns = CommunalLayoutEngine.distributeCardsIntoColumns(communalCards)
+ LazyRow(
+ modifier = modifier.height(layoutConfig.gridHeight),
+ horizontalArrangement = Arrangement.spacedBy(layoutConfig.gridGutter),
+ ) {
+ for (column in columns) {
+ item {
+ Column(
+ modifier = Modifier.width(layoutConfig.cardWidth),
+ verticalArrangement = Arrangement.spacedBy(layoutConfig.gridGutter),
+ ) {
+ for (cardInfo in column) {
+ Row(
+ modifier = Modifier.height(layoutConfig.cardHeight(cardInfo.size)),
+ ) {
+ cardInfo.card.Content(Modifier.fillMaxSize())
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutCard.kt b/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutCard.kt
new file mode 100644
index 0000000..ac8aa67
--- /dev/null
+++ b/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutCard.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.layout.ui.compose.config
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+
+/** A card that hosts content to be rendered in the communal grid layout. */
+abstract class CommunalGridLayoutCard {
+ /**
+ * Content to be hosted by the card.
+ *
+ * To host non-Compose views, see
+ * https://developer.android.com/jetpack/compose/migrate/interoperability-apis/views-in-compose.
+ */
+ @Composable abstract fun Content(modifier: Modifier)
+
+ /**
+ * Sizes supported by the card.
+ *
+ * If multiple sizes are available, they should be ranked in order of preference, from most to
+ * least preferred.
+ */
+ abstract val supportedSizes: List<Size>
+
+ /**
+ * Priority of the content hosted by the card.
+ *
+ * The value of priority is relative to other cards. Cards with a higher priority are generally
+ * ordered first.
+ */
+ open val priority: Int = 0
+
+ /**
+ * Size of the card.
+ *
+ * @param value A numeric value that represents the size. Must be less than or equal to
+ * [Size.FULL].
+ */
+ enum class Size(val value: Int) {
+ /** The card takes up full height of the grid layout. */
+ FULL(value = 6),
+
+ /** The card takes up half of the vertical space of the grid layout. */
+ HALF(value = 3),
+
+ /** The card takes up a third of the vertical space of the grid layout. */
+ THIRD(value = 2),
+ }
+}
diff --git a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutConfig.kt b/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutConfig.kt
new file mode 100644
index 0000000..143df83
--- /dev/null
+++ b/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutConfig.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.layout.ui.compose.config
+
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.times
+
+/**
+ * Configurations of the communal grid layout.
+ *
+ * The communal grid layout follows Material Design's responsive layout grid (see
+ * https://m2.material.io/design/layout/responsive-layout-grid.html), in which the layout is divided
+ * up by columns and gutters, and each card occupies one or multiple columns.
+ */
+data class CommunalGridLayoutConfig(
+ /**
+ * Size in dp of each grid column.
+ *
+ * Every card occupies one or more grid columns, which means that the width of each card is
+ * influenced by the size of the grid columns.
+ */
+ val gridColumnSize: Dp,
+
+ /**
+ * Size in dp of each grid gutter.
+ *
+ * A gutter is the space between columns that helps separate content. This is, therefore, also
+ * the size of the gaps between cards, both horizontally and vertically.
+ */
+ val gridGutter: Dp,
+
+ /**
+ * Height in dp of the grid layout.
+ *
+ * Cards with a full size take up the entire height of the grid layout.
+ */
+ val gridHeight: Dp,
+
+ /**
+ * Number of grid columns that each card occupies.
+ *
+ * It's important to note that all the cards take up the same number of grid columns, or in
+ * simpler terms, they all have the same width.
+ */
+ val gridColumnsPerCard: Int,
+) {
+ /**
+ * Width in dp of each card.
+ *
+ * It's important to note that all the cards take up the same number of grid columns, or in
+ * simpler terms, they all have the same width.
+ */
+ val cardWidth = gridColumnSize * gridColumnsPerCard + gridGutter * (gridColumnsPerCard - 1)
+
+ /** Returns the height of a card in dp, based on its size. */
+ fun cardHeight(cardSize: CommunalGridLayoutCard.Size): Dp {
+ return when (cardSize) {
+ CommunalGridLayoutCard.Size.FULL -> cardHeightBy(denominator = 1)
+ CommunalGridLayoutCard.Size.HALF -> cardHeightBy(denominator = 2)
+ CommunalGridLayoutCard.Size.THIRD -> cardHeightBy(denominator = 3)
+ }
+ }
+
+ /** Returns the height of a card in dp when the layout is evenly divided by [denominator]. */
+ private fun cardHeightBy(denominator: Int): Dp {
+ return (gridHeight - (denominator - 1) * gridGutter) / denominator
+ }
+}
diff --git a/packages/SystemUI/communal/layout/tests/Android.bp b/packages/SystemUI/communal/layout/tests/Android.bp
new file mode 100644
index 0000000..a60b1de
--- /dev/null
+++ b/packages/SystemUI/communal/layout/tests/Android.bp
@@ -0,0 +1,47 @@
+// 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 {
+ default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
+}
+
+android_test {
+ name: "CommunalLayoutLibTests",
+ srcs: [
+ "**/*.kt",
+ ],
+ static_libs: [
+ "CommunalLayoutLib",
+ "androidx.test.runner",
+ "androidx.test.rules",
+ "androidx.test.ext.junit",
+ "frameworks-base-testutils",
+ "junit",
+ "kotlinx_coroutines_test",
+ "mockito-target-extended-minus-junit4",
+ "platform-test-annotations",
+ "testables",
+ "truth-prebuilt",
+ ],
+ libs: [
+ "android.test.mock",
+ "android.test.base",
+ "android.test.runner",
+ ],
+ jni_libs: [
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ ],
+ manifest: "AndroidManifest.xml",
+}
diff --git a/packages/SystemUI/communal/layout/tests/AndroidManifest.xml b/packages/SystemUI/communal/layout/tests/AndroidManifest.xml
new file mode 100644
index 0000000..b19007c
--- /dev/null
+++ b/packages/SystemUI/communal/layout/tests/AndroidManifest.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.systemui.communal.layout.tests">
+
+ <application android:debuggable="true" android:largeHeap="true">
+ <uses-library android:name="android.test.mock" />
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.testing.TestableInstrumentation"
+ android:targetPackage="com.android.systemui.communal.layout.tests"
+ android:label="Tests for CommunalLayoutLib">
+ </instrumentation>
+
+</manifest>
diff --git a/packages/SystemUI/communal/layout/tests/AndroidTest.xml b/packages/SystemUI/communal/layout/tests/AndroidTest.xml
new file mode 100644
index 0000000..1352b23
--- /dev/null
+++ b/packages/SystemUI/communal/layout/tests/AndroidTest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration description="Runs tests for CommunalLayoutLib">
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="install-arg" value="-t" />
+ <option name="test-file-name" value="CommunalLayoutLibTests.apk" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="framework-base-presubmit" />
+ <option name="test-tag" value="CommunalLayoutLibTests" />
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.systemui.communal.layout.tests" />
+ <option name="runner" value="android.testing.TestableInstrumentation" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+
+</configuration>
diff --git a/packages/SystemUI/communal/layout/tests/src/com/android/systemui/communal/layout/CommunalLayoutEngineTest.kt b/packages/SystemUI/communal/layout/tests/src/com/android/systemui/communal/layout/CommunalLayoutEngineTest.kt
new file mode 100644
index 0000000..fdf65f5
--- /dev/null
+++ b/packages/SystemUI/communal/layout/tests/src/com/android/systemui/communal/layout/CommunalLayoutEngineTest.kt
@@ -0,0 +1,99 @@
+package com.android.systemui.communal.layout
+
+import androidx.compose.material3.Card
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.communal.layout.ui.compose.config.CommunalGridLayoutCard
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CommunalLayoutEngineTest {
+ @Test
+ fun distribution_fullLayout() {
+ val cards =
+ listOf(
+ generateCard(CommunalGridLayoutCard.Size.FULL),
+ generateCard(CommunalGridLayoutCard.Size.HALF),
+ generateCard(CommunalGridLayoutCard.Size.HALF),
+ generateCard(CommunalGridLayoutCard.Size.THIRD),
+ generateCard(CommunalGridLayoutCard.Size.THIRD),
+ generateCard(CommunalGridLayoutCard.Size.THIRD),
+ )
+ val expected =
+ listOf(
+ listOf(
+ CommunalGridLayoutCard.Size.FULL,
+ ),
+ listOf(
+ CommunalGridLayoutCard.Size.HALF,
+ CommunalGridLayoutCard.Size.HALF,
+ ),
+ listOf(
+ CommunalGridLayoutCard.Size.THIRD,
+ CommunalGridLayoutCard.Size.THIRD,
+ CommunalGridLayoutCard.Size.THIRD,
+ ),
+ )
+
+ assertDistribution(cards, expected)
+ }
+
+ @Test
+ fun distribution_layoutWithGaps() {
+ val cards =
+ listOf(
+ generateCard(CommunalGridLayoutCard.Size.HALF),
+ generateCard(CommunalGridLayoutCard.Size.THIRD),
+ generateCard(CommunalGridLayoutCard.Size.HALF),
+ generateCard(CommunalGridLayoutCard.Size.FULL),
+ generateCard(CommunalGridLayoutCard.Size.THIRD),
+ )
+ val expected =
+ listOf(
+ listOf(
+ CommunalGridLayoutCard.Size.HALF,
+ CommunalGridLayoutCard.Size.THIRD,
+ ),
+ listOf(
+ CommunalGridLayoutCard.Size.HALF,
+ ),
+ listOf(
+ CommunalGridLayoutCard.Size.FULL,
+ ),
+ listOf(
+ CommunalGridLayoutCard.Size.THIRD,
+ ),
+ )
+
+ assertDistribution(cards, expected)
+ }
+
+ private fun assertDistribution(
+ cards: List<CommunalGridLayoutCard>,
+ expected: List<List<CommunalGridLayoutCard.Size>>,
+ ) {
+ val result = CommunalLayoutEngine.distributeCardsIntoColumns(cards)
+
+ for (c in expected.indices) {
+ for (r in expected[c].indices) {
+ assertThat(result[c][r].size).isEqualTo(expected[c][r])
+ }
+ }
+ }
+
+ private fun generateCard(size: CommunalGridLayoutCard.Size): CommunalGridLayoutCard {
+ return object : CommunalGridLayoutCard() {
+ override val supportedSizes = listOf(size)
+
+ @Composable
+ override fun Content(modifier: Modifier) {
+ Card(modifier = modifier, content = {})
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/communal/layout/tests/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutConfigTest.kt b/packages/SystemUI/communal/layout/tests/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutConfigTest.kt
new file mode 100644
index 0000000..946eeec
--- /dev/null
+++ b/packages/SystemUI/communal/layout/tests/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutConfigTest.kt
@@ -0,0 +1,63 @@
+package com.android.systemui.communal.layout.ui.compose.config
+
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CommunalGridLayoutConfigTest {
+ @Test
+ fun cardWidth() {
+ Truth.assertThat(
+ CommunalGridLayoutConfig(
+ gridColumnSize = 5.dp,
+ gridGutter = 3.dp,
+ gridHeight = 17.dp,
+ gridColumnsPerCard = 1,
+ )
+ .cardWidth
+ )
+ .isEqualTo(5.dp)
+
+ Truth.assertThat(
+ CommunalGridLayoutConfig(
+ gridColumnSize = 5.dp,
+ gridGutter = 3.dp,
+ gridHeight = 17.dp,
+ gridColumnsPerCard = 2,
+ )
+ .cardWidth
+ )
+ .isEqualTo(13.dp)
+
+ Truth.assertThat(
+ CommunalGridLayoutConfig(
+ gridColumnSize = 5.dp,
+ gridGutter = 3.dp,
+ gridHeight = 17.dp,
+ gridColumnsPerCard = 3,
+ )
+ .cardWidth
+ )
+ .isEqualTo(21.dp)
+ }
+
+ @Test
+ fun cardHeight() {
+ val config =
+ CommunalGridLayoutConfig(
+ gridColumnSize = 5.dp,
+ gridGutter = 2.dp,
+ gridHeight = 10.dp,
+ gridColumnsPerCard = 3,
+ )
+
+ Truth.assertThat(config.cardHeight(CommunalGridLayoutCard.Size.FULL)).isEqualTo(10.dp)
+ Truth.assertThat(config.cardHeight(CommunalGridLayoutCard.Size.HALF)).isEqualTo(4.dp)
+ Truth.assertThat(config.cardHeight(CommunalGridLayoutCard.Size.THIRD)).isEqualTo(2.dp)
+ }
+}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateToScene.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateToScene.kt
index 60c3fd3..88944f10 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateToScene.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateToScene.kt
@@ -108,7 +108,7 @@
) {
val fromScene = layoutImpl.state.transitionState.currentScene
val isUserInput =
- (layoutImpl.state.transitionState as? TransitionState.Transition)?.isInitiatedByUserInput
+ (layoutImpl.state.transitionState as? TransitionState.Transition)?.isUserInputDriven
?: false
val animationSpec = layoutImpl.transitions.transitionSpec(fromScene, target).spec
@@ -119,23 +119,9 @@
val targetProgress = if (reversed) 0f else 1f
val transition =
if (reversed) {
- OneOffTransition(
- fromScene = target,
- toScene = fromScene,
- currentScene = target,
- isUserInput,
- isUserInputOngoing = false,
- animatable,
- )
+ OneOffTransition(target, fromScene, currentScene = target, isUserInput, animatable)
} else {
- OneOffTransition(
- fromScene = fromScene,
- toScene = target,
- currentScene = target,
- isUserInput,
- isUserInputOngoing = false,
- animatable,
- )
+ OneOffTransition(fromScene, target, currentScene = target, isUserInput, animatable)
}
// Change the current layout state to use this new transition.
@@ -156,8 +142,7 @@
override val fromScene: SceneKey,
override val toScene: SceneKey,
override val currentScene: SceneKey,
- override val isInitiatedByUserInput: Boolean,
- override val isUserInputOngoing: Boolean,
+ override val isUserInputDriven: Boolean,
private val animatable: Animatable<Float, AnimationVector1D>,
) : TransitionState.Transition {
override val progress: Float
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Element.kt
index 3bcd920f..ce96bbf 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Element.kt
@@ -21,6 +21,7 @@
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.movableContentOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
@@ -59,6 +60,17 @@
/** The mapping between a scene and the values/state this element has in that scene, if any. */
val sceneValues = SnapshotStateMap<SceneKey, TargetValues>()
+ /**
+ * The movable content of this element, if this element is composed using
+ * [SceneScope.MovableElement].
+ */
+ val movableContent by
+ // This is only accessed from the composition (main) thread, so no need to use the default
+ // lock of lazy {} to synchronize.
+ lazy(mode = LazyThreadSafetyMode.NONE) {
+ movableContentOf { content: @Composable () -> Unit -> content() }
+ }
+
override fun toString(): String {
return "Element(key=$key)"
}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Key.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Key.kt
index b7acc48..bc015ee 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Key.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Key.kt
@@ -22,7 +22,7 @@
* A base class to create unique keys, associated to an [identity] that is used to check the
* equality of two key instances.
*/
-sealed class Key(val name: String, val identity: Any) {
+sealed class Key(val debugName: String, val identity: Any) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (this.javaClass != other?.javaClass) return false
@@ -34,7 +34,7 @@
}
override fun toString(): String {
- return "Key(name=$name)"
+ return "Key(debugName=$debugName)"
}
}
@@ -49,7 +49,7 @@
val rootElementKey = ElementKey(name, identity)
override fun toString(): String {
- return "SceneKey(name=$name)"
+ return "SceneKey(debugName=$debugName)"
}
}
@@ -71,7 +71,7 @@
}
override fun toString(): String {
- return "ElementKey(name=$name)"
+ return "ElementKey(debugName=$debugName)"
}
companion object {
@@ -89,6 +89,6 @@
/** Key for a shared value of an element. */
class ValueKey(name: String, identity: Any = Object()) : Key(name, identity) {
override fun toString(): String {
- return "ValueKey(name=$name)"
+ return "ValueKey(debugName=$debugName)"
}
}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/MovableElement.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/MovableElement.kt
new file mode 100644
index 0000000..11bbf2a
--- /dev/null
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/MovableElement.kt
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.animation.scene
+
+import android.graphics.Picture
+import android.util.Log
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.snapshots.Snapshot
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.draw.drawWithCache
+import androidx.compose.ui.graphics.Canvas
+import androidx.compose.ui.graphics.drawscope.draw
+import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
+import androidx.compose.ui.graphics.nativeCanvas
+import androidx.compose.ui.layout.layout
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.IntSize
+
+private const val TAG = "MovableElement"
+
+private object MovableElementScopeImpl : MovableElementScope
+
+@Composable
+internal fun MovableElement(
+ layoutImpl: SceneTransitionLayoutImpl,
+ scene: Scene,
+ key: ElementKey,
+ modifier: Modifier,
+ content: @Composable MovableElementScope.() -> Unit,
+) {
+ Box(modifier.element(layoutImpl, scene, key)) {
+ // Get the Element from the map. It will always be the same and we don't want to recompose
+ // every time an element is added/removed from SceneTransitionLayoutImpl.elements, so we
+ // disable read observation during the look-up in that map.
+ val element = Snapshot.withoutReadObservation { layoutImpl.elements.getValue(key) }
+
+ // The [Picture] to which we save the last drawing commands of this element. This is
+ // necessary because the content of this element might not be composed in this scene, in
+ // which case we still need to draw it.
+ val picture = remember { Picture() }
+
+ if (shouldComposeMovableElement(layoutImpl, scene.key, element)) {
+ Box(
+ Modifier.drawWithCache {
+ val width = size.width.toInt()
+ val height = size.height.toInt()
+
+ onDrawWithContent {
+ // Save the draw commands into [picture] for later to draw the last content
+ // even when this movable content is not composed.
+ val pictureCanvas = Canvas(picture.beginRecording(width, height))
+ draw(this, this.layoutDirection, pictureCanvas, this.size) {
+ this@onDrawWithContent.drawContent()
+ }
+ picture.endRecording()
+
+ // Draw the content.
+ drawIntoCanvas { canvas -> canvas.nativeCanvas.drawPicture(picture) }
+ }
+ }
+ ) {
+ element.movableContent { MovableElementScopeImpl.content() }
+ }
+ } else {
+ // If we are not composed, we draw the previous drawing commands at the same size as the
+ // movable content when it was composed in this scene.
+ val sceneValues = element.sceneValues.getValue(scene.key)
+
+ Spacer(
+ Modifier.layout { measurable, _ ->
+ val size =
+ sceneValues.targetSize.takeIf { it != Element.SizeUnspecified }
+ ?: IntSize.Zero
+ val placeable =
+ measurable.measure(Constraints.fixed(size.width, size.height))
+ layout(size.width, size.height) { placeable.place(0, 0) }
+ }
+ .drawBehind {
+ drawIntoCanvas { canvas -> canvas.nativeCanvas.drawPicture(picture) }
+ }
+ )
+ }
+ }
+}
+
+private fun shouldComposeMovableElement(
+ layoutImpl: SceneTransitionLayoutImpl,
+ scene: SceneKey,
+ element: Element,
+): Boolean {
+ val transitionState = layoutImpl.state.transitionState
+
+ // If we are idle, there is only one [scene] that is composed so we can compose our movable
+ // content here.
+ if (transitionState is TransitionState.Idle) {
+ check(transitionState.currentScene == scene)
+ return true
+ }
+
+ val fromScene = (transitionState as TransitionState.Transition).fromScene
+ val toScene = transitionState.toScene
+ if (fromScene == toScene) {
+ check(fromScene == scene)
+ return true
+ }
+
+ val fromReady = layoutImpl.isSceneReady(fromScene)
+ val toReady = layoutImpl.isSceneReady(toScene)
+
+ val otherScene =
+ when (scene) {
+ fromScene -> toScene
+ toScene -> fromScene
+ else ->
+ error(
+ "shouldComposeMovableElement(scene=$scene) called with fromScene=$fromScene " +
+ "and toScene=$toScene"
+ )
+ }
+
+ val isShared = otherScene in element.sceneValues
+
+ if (isShared && !toReady && !fromReady) {
+ // This should usually not happen given that fromScene should be ready, but let's log a
+ // warning here in case it does so it helps debugging flicker issues caused by this part of
+ // the code.
+ Log.w(
+ TAG,
+ "MovableElement $element might have to be composed for the first time in both " +
+ "fromScene=$fromScene and toScene=$toScene. This will probably lead to a flicker " +
+ "where the size of the element will jump from IntSize.Zero to its actual size " +
+ "during the transition."
+ )
+ }
+
+ // Element is not shared in this transition.
+ if (!isShared) {
+ return true
+ }
+
+ // toScene is not ready (because we are composing it for the first time), so we compose it there
+ // first. This is the most common scenario when starting a transition that has a shared movable
+ // element.
+ if (!toReady) {
+ return scene == toScene
+ }
+
+ // This should usually not happen, but if we are also composing for the first time in fromScene
+ // then we should compose it there only.
+ if (!fromReady) {
+ return scene == fromScene
+ }
+
+ // If we are ready in both scenes, then compose in the scene that has the highest zIndex (unless
+ // it is a background) given that this is the one that is going to be drawn.
+ val isHighestScene = layoutImpl.scene(scene).zIndex > layoutImpl.scene(otherScene).zIndex
+ return if (element.key.isBackground) {
+ !isHighestScene
+ } else {
+ isHighestScene
+ }
+}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ObservableTransitionState.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ObservableTransitionState.kt
index 1b79dbd..ccdec6e 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ObservableTransitionState.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ObservableTransitionState.kt
@@ -52,14 +52,7 @@
* scene, this value will remain true after the pointer is no longer touching the screen and
* will be true in any transition created to animate back to the original position.
*/
- val isInitiatedByUserInput: Boolean,
-
- /**
- * Whether user input is currently driving the transition. For example, if a user is
- * dragging a pointer, this emits true. Once they lift their finger, this emits false while
- * the transition completes/settles.
- */
- val isUserInputOngoing: Flow<Boolean>,
+ val isUserInputDriven: Boolean,
) : ObservableTransitionState()
}
@@ -80,8 +73,7 @@
fromScene = state.fromScene,
toScene = state.toScene,
progress = snapshotFlow { state.progress },
- isInitiatedByUserInput = state.isInitiatedByUserInput,
- isUserInputOngoing = snapshotFlow { state.isUserInputOngoing },
+ isUserInputDriven = state.isUserInputDriven,
)
}
}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Scene.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Scene.kt
index 3985233..3fd6828 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Scene.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Scene.kt
@@ -90,4 +90,13 @@
canOverflow,
)
}
+
+ @Composable
+ override fun MovableElement(
+ key: ElementKey,
+ modifier: Modifier,
+ content: @Composable MovableElementScope.() -> Unit,
+ ) {
+ MovableElement(layoutImpl, scene, key, modifier, content)
+ }
}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index 58c7bdb..4283c0e 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -85,6 +85,13 @@
)
}
+/**
+ * A DSL marker to prevent people from nesting calls to Modifier.element() inside a MovableElement,
+ * which is not supported.
+ */
+@DslMarker annotation class ElementDsl
+
+@ElementDsl
interface SceneScope {
/**
* Tag an element identified by [key].
@@ -95,12 +102,37 @@
* Additionally, this [key] will be used to detect elements that are shared between scenes to
* automatically interpolate their size, offset and [shared values][animateSharedValueAsState].
*
+ * Note that shared elements tagged using this function will be duplicated in each scene they
+ * are part of, so any **internal** state (e.g. state created using `remember {
+ * mutableStateOf(...) }`) will be lost. If you need to preserve internal state, you should use
+ * [MovableElement] instead.
+ *
+ * @see MovableElement
+ *
* TODO(b/291566282): Migrate this to the new Modifier Node API and remove the @Composable
* constraint.
*/
@Composable fun Modifier.element(key: ElementKey): Modifier
/**
+ * Create a *movable* element identified by [key].
+ *
+ * This creates an element that will be automatically shared when present in multiple scenes and
+ * that can be transformed during transitions, the same way that [element] does. The major
+ * difference with [element] is that elements created with [MovableElement] will be "moved" and
+ * composed only once during transitions (as opposed to [element] that duplicates shared
+ * elements) so that any internal state is preserved during and after the transition.
+ *
+ * @see element
+ */
+ @Composable
+ fun MovableElement(
+ key: ElementKey,
+ modifier: Modifier,
+ content: @Composable MovableElementScope.() -> Unit,
+ )
+
+ /**
* Animate some value of a shared element.
*
* @param value the value of this shared value in the current scene.
@@ -126,6 +158,10 @@
): State<T>
}
+// TODO(b/291053742): Add animateSharedValueAsState(targetValue) without any ValueKey and ElementKey
+// arguments to allow sharing values inside a movable element.
+@ElementDsl interface MovableElementScope
+
/** An action performed by the user. */
sealed interface UserAction
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
index b3a7a8e9..4952270 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
@@ -199,4 +199,6 @@
return readyScenes.containsKey(transition.fromScene) &&
readyScenes.containsKey(transition.toScene)
}
+
+ internal fun isSceneReady(scene: SceneKey): Boolean = readyScenes.containsKey(scene)
}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index b9f83c5..7a21211 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -70,9 +70,6 @@
val progress: Float
/** Whether the transition was triggered by user input rather than being programmatic. */
- val isInitiatedByUserInput: Boolean
-
- /** Whether user input is currently driving the transition. */
- val isUserInputOngoing: Boolean
+ val isUserInputDriven: Boolean
}
}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt
index e275fca..1cbfe30 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt
@@ -66,7 +66,7 @@
// swipe in the other direction.
val startDragImmediately =
state == transition &&
- !transition.isUserInputOngoing &&
+ transition.isAnimatingOffset &&
!currentScene.shouldEnableSwipes(orientation.opposite())
// The velocity threshold at which the intent of the user is to swipe up or down. It is the same
@@ -126,7 +126,7 @@
override val progress: Float
get() {
- val offset = if (isUserInputOngoing) dragOffset else offsetAnimatable.value
+ val offset = if (isAnimatingOffset) offsetAnimatable.value else dragOffset
if (distance == 0f) {
// This can happen only if fromScene == toScene.
error(
@@ -137,15 +137,16 @@
return offset / distance
}
- override val isInitiatedByUserInput = true
-
- var _isUserInputOngoing by mutableStateOf(false)
- override val isUserInputOngoing: Boolean
- get() = _isUserInputOngoing
+ override val isUserInputDriven = true
/** The current offset caused by the drag gesture. */
var dragOffset by mutableFloatStateOf(0f)
+ /**
+ * Whether the offset is animated (the user lifted their finger) or if it is driven by gesture.
+ */
+ var isAnimatingOffset by mutableStateOf(false)
+
/** The animatable used to animate the offset once the user lifted its finger. */
val offsetAnimatable = Animatable(0f, visibilityThreshold = OffsetVisibilityThreshold)
@@ -208,11 +209,9 @@
transition: SwipeTransition,
orientation: Orientation,
) {
- transition._isUserInputOngoing = true
-
if (layoutImpl.state.transitionState == transition) {
// This [transition] was already driving the animation: simply take over it.
- if (!transition.isUserInputOngoing) {
+ if (transition.isAnimatingOffset) {
// Stop animating and start from where the current offset. Setting the animation job to
// `null` will effectively cancel the animation.
transition.stopOffsetAnimation()
@@ -457,29 +456,30 @@
) {
transition.startOffsetAnimation {
launch {
- if (transition.isUserInputOngoing) {
- transition.offsetAnimatable.snapTo(transition.dragOffset)
- }
- transition._isUserInputOngoing = false
+ if (!transition.isAnimatingOffset) {
+ transition.offsetAnimatable.snapTo(transition.dragOffset)
+ }
+ transition.isAnimatingOffset = true
- transition.offsetAnimatable.animateTo(
- targetOffset,
- // TODO(b/290184746): Make this spring spec configurable.
- spring(
- stiffness = Spring.StiffnessMediumLow,
- visibilityThreshold = OffsetVisibilityThreshold
- ),
- initialVelocity = initialVelocity,
- )
+ transition.offsetAnimatable.animateTo(
+ targetOffset,
+ // TODO(b/290184746): Make this spring spec configurable.
+ spring(
+ stiffness = Spring.StiffnessMediumLow,
+ visibilityThreshold = OffsetVisibilityThreshold
+ ),
+ initialVelocity = initialVelocity,
+ )
- // Now that the animation is done, the state should be idle. Note that if the state
- // was changed since this animation started, some external code changed it and we
- // shouldn't do anything here. Note also that this job will be cancelled in the case
- // where the user intercepts this swipe.
- if (layoutImpl.state.transitionState == transition) {
- layoutImpl.state.transitionState = TransitionState.Idle(targetScene)
+ // Now that the animation is done, the state should be idle. Note that if the state
+ // was changed since this animation started, some external code changed it and we
+ // shouldn't do anything here. Note also that this job will be cancelled in the case
+ // where the user intercepts this swipe.
+ if (layoutImpl.state.transitionState == transition) {
+ layoutImpl.state.transitionState = TransitionState.Idle(targetScene)
+ }
}
- }
+ .also { it.invokeOnCompletion { transition.isAnimatingOffset = false } }
}
}
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/MovableElementTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/MovableElementTest.kt
new file mode 100644
index 0000000..4204cd5
--- /dev/null
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/MovableElementTest.kt
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.animation.scene
+
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.hasParent
+import androidx.compose.ui.test.hasText
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onAllNodesWithText
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.test.assertSizeIsEqualTo
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class MovableElementTest {
+ @get:Rule val rule = createComposeRule()
+
+ /** An element that displays a counter that is incremented whenever this element is clicked. */
+ @Composable
+ private fun Counter(modifier: Modifier = Modifier) {
+ var count by remember { mutableIntStateOf(0) }
+ Box(modifier.fillMaxSize().clickable { count++ }) { Text("count: $count") }
+ }
+
+ @Composable
+ private fun SceneScope.MovableCounter(key: ElementKey, modifier: Modifier) {
+ MovableElement(key, modifier) { Counter() }
+ }
+
+ @Test
+ fun modifierElementIsDuplicatedDuringTransitions() {
+ rule.testTransition(
+ fromSceneContent = {
+ Box(Modifier.element(TestElements.Foo).size(50.dp)) { Counter() }
+ },
+ toSceneContent = { Box(Modifier.element(TestElements.Foo).size(100.dp)) { Counter() } },
+ transition = { spec = tween(durationMillis = 16 * 4, easing = LinearEasing) },
+ fromScene = TestScenes.SceneA,
+ toScene = TestScenes.SceneB,
+ ) {
+ before {
+ // Click 3 times on the counter.
+ rule.onNodeWithText("count: 0").assertIsDisplayed().performClick()
+ rule.onNodeWithText("count: 1").assertIsDisplayed().performClick()
+ rule.onNodeWithText("count: 2").assertIsDisplayed().performClick()
+ rule
+ .onNodeWithText("count: 3")
+ .assertIsDisplayed()
+ .assertSizeIsEqualTo(50.dp, 50.dp)
+
+ // There are no other counters.
+ assertThat(
+ rule
+ .onAllNodesWithText("count: ", substring = true)
+ .fetchSemanticsNodes()
+ .size
+ )
+ .isEqualTo(1)
+ }
+
+ at(32) {
+ // In the middle of the transition, there are 2 copies of the counter: the previous
+ // one from scene A (equal to 3) and the new one from scene B (equal to 0).
+ rule
+ .onNode(
+ hasText("count: 3") and
+ hasParent(isElement(TestElements.Foo, scene = TestScenes.SceneA))
+ )
+ .assertIsDisplayed()
+ .assertSizeIsEqualTo(75.dp, 75.dp)
+
+ rule
+ .onNode(
+ hasText("count: 0") and
+ hasParent(isElement(TestElements.Foo, scene = TestScenes.SceneB))
+ )
+ .assertIsDisplayed()
+ .assertSizeIsEqualTo(75.dp, 75.dp)
+
+ // There are exactly 2 counters.
+ assertThat(
+ rule
+ .onAllNodesWithText("count: ", substring = true)
+ .fetchSemanticsNodes()
+ .size
+ )
+ .isEqualTo(2)
+ }
+
+ after {
+ // At the end of the transition, only the counter from scene B is composed.
+ rule
+ .onNodeWithText("count: 0")
+ .assertIsDisplayed()
+ .assertSizeIsEqualTo(100.dp, 100.dp)
+
+ // There are no other counters.
+ assertThat(
+ rule
+ .onAllNodesWithText("count: ", substring = true)
+ .fetchSemanticsNodes()
+ .size
+ )
+ .isEqualTo(1)
+ }
+ }
+ }
+
+ @Test
+ fun movableElementIsMovedAndComposedOnlyOnce() {
+ rule.testTransition(
+ fromSceneContent = { MovableCounter(TestElements.Foo, Modifier.size(50.dp)) },
+ toSceneContent = { MovableCounter(TestElements.Foo, Modifier.size(100.dp)) },
+ transition = { spec = tween(durationMillis = 16 * 4, easing = LinearEasing) },
+ fromScene = TestScenes.SceneA,
+ toScene = TestScenes.SceneB,
+ ) {
+ before {
+ // Click 3 times on the counter.
+ rule.onNodeWithText("count: 0").assertIsDisplayed().performClick()
+ rule.onNodeWithText("count: 1").assertIsDisplayed().performClick()
+ rule.onNodeWithText("count: 2").assertIsDisplayed().performClick()
+ rule
+ .onNodeWithText("count: 3")
+ .assertIsDisplayed()
+ .assertSizeIsEqualTo(50.dp, 50.dp)
+
+ // There are no other counters.
+ assertThat(
+ rule
+ .onAllNodesWithText("count: ", substring = true)
+ .fetchSemanticsNodes()
+ .size
+ )
+ .isEqualTo(1)
+ }
+
+ at(32) {
+ // During the transition, there is a single counter that is moved, with the current
+ // value.
+ rule
+ .onNode(hasText("count: 3"))
+ .assertIsDisplayed()
+ .assertSizeIsEqualTo(75.dp, 75.dp)
+
+ // There are no other counters.
+ assertThat(
+ rule
+ .onAllNodesWithText("count: ", substring = true)
+ .fetchSemanticsNodes()
+ .size
+ )
+ .isEqualTo(1)
+ }
+
+ after {
+ // At the end of the transition, the counter still has the current value.
+ rule
+ .onNodeWithText("count: 3")
+ .assertIsDisplayed()
+ .assertSizeIsEqualTo(100.dp, 100.dp)
+
+ // There are no other counters.
+ assertThat(
+ rule
+ .onAllNodesWithText("count: ", substring = true)
+ .fetchSemanticsNodes()
+ .size
+ )
+ .isEqualTo(1)
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
index 328866e..5afd420 100644
--- a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
@@ -117,7 +117,7 @@
.size(size)
.background(Color.Red)
.element(TestElements.Foo)
- .testTag(TestElements.Foo.name)
+ .testTag(TestElements.Foo.debugName)
) {
// Offset the single child of Foo by some animated shared offset.
val offset by animateSharedDpAsState(childOffset, TestValues.Value1, TestElements.Foo)
@@ -129,7 +129,7 @@
}
.size(30.dp)
.background(Color.Blue)
- .testTag(TestElements.Bar.name)
+ .testTag(TestElements.Bar.debugName)
)
}
}
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
index 53ed2b5..df3b72a 100644
--- a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
@@ -63,7 +63,7 @@
{ currentScene = it },
EmptyTestTransitions,
state = layoutState,
- modifier = Modifier.size(LayoutWidth, LayoutHeight).testTag(TestElements.Foo.name),
+ modifier = Modifier.size(LayoutWidth, LayoutHeight).testTag(TestElements.Foo.debugName),
) {
scene(
TestScenes.SceneA,
@@ -122,8 +122,7 @@
assertThat(transition.toScene).isEqualTo(TestScenes.SceneB)
assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA)
assertThat(transition.progress).isEqualTo(55.dp / LayoutWidth)
- assertThat(transition.isInitiatedByUserInput).isTrue()
- assertThat(transition.isUserInputOngoing).isTrue()
+ assertThat(transition.isUserInputDriven).isTrue()
// Release the finger. We should now be animating back to A (currentScene = SceneA) given
// that 55dp < positional threshold.
@@ -135,8 +134,7 @@
assertThat(transition.toScene).isEqualTo(TestScenes.SceneB)
assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA)
assertThat(transition.progress).isEqualTo(55.dp / LayoutWidth)
- assertThat(transition.isInitiatedByUserInput).isTrue()
- assertThat(transition.isUserInputOngoing).isFalse()
+ assertThat(transition.isUserInputDriven).isTrue()
// Wait for the animation to finish. We should now be in scene A.
rule.waitForIdle()
@@ -158,8 +156,7 @@
assertThat(transition.toScene).isEqualTo(TestScenes.SceneC)
assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA)
assertThat(transition.progress).isEqualTo(56.dp / LayoutHeight)
- assertThat(transition.isInitiatedByUserInput).isTrue()
- assertThat(transition.isUserInputOngoing).isTrue()
+ assertThat(transition.isUserInputDriven).isTrue()
// Release the finger. We should now be animating to C (currentScene = SceneC) given
// that 56dp >= positional threshold.
@@ -171,8 +168,7 @@
assertThat(transition.toScene).isEqualTo(TestScenes.SceneC)
assertThat(transition.currentScene).isEqualTo(TestScenes.SceneC)
assertThat(transition.progress).isEqualTo(56.dp / LayoutHeight)
- assertThat(transition.isInitiatedByUserInput).isTrue()
- assertThat(transition.isUserInputOngoing).isFalse()
+ assertThat(transition.isUserInputDriven).isTrue()
// Wait for the animation to finish. We should now be in scene C.
rule.waitForIdle()
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/TestTransition.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/TestTransition.kt
index 268057f..e0ae1be 100644
--- a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/TestTransition.kt
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/TestTransition.kt
@@ -22,13 +22,13 @@
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
+import androidx.compose.ui.test.SemanticsMatcher
import androidx.compose.ui.test.SemanticsNodeInteraction
import androidx.compose.ui.test.SemanticsNodeInteractionCollection
import androidx.compose.ui.test.hasParent
import androidx.compose.ui.test.hasTestTag
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import androidx.compose.ui.test.onAllNodesWithTag
-import androidx.compose.ui.test.onNodeWithTag
@DslMarker annotation class TransitionTestDsl
@@ -63,6 +63,8 @@
@TransitionTestDsl
interface TransitionTestAssertionScope {
+ fun isElement(element: ElementKey, scene: SceneKey? = null): SemanticsMatcher
+
/**
* Assert on [element].
*
@@ -130,15 +132,19 @@
val test = transitionTest(builder)
val assertionScope =
object : TransitionTestAssertionScope {
+ override fun isElement(element: ElementKey, scene: SceneKey?): SemanticsMatcher {
+ return if (scene == null) {
+ hasTestTag(element.testTag)
+ } else {
+ hasTestTag(element.testTag) and hasParent(hasTestTag(scene.testTag))
+ }
+ }
+
override fun onElement(
element: ElementKey,
scene: SceneKey?
): SemanticsNodeInteraction {
- return if (scene == null) {
- onNodeWithTag(element.testTag)
- } else {
- onNode(hasTestTag(element.testTag) and hasParent(hasTestTag(scene.testTag)))
- }
+ return onNode(isElement(element, scene))
}
override fun onSharedElement(element: ElementKey): SemanticsNodeInteractionCollection {
diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt
index 5b4a8fb..3d670b8 100644
--- a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt
+++ b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt
@@ -64,6 +64,12 @@
throwComposeUnavailableError()
}
+ override fun createCommunalView(
+ context: Context,
+ ): View {
+ throwComposeUnavailableError()
+ }
+
private fun throwComposeUnavailableError(): Nothing {
error(
"Compose is not available. Make sure to check isComposeAvailable() before calling any" +
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt
index ac59989..7b11ac7 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt
@@ -30,6 +30,7 @@
import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation
import com.android.systemui.common.ui.compose.windowinsets.DisplayCutout
import com.android.systemui.common.ui.compose.windowinsets.DisplayCutoutProvider
+import com.android.systemui.communal.ui.compose.CommunalHub
import com.android.systemui.people.ui.compose.PeopleScreen
import com.android.systemui.people.ui.viewmodel.PeopleViewModel
import com.android.systemui.qs.footer.ui.compose.FooterActions
@@ -93,6 +94,12 @@
}
}
+ override fun createCommunalView(
+ context: Context,
+ ): View {
+ return ComposeView(context).apply { setContent { PlatformTheme { CommunalHub() } } }
+ }
+
// TODO(b/298525212): remove once Compose exposes window inset bounds.
private fun displayCutoutFromWindowInsets(
scope: CoroutineScope,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
new file mode 100644
index 0000000..4d2978d
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -0,0 +1,22 @@
+package com.android.systemui.communal.ui.compose
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+
+@Composable
+fun CommunalHub(modifier: Modifier = Modifier) {
+ Box(
+ modifier = modifier.fillMaxSize().background(Color.White),
+ ) {
+ Text(
+ modifier = Modifier.align(Alignment.Center),
+ text = "Hello Communal!",
+ )
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
index 0d2ba28..d1c12ac 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
@@ -16,14 +16,8 @@
package com.android.systemui.communal.ui.compose
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.scene.shared.model.Direction
@@ -51,13 +45,6 @@
@Composable
override fun SceneScope.Content(modifier: Modifier) {
- Box(
- modifier = modifier.fillMaxSize().background(Color.White),
- ) {
- Text(
- modifier = Modifier.align(Alignment.Center),
- text = "Hello Communal!",
- )
- }
+ CommunalHub(modifier)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
index e12b7ea..73cb72c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
@@ -47,10 +47,10 @@
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.android.compose.theme.LocalAndroidColorScheme
-import com.android.systemui.res.R
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.people.ui.viewmodel.PeopleTileViewModel
import com.android.systemui.people.ui.viewmodel.PeopleViewModel
+import com.android.systemui.res.R
/**
* Compose the screen associated to a [PeopleViewModel].
@@ -86,9 +86,9 @@
modifier = Modifier.fillMaxSize(),
) {
if (priorityTiles.isNotEmpty() || recentTiles.isNotEmpty()) {
- PeopleScreenWithConversations(priorityTiles, recentTiles, viewModel::onTileClicked)
+ PeopleScreenWithConversations(priorityTiles, recentTiles, viewModel.onTileClicked)
} else {
- PeopleScreenEmpty(viewModel::onUserJourneyCancelled)
+ PeopleScreenEmpty(viewModel.onUserJourneyCancelled)
}
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index 0da562b..2e93a09 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -161,8 +161,7 @@
fromScene = fromScene.toModel().key,
toScene = toScene.toModel().key,
progress = progress,
- isInitiatedByUserInput = isInitiatedByUserInput,
- isUserInputOngoing = isUserInputOngoing,
+ isUserInputDriven = isUserInputDriven,
)
}
}
diff --git a/packages/SystemUI/proguard_common.flags b/packages/SystemUI/proguard_common.flags
index be1e655..445bdc2 100644
--- a/packages/SystemUI/proguard_common.flags
+++ b/packages/SystemUI/proguard_common.flags
@@ -2,45 +2,17 @@
# Needed to ensure callback field references are kept in their respective
# owning classes when the downstream callback registrars only store weak refs.
-# TODO(b/264686688): Handle these cases with more targeted annotations.
--keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
- private com.android.keyguard.KeyguardUpdateMonitorCallback *;
- private com.android.systemui.privacy.PrivacyConfig$Callback *;
- private com.android.systemui.privacy.PrivacyItemController$Callback *;
- private com.android.systemui.settings.UserTracker$Callback *;
- private com.android.systemui.statusbar.phone.StatusBarWindowCallback *;
- private com.android.systemui.util.service.Observer$Callback *;
- private com.android.systemui.util.service.ObservableServiceConnection$Callback *;
-}
-# Note that these rules are temporary companions to the above rules, required
-# for cases like Kotlin where fields with anonymous types use the anonymous type
-# rather than the supertype.
--if class * extends com.android.keyguard.KeyguardUpdateMonitorCallback
--keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
+# Note that we restrict this to SysUISingleton classes, as other registering
+# classes should either *always* unregister or *never* register from their
+# constructor. We also keep callback class names for easier debugging.
+-keepnames @com.android.systemui.util.annotations.WeaklyReferencedCallback class *
+-keepnames class * extends @com.android.systemui.util.annotations.WeaklyReferencedCallback **
+-if @com.android.systemui.util.annotations.WeaklyReferencedCallback class *
+-keepclassmembers,allowaccessmodification @com.android.systemui.dagger.SysUISingleton class * {
<1> *;
}
--if class * extends com.android.systemui.privacy.PrivacyConfig$Callback
--keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
- <1> *;
-}
--if class * extends com.android.systemui.privacy.PrivacyItemController$Callback
--keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
- <1> *;
-}
--if class * extends com.android.systemui.settings.UserTracker$Callback
--keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
- <1> *;
-}
--if class * extends com.android.systemui.statusbar.phone.StatusBarWindowCallback
--keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
- <1> *;
-}
--if class * extends com.android.systemui.util.service.Observer$Callback
--keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
- <1> *;
-}
--if class * extends com.android.systemui.util.service.ObservableServiceConnection$Callback
--keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
+-if class * extends @com.android.systemui.util.annotations.WeaklyReferencedCallback **
+-keepclassmembers,allowaccessmodification @com.android.systemui.dagger.SysUISingleton class * {
<1> *;
}
diff --git a/packages/SystemUI/res/color/qs_tile_ripple_color.xml b/packages/SystemUI/res/color/qs_tile_ripple_color.xml
new file mode 100644
index 0000000..c106254
--- /dev/null
+++ b/packages/SystemUI/res/color/qs_tile_ripple_color.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="?android:attr/colorControlHighlight" android:state_hovered="true"
+ android:state_pressed="true" />
+ <!-- RippleDrawable has default way of handling hover state with highlighting but it's not
+ consistent with our approach so we make highlighting invisible and instead do custom handling
+ of hover state on a different level -->
+ <item android:color="@color/transparent" android:state_hovered="true" />
+ <item android:color="?android:attr/colorControlHighlight" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_tile_background.xml b/packages/SystemUI/res/drawable/qs_tile_background.xml
index 265f575..ef3c61b 100644
--- a/packages/SystemUI/res/drawable/qs_tile_background.xml
+++ b/packages/SystemUI/res/drawable/qs_tile_background.xml
@@ -15,9 +15,25 @@
~ limitations under the License.
-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="?android:attr/colorControlHighlight">
+ android:color="@color/qs_tile_ripple_color">
<item android:id="@android:id/mask"
- android:drawable="@drawable/qs_tile_background_shape" />
- <item android:id="@id/background"
- android:drawable="@drawable/qs_tile_background_shape"/>
+ android:drawable="@drawable/qs_tile_background_shape"
+ />
+ <item android:id="@id/background">
+ <layer-list>
+ <item
+ android:id="@+id/qs_tile_background_base"
+ android:drawable="@drawable/qs_tile_background_shape" />
+ <item android:id="@+id/qs_tile_background_overlay">
+ <selector>
+ <item
+ android:state_hovered="true"
+ android:drawable="@drawable/qs_tile_background_shape" />
+ <item
+ android:state_focused="true"
+ android:drawable="@drawable/qs_tile_background_shape" />
+ </selector>
+ </item>
+ </layer-list>
+ </item>
</ripple>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/bluetooth_device_item.xml b/packages/SystemUI/res/layout/bluetooth_device_item.xml
index 1c7e997..6d77943 100644
--- a/packages/SystemUI/res/layout/bluetooth_device_item.xml
+++ b/packages/SystemUI/res/layout/bluetooth_device_item.xml
@@ -27,7 +27,6 @@
<ImageView
android:id="@+id/bluetooth_device_icon"
- android:contentDescription="@string/accessibility_bluetooth_device_icon"
android:layout_width="24dp"
android:layout_height="24dp"
app:layout_constraintStart_toStartOf="parent"
@@ -39,8 +38,12 @@
android:layout_width="0dp"
android:id="@+id/bluetooth_device_name"
style="@style/BluetoothTileDialog.DeviceName"
+ android:textDirection="locale"
+ android:textAlignment="gravity"
android:paddingStart="20dp"
android:paddingTop="10dp"
+ android:maxLines="1"
+ android:ellipsize="end"
app:layout_constraintWidth_percent="0.7"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@+id/bluetooth_device_icon"
@@ -55,6 +58,8 @@
style="@style/BluetoothTileDialog.DeviceSummary"
android:paddingStart="20dp"
android:paddingBottom="10dp"
+ android:maxLines="1"
+ android:ellipsize="end"
app:layout_constraintWidth_percent="0.7"
app:layout_constraintTop_toBottomOf="@+id/bluetooth_device_name"
app:layout_constraintStart_toEndOf="@+id/bluetooth_device_icon"
@@ -66,6 +71,7 @@
android:id="@+id/gear_icon"
android:layout_width="0dp"
android:layout_height="0dp"
+ android:contentDescription="@string/accessibility_bluetooth_device_settings_gear"
app:layout_constraintStart_toEndOf="@+id/bluetooth_device_name"
app:layout_constraintEnd_toEndOf="@+id/gear_icon_image"
app:layout_constraintTop_toTopOf="parent"
diff --git a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
index 16aeb95..5d986e0 100644
--- a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
+++ b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
@@ -27,6 +27,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="24dp"
+ android:maxLines="1"
android:ellipsize="end"
android:gravity="center_vertical|center_horizontal"
android:text="@string/quick_settings_bluetooth_label"
@@ -58,9 +59,12 @@
style="@style/BluetoothTileDialog.Device"
android:layout_width="0dp"
android:layout_height="64dp"
+ android:maxLines="1"
+ android:ellipsize="end"
android:gravity="center_vertical"
android:layout_marginTop="4dp"
android:text="@string/turn_on_bluetooth"
+ android:clickable="false"
android:textAppearance="@style/TextAppearance.Dialog.Body.Message"
android:textSize="16sp"
app:layout_constraintEnd_toStartOf="@+id/bluetooth_toggle"
@@ -84,53 +88,17 @@
app:layout_constraintStart_toEndOf="@+id/bluetooth_toggle_title"
app:layout_constraintTop_toBottomOf="@id/bluetooth_tile_dialog_subtitle" />
- <androidx.constraintlayout.widget.Group
- android:id="@+id/pair_new_device_layout_group"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:visibility="gone"
- app:constraint_referenced_ids="ic_add,pair_new_device_text" />
-
- <ImageView
- android:id="@+id/ic_add"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_marginStart="36dp"
- android:gravity="center_vertical"
- android:importantForAccessibility="no"
- android:src="@drawable/ic_add"
- app:layout_constraintBottom_toTopOf="@id/device_list"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toStartOf="@id/pair_new_device_text"
- app:layout_constraintTop_toBottomOf="@id/bluetooth_toggle_title"
- android:tint="?android:attr/textColorPrimary" />
-
- <TextView
- android:id="@+id/pair_new_device_text"
- style="@style/BluetoothTileDialog.Device"
- android:layout_width="0dp"
- android:layout_height="@dimen/bluetooth_dialog_device_height"
- android:gravity="center_vertical"
- android:layout_marginStart="0dp"
- android:paddingStart="20dp"
- android:text="@string/pair_new_bluetooth_devices"
- android:textSize="14sp"
- android:textAppearance="@style/TextAppearance.Dialog.Title"
- app:layout_constraintBottom_toTopOf="@id/device_list"
- app:layout_constraintStart_toEndOf="@+id/ic_add"
- app:layout_constraintTop_toBottomOf="@id/bluetooth_toggle_title"
- app:layout_constraintEnd_toEndOf="parent" />
-
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/device_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_marginTop="20dp"
android:nestedScrollingEnabled="false"
android:overScrollMode="never"
android:scrollbars="vertical"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toBottomOf="@id/pair_new_device_text"
+ app:layout_constraintTop_toBottomOf="@id/bluetooth_toggle"
app:layout_constraintBottom_toTopOf="@+id/see_all_text" />
<androidx.constraintlayout.widget.Group
@@ -148,7 +116,7 @@
android:importantForAccessibility="no"
android:gravity="center_vertical"
android:src="@drawable/ic_arrow_forward"
- app:layout_constraintBottom_toTopOf="@+id/done_button"
+ app:layout_constraintBottom_toTopOf="@+id/pair_new_device_text"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/see_all_text"
app:layout_constraintTop_toBottomOf="@id/device_list" />
@@ -157,18 +125,59 @@
android:id="@+id/see_all_text"
style="@style/BluetoothTileDialog.Device"
android:layout_width="0dp"
- android:layout_height="@dimen/bluetooth_dialog_device_height"
+ android:layout_height="64dp"
+ android:maxLines="1"
+ android:ellipsize="end"
android:gravity="center_vertical"
android:layout_marginStart="0dp"
android:paddingStart="20dp"
android:text="@string/see_all_bluetooth_devices"
android:textSize="14sp"
android:textAppearance="@style/TextAppearance.Dialog.Title"
- app:layout_constraintBottom_toTopOf="@+id/done_button"
+ app:layout_constraintBottom_toTopOf="@+id/pair_new_device_text"
app:layout_constraintStart_toEndOf="@+id/ic_arrow"
app:layout_constraintTop_toBottomOf="@id/device_list"
app:layout_constraintEnd_toEndOf="parent" />
+ <androidx.constraintlayout.widget.Group
+ android:id="@+id/pair_new_device_layout_group"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ app:constraint_referenced_ids="ic_add,pair_new_device_text" />
+
+ <ImageView
+ android:id="@+id/ic_add"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_marginStart="36dp"
+ android:gravity="center_vertical"
+ android:importantForAccessibility="no"
+ android:src="@drawable/ic_add"
+ app:layout_constraintBottom_toTopOf="@id/done_button"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/pair_new_device_text"
+ app:layout_constraintTop_toBottomOf="@id/see_all_text"
+ android:tint="?android:attr/textColorPrimary" />
+
+ <TextView
+ android:id="@+id/pair_new_device_text"
+ style="@style/BluetoothTileDialog.Device"
+ android:layout_width="0dp"
+ android:layout_height="64dp"
+ android:maxLines="1"
+ android:ellipsize="end"
+ android:gravity="center_vertical"
+ android:layout_marginStart="0dp"
+ android:paddingStart="20dp"
+ android:text="@string/pair_new_bluetooth_devices"
+ android:textSize="14sp"
+ android:textAppearance="@style/TextAppearance.Dialog.Title"
+ app:layout_constraintBottom_toTopOf="@id/done_button"
+ app:layout_constraintStart_toEndOf="@+id/ic_add"
+ app:layout_constraintTop_toBottomOf="@id/see_all_text"
+ app:layout_constraintEnd_toEndOf="parent" />
+
<Button
android:id="@+id/done_button"
style="@style/Widget.Dialog.Button"
@@ -184,5 +193,5 @@
android:text="@string/inline_done_button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toBottomOf="@id/see_all_text" />
+ app:layout_constraintTop_toBottomOf="@id/pair_new_device_text" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 6d54058..5a83c7d 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -852,6 +852,11 @@
'keyguard_affordance_horizontal_offset' -->
<dimen name="keyguard_indication_area_padding">82dp</dimen>
+ <!-- The width/padding of the communal tutorial indicator on keyguard. -->
+ <dimen name="communal_tutorial_indicator_fixed_width">168dp</dimen>
+ <dimen name="communal_tutorial_indicator_padding">24dp</dimen>
+ <dimen name="communal_tutorial_indicator_horizontal_offset">32dp</dimen>
+
<!-- The width/height of the unlock icon view on keyguard. -->
<dimen name="keyguard_lock_height">42dp</dimen>
<dimen name="keyguard_lock_padding">20dp</dimen>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 81101d8..05f4334 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -222,12 +222,14 @@
<item type="id" name="lock_icon" />
<item type="id" name="lock_icon_bg" />
<item type="id" name="burn_in_layer" />
+ <item type="id" name="communal_tutorial_indicator" />
<!-- Privacy dialog -->
<item type="id" name="privacy_dialog_close_app_button" />
<item type="id" name="privacy_dialog_manage_app_button" />
<!-- Communal mode -->
+ <item type="id" name="communal_hub" />
<item type="id" name="communal_widget_wrapper" />
<!-- Values assigned to the views in Biometrics Prompt -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index a2637d5..321594f 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1043,6 +1043,9 @@
<!-- Indication on the keyguard that is shown when the device is dock charging. [CHAR LIMIT=80]-->
<string name="keyguard_indication_charging_time_dock"><xliff:g id="percentage" example="20%">%2$s</xliff:g> • Charging • Full in <xliff:g id="charging_time_left" example="4 hr, 2 min">%1$s</xliff:g></string>
+ <!-- Indicator shown to start the communal tutorial. [CHAR LIMIT=100] -->
+ <string name="communal_tutorial_indicator_text">Click on the arrow button to start the communal tutorial</string>
+
<!-- Related to user switcher --><skip/>
<!-- Accessibility label for the button that opens the user switcher. -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 80040a3..631423e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -155,6 +155,26 @@
}
}
+
+ /**
+ * Requests for a new snapshot to be taken for the given task, stores it in the cache, and
+ * returns a {@link ThumbnailData} with the result.
+ */
+ @NonNull
+ public ThumbnailData takeTaskThumbnail(int taskId) {
+ TaskSnapshot snapshot = null;
+ try {
+ snapshot = getService().takeTaskSnapshot(taskId, /* updateCache= */ true);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to take task snapshot", e);
+ }
+ if (snapshot != null) {
+ return new ThumbnailData(snapshot);
+ } else {
+ return new ThumbnailData();
+ }
+ }
+
/**
* Removes the outdated snapshot of home task.
*
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
index 1e7222d..88b9c02 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
@@ -233,6 +233,11 @@
runner.onAnimationCancelled();
finishRunnable.run();
}
+
+ @Override
+ public void onTransitionConsumed(IBinder iBinder, boolean aborted)
+ throws RemoteException {
+ }
};
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index f6a0563..9bddcd7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -22,10 +22,10 @@
import com.android.app.animation.Interpolators;
import com.android.keyguard.dagger.KeyguardStatusViewScope;
-import com.android.systemui.res.R;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.core.LogLevel;
import com.android.systemui.plugins.ClockController;
+import com.android.systemui.res.R;
import com.android.systemui.shared.clocks.DefaultClockController;
import java.io.PrintWriter;
@@ -452,6 +452,10 @@
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
+ // TODO: b/305022530
+ if (mClock.getConfig().getId().equals("DIGITAL_CLOCK_METRO")) {
+ mClock.getEvents().onColorPaletteChanged(mContext.getResources());
+ }
if (changed) {
post(() -> updateClockTargetRegions());
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 50be97e..3585feb 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -44,8 +44,6 @@
import android.view.WindowInsets;
import android.view.WindowInsetsAnimationControlListener;
import android.view.WindowInsetsAnimationController;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
import android.widget.TextView;
import androidx.annotation.NonNull;
@@ -66,18 +64,8 @@
*/
public class KeyguardPasswordView extends KeyguardAbsKeyInputView {
- private final int mDisappearYTranslation;
-
- private static final long IME_DISAPPEAR_DURATION_MS = 125;
-
- // A delay constant to be used in a workaround for the situation where InputMethodManagerService
- // is not switched to the new user yet.
- // TODO: Remove this by ensuring such a race condition never happens.
-
private TextView mPasswordEntry;
private TextViewInputDisabler mPasswordEntryDisabler;
- private Interpolator mLinearOutSlowInInterpolator;
- private Interpolator mFastOutLinearInInterpolator;
private DisappearAnimationListener mDisappearAnimationListener;
@Nullable private MotionLayout mContainerMotionLayout;
private boolean mAlreadyUsingSplitBouncer = false;
@@ -93,12 +81,6 @@
public KeyguardPasswordView(Context context, AttributeSet attrs) {
super(context, attrs);
- mDisappearYTranslation = getResources().getDimensionPixelSize(
- R.dimen.disappear_y_translation);
- mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(
- context, android.R.interpolator.linear_out_slow_in);
- mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(
- context, android.R.interpolator.fast_out_linear_in);
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 3bf1482..b7bb35e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1250,6 +1250,7 @@
* @deprecated This is being migrated to use modern architecture, this method is visible purely
* for bridging the gap while the migration is active.
*/
+ @Deprecated
private void handleFaceAuthFailed() {
Assert.isMainThread();
String reason =
@@ -1278,6 +1279,7 @@
* @deprecated This is being migrated to use modern architecture, this method is visible purely
* for bridging the gap while the migration is active.
*/
+ @Deprecated
private void handleFaceAcquired(int acquireInfo) {
Assert.isMainThread();
for (int i = 0; i < mCallbacks.size(); i++) {
@@ -1299,6 +1301,7 @@
* @deprecated This is being migrated to use modern architecture, this method is visible purely
* for bridging the gap while the migration is active.
*/
+ @Deprecated
private void handleFaceAuthenticated(int authUserId, boolean isStrongBiometric) {
Trace.beginSection("KeyGuardUpdateMonitor#handlerFaceAuthenticated");
try {
@@ -1327,6 +1330,7 @@
* @deprecated This is being migrated to use modern architecture, this method is visible purely
* for bridging the gap while the migration is active.
*/
+ @Deprecated
private void handleFaceHelp(int msgId, String helpString) {
if (mFaceAcquiredInfoIgnoreList.contains(msgId)) {
return;
@@ -1344,6 +1348,7 @@
* @deprecated This is being migrated to use modern architecture, this method is visible purely
* for bridging the gap while the migration is active.
*/
+ @Deprecated
private void handleFaceError(int msgId, final String originalErrMsg) {
Assert.isMainThread();
String errString = originalErrMsg;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 7b59632..2476067 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -25,12 +25,14 @@
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.plugins.WeatherData;
import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.util.annotations.WeaklyReferencedCallback;
import java.util.TimeZone;
/**
* Callback for general information relevant to lock screen.
*/
+@WeaklyReferencedCallback
public class KeyguardUpdateMonitorCallback {
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 0d3f726..83da80f 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -692,10 +692,10 @@
mVelocityTracker.addMovement(event);
// Compute pointer velocity in pixels per second.
mVelocityTracker.computeCurrentVelocity(1000);
- float velocity = UdfpsController.computePointerSpeed(mVelocityTracker,
+ float velocity = computePointerSpeed(mVelocityTracker,
mActivePointerId);
if (event.getClassification() != MotionEvent.CLASSIFICATION_DEEP_PRESS
- && UdfpsController.exceedsVelocityThreshold(velocity)) {
+ && exceedsVelocityThreshold(velocity)) {
Log.v(TAG, "lock icon long-press rescheduled due to "
+ "high pointer velocity=" + velocity);
mLongPressCancelRunnable.run();
@@ -713,6 +713,23 @@
return true;
}
+ /**
+ * Calculate the pointer speed given a velocity tracker and the pointer id.
+ * This assumes that the velocity tracker has already been passed all relevant motion events.
+ */
+ private static float computePointerSpeed(@NonNull VelocityTracker tracker, int pointerId) {
+ final float vx = tracker.getXVelocity(pointerId);
+ final float vy = tracker.getYVelocity(pointerId);
+ return (float) Math.sqrt(Math.pow(vx, 2.0) + Math.pow(vy, 2.0));
+ }
+
+ /**
+ * Whether the velocity exceeds the acceptable UDFPS debouncing threshold.
+ */
+ private static boolean exceedsVelocityThreshold(float velocity) {
+ return velocity > 750f;
+ }
+
private boolean actionableDownEventStartedOnView(MotionEvent event) {
if (!isActionable()) {
return false;
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index 2dfb370..fe19616 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -451,7 +451,7 @@
}
fun logSubInfo(subInfo: SubscriptionInfo?) {
- logBuffer.log(TAG, VERBOSE, { str1 = "$subInfo" }, { "SubInfo:$str1" })
+ logBuffer.log(TAG, DEBUG, { str1 = "$subInfo" }, { "SubInfo:$str1" })
}
fun logTimeFormatChanged(newTimeFormat: String?) {
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 7739021..1a34cc4 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -15,123 +15,55 @@
package com.android.systemui;
import android.annotation.Nullable;
-import android.app.AlarmManager;
-import android.app.INotificationManager;
-import android.app.IWallpaperManager;
-import android.hardware.SensorPrivacyManager;
-import android.hardware.display.NightDisplayListener;
import android.os.Handler;
import android.os.Looper;
import android.util.ArrayMap;
-import android.util.DisplayMetrics;
-import android.view.IWindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.Preconditions;
-import com.android.keyguard.KeyguardSecurityModel;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
-import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
import com.android.systemui.animation.DialogLaunchAnimator;
-import com.android.systemui.appops.AppOpsController;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dock.DockManager;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.fragments.FragmentService;
-import com.android.systemui.keyguard.ScreenLifecycle;
-import com.android.systemui.keyguard.WakefulnessLifecycle;
-import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationModeController;
-import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
-import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.plugins.PluginDependencyProvider;
import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.power.EnhancedEstimates;
-import com.android.systemui.power.PowerUI;
-import com.android.systemui.privacy.PrivacyItemController;
-import com.android.systemui.qs.ReduceBrightColorsController;
-import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
import com.android.systemui.recents.OverviewProxyService;
-import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.settings.UserTracker;
-import com.android.systemui.shade.ShadeController;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
-import com.android.systemui.shared.system.PackageManagerWrapper;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.NotificationListener;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.SmartReplyController;
-import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.events.PrivacyDotViewController;
-import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager;
-import com.android.systemui.statusbar.phone.AutoHideController;
-import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import com.android.systemui.statusbar.phone.LightBarController;
-import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
-import com.android.systemui.statusbar.phone.ManagedProfileController;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider;
-import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.SystemUIDialogManager;
-import com.android.systemui.statusbar.policy.AccessibilityController;
-import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.BluetoothController;
-import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.FlashlightController;
-import com.android.systemui.statusbar.policy.HotspotController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.LocationController;
-import com.android.systemui.statusbar.policy.NextAlarmController;
-import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
-import com.android.systemui.statusbar.policy.RotationLockController;
-import com.android.systemui.statusbar.policy.SecurityController;
-import com.android.systemui.statusbar.policy.SensorPrivacyController;
-import com.android.systemui.statusbar.policy.SmartReplyConstants;
-import com.android.systemui.statusbar.policy.UserInfoController;
-import com.android.systemui.statusbar.policy.UserSwitcherController;
-import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.statusbar.window.StatusBarWindowController;
-import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.tuner.TunablePadding.TunablePaddingService;
import com.android.systemui.tuner.TunerService;
-import com.android.systemui.util.DeviceConfigProxy;
-import com.android.systemui.util.leak.GarbageMonitor;
-import com.android.systemui.util.leak.LeakDetector;
-import com.android.systemui.util.leak.LeakReporter;
-import com.android.systemui.util.sensors.AsyncSensorManager;
import dagger.Lazy;
-import java.util.concurrent.Executor;
import java.util.function.Consumer;
import javax.inject.Inject;
@@ -154,10 +86,6 @@
*/
@SysUISingleton
public class Dependency {
- /**
- * Key for getting a the main looper.
- */
- private static final String MAIN_LOOPER_NAME = "main_looper";
/**
* Key for getting a background Looper for background work.
@@ -171,15 +99,6 @@
* Generic handler on the main thread.
*/
private static final String MAIN_HANDLER_NAME = "main_handler";
- /**
- * Generic executor on the main thread.
- */
- private static final String MAIN_EXECUTOR_NAME = "main_executor";
-
- /**
- * Generic executor on a background thread.
- */
- private static final String BACKGROUND_EXECUTOR_NAME = "background_executor";
/**
* An email address to send memory leak reports to by default.
@@ -197,10 +116,6 @@
*/
public static final DependencyKey<Looper> BG_LOOPER = new DependencyKey<>(BG_LOOPER_NAME);
/**
- * Key for getting a mainer Looper.
- */
- public static final DependencyKey<Looper> MAIN_LOOPER = new DependencyKey<>(MAIN_LOOPER_NAME);
- /**
* Key for getting a Handler for receiving time tick broadcasts on.
*/
public static final DependencyKey<Handler> TIME_TICK_HANDLER =
@@ -211,133 +126,43 @@
public static final DependencyKey<Handler> MAIN_HANDLER =
new DependencyKey<>(MAIN_HANDLER_NAME);
- /**
- * Generic executor on the main thread.
- */
- public static final DependencyKey<Executor> MAIN_EXECUTOR =
- new DependencyKey<>(MAIN_EXECUTOR_NAME);
- /**
- * Generic executor on a background thread.
- */
- public static final DependencyKey<Executor> BACKGROUND_EXECUTOR =
- new DependencyKey<>(BACKGROUND_EXECUTOR_NAME);
-
- /**
- * An email address to send memory leak reports to by default.
- */
- public static final DependencyKey<String> LEAK_REPORT_EMAIL =
- new DependencyKey<>(LEAK_REPORT_EMAIL_NAME);
-
private final ArrayMap<Object, Object> mDependencies = new ArrayMap<>();
private final ArrayMap<Object, LazyDependencyCreator> mProviders = new ArrayMap<>();
@Inject DumpManager mDumpManager;
- @Inject Lazy<ActivityStarter> mActivityStarter;
@Inject Lazy<BroadcastDispatcher> mBroadcastDispatcher;
- @Inject Lazy<AsyncSensorManager> mAsyncSensorManager;
@Inject Lazy<BluetoothController> mBluetoothController;
- @Inject Lazy<LocationController> mLocationController;
- @Inject Lazy<RotationLockController> mRotationLockController;
- @Inject Lazy<ZenModeController> mZenModeController;
- @Inject Lazy<HotspotController> mHotspotController;
- @Inject Lazy<CastController> mCastController;
@Inject Lazy<FlashlightController> mFlashlightController;
- @Inject Lazy<UserSwitcherController> mUserSwitcherController;
- @Inject Lazy<UserInfoController> mUserInfoController;
- @Inject Lazy<KeyguardStateController> mKeyguardMonitor;
@Inject Lazy<KeyguardUpdateMonitor> mKeyguardUpdateMonitor;
- @Inject Lazy<NightDisplayListener> mNightDisplayListener;
- @Inject Lazy<ReduceBrightColorsController> mReduceBrightColorsController;
- @Inject Lazy<ManagedProfileController> mManagedProfileController;
- @Inject Lazy<NextAlarmController> mNextAlarmController;
- @Inject Lazy<DataSaverController> mDataSaverController;
- @Inject Lazy<AccessibilityController> mAccessibilityController;
@Inject Lazy<DeviceProvisionedController> mDeviceProvisionedController;
@Inject Lazy<PluginManager> mPluginManager;
@Inject Lazy<AssistManager> mAssistManager;
- @Inject Lazy<SecurityController> mSecurityController;
- @Inject Lazy<LeakDetector> mLeakDetector;
- @Inject Lazy<LeakReporter> mLeakReporter;
- @Inject Lazy<GarbageMonitor> mGarbageMonitor;
@Inject Lazy<TunerService> mTunerService;
- @Inject Lazy<NotificationShadeWindowController> mNotificationShadeWindowController;
- @Inject Lazy<StatusBarWindowController> mTempStatusBarWindowController;
@Inject Lazy<DarkIconDispatcher> mDarkIconDispatcher;
- @Inject Lazy<StatusBarIconController> mStatusBarIconController;
- @Inject Lazy<ScreenLifecycle> mScreenLifecycle;
- @Inject Lazy<WakefulnessLifecycle> mWakefulnessLifecycle;
@Inject Lazy<FragmentService> mFragmentService;
- @Inject Lazy<ExtensionController> mExtensionController;
- @Inject Lazy<PluginDependencyProvider> mPluginDependencyProvider;
@Nullable
- @Inject Lazy<LocalBluetoothManager> mLocalBluetoothManager;
@Inject Lazy<VolumeDialogController> mVolumeDialogController;
@Inject Lazy<MetricsLogger> mMetricsLogger;
- @Inject Lazy<AccessibilityManagerWrapper> mAccessibilityManagerWrapper;
- @Inject Lazy<SysuiColorExtractor> mSysuiColorExtractor;
@Inject Lazy<TunablePaddingService> mTunablePaddingService;
@Inject Lazy<UiOffloadThread> mUiOffloadThread;
- @Inject Lazy<PowerUI.WarningsUI> mWarningsUI;
@Inject Lazy<LightBarController> mLightBarController;
- @Inject Lazy<IWindowManager> mIWindowManager;
@Inject Lazy<OverviewProxyService> mOverviewProxyService;
@Inject Lazy<NavigationModeController> mNavBarModeController;
@Inject Lazy<AccessibilityButtonModeObserver> mAccessibilityButtonModeObserver;
@Inject Lazy<AccessibilityButtonTargetsObserver> mAccessibilityButtonListController;
- @Inject Lazy<EnhancedEstimates> mEnhancedEstimates;
- @Inject Lazy<VibratorHelper> mVibratorHelper;
@Inject Lazy<IStatusBarService> mIStatusBarService;
- @Inject Lazy<DisplayMetrics> mDisplayMetrics;
- @Inject Lazy<LockscreenGestureLogger> mLockscreenGestureLogger;
- @Inject Lazy<ShadeController> mShadeController;
@Inject Lazy<NotificationRemoteInputManager.Callback> mNotificationRemoteInputManagerCallback;
- @Inject Lazy<AppOpsController> mAppOpsController;
@Inject Lazy<NavigationBarController> mNavigationBarController;
- @Inject Lazy<AccessibilityFloatingMenuController> mAccessibilityFloatingMenuController;
@Inject Lazy<StatusBarStateController> mStatusBarStateController;
- @Inject Lazy<NotificationLockscreenUserManager> mNotificationLockscreenUserManager;
@Inject Lazy<NotificationMediaManager> mNotificationMediaManager;
- @Inject Lazy<NotificationRemoteInputManager> mNotificationRemoteInputManager;
- @Inject Lazy<SmartReplyConstants> mSmartReplyConstants;
- @Inject Lazy<NotificationListener> mNotificationListener;
- @Inject Lazy<NotificationLogger> mNotificationLogger;
- @Inject Lazy<KeyguardDismissUtil> mKeyguardDismissUtil;
- @Inject Lazy<SmartReplyController> mSmartReplyController;
- @Inject Lazy<RemoteInputQuickSettingsDisabler> mRemoteInputQuickSettingsDisabler;
- @Inject Lazy<SensorPrivacyManager> mSensorPrivacyManager;
- @Inject Lazy<AutoHideController> mAutoHideController;
- @Inject Lazy<PrivacyItemController> mPrivacyItemController;
@Inject @Background Lazy<Looper> mBgLooper;
- @Inject @Background Lazy<Handler> mBgHandler;
- @Inject @Main Lazy<Looper> mMainLooper;
@Inject @Main Lazy<Handler> mMainHandler;
@Inject @Named(TIME_TICK_HANDLER_NAME) Lazy<Handler> mTimeTickHandler;
- @Inject @Named(LEAK_REPORT_EMAIL_NAME) Lazy<String> mLeakReportEmail;
- @Inject @Main Lazy<Executor> mMainExecutor;
- @Inject @Background Lazy<Executor> mBackgroundExecutor;
- @Inject Lazy<ActivityManagerWrapper> mActivityManagerWrapper;
- @Inject Lazy<DevicePolicyManagerWrapper> mDevicePolicyManagerWrapper;
- @Inject Lazy<PackageManagerWrapper> mPackageManagerWrapper;
- @Inject Lazy<SensorPrivacyController> mSensorPrivacyController;
- @Inject Lazy<DockManager> mDockManager;
- @Inject Lazy<INotificationManager> mINotificationManager;
@Inject Lazy<SysUiState> mSysUiStateFlagsContainer;
- @Inject Lazy<AlarmManager> mAlarmManager;
- @Inject Lazy<KeyguardSecurityModel> mKeyguardSecurityModel;
- @Inject Lazy<DozeParameters> mDozeParameters;
- @Inject Lazy<IWallpaperManager> mWallpaperManager;
@Inject Lazy<CommandQueue> mCommandQueue;
- @Inject Lazy<RecordingController> mRecordingController;
- @Inject Lazy<MediaOutputDialogFactory> mMediaOutputDialogFactory;
- @Inject Lazy<DeviceConfigProxy> mDeviceConfigProxy;
- @Inject Lazy<TelephonyListenerManager> mTelephonyListenerManager;
- @Inject Lazy<SystemStatusAnimationScheduler> mSystemStatusAnimationSchedulerLazy;
- @Inject Lazy<PrivacyDotViewController> mPrivacyDotViewControllerLazy;
- @Inject Lazy<EdgeBackGestureHandler.Factory> mEdgeBackGestureHandlerFactoryLazy;
@Inject Lazy<UiEventLogger> mUiEventLogger;
@Inject Lazy<StatusBarContentInsetsProvider> mContentInsetsProviderLazy;
- @Inject Lazy<InternetDialogFactory> mInternetDialogFactory;
@Inject Lazy<FeatureFlags> mFeatureFlagsLazy;
@Inject Lazy<NotificationSectionsManager> mNotificationSectionsManagerLazy;
@Inject Lazy<ScreenOffAnimationController> mScreenOffAnimationController;
@@ -360,183 +185,36 @@
// on imports.
mProviders.put(TIME_TICK_HANDLER, mTimeTickHandler::get);
mProviders.put(BG_LOOPER, mBgLooper::get);
- mProviders.put(MAIN_LOOPER, mMainLooper::get);
mProviders.put(MAIN_HANDLER, mMainHandler::get);
- mProviders.put(MAIN_EXECUTOR, mMainExecutor::get);
- mProviders.put(BACKGROUND_EXECUTOR, mBackgroundExecutor::get);
- mProviders.put(ActivityStarter.class, mActivityStarter::get);
mProviders.put(BroadcastDispatcher.class, mBroadcastDispatcher::get);
-
- mProviders.put(AsyncSensorManager.class, mAsyncSensorManager::get);
-
mProviders.put(BluetoothController.class, mBluetoothController::get);
- mProviders.put(SensorPrivacyManager.class, mSensorPrivacyManager::get);
-
- mProviders.put(LocationController.class, mLocationController::get);
-
- mProviders.put(RotationLockController.class, mRotationLockController::get);
-
- mProviders.put(ZenModeController.class, mZenModeController::get);
-
- mProviders.put(HotspotController.class, mHotspotController::get);
-
- mProviders.put(CastController.class, mCastController::get);
-
mProviders.put(FlashlightController.class, mFlashlightController::get);
-
- mProviders.put(KeyguardStateController.class, mKeyguardMonitor::get);
-
mProviders.put(KeyguardUpdateMonitor.class, mKeyguardUpdateMonitor::get);
-
- mProviders.put(UserSwitcherController.class, mUserSwitcherController::get);
-
- mProviders.put(UserInfoController.class, mUserInfoController::get);
-
- mProviders.put(NightDisplayListener.class, mNightDisplayListener::get);
-
- mProviders.put(ReduceBrightColorsController.class, mReduceBrightColorsController::get);
-
- mProviders.put(ManagedProfileController.class, mManagedProfileController::get);
-
- mProviders.put(NextAlarmController.class, mNextAlarmController::get);
-
- mProviders.put(DataSaverController.class, mDataSaverController::get);
-
- mProviders.put(AccessibilityController.class, mAccessibilityController::get);
-
mProviders.put(DeviceProvisionedController.class, mDeviceProvisionedController::get);
-
mProviders.put(PluginManager.class, mPluginManager::get);
-
mProviders.put(AssistManager.class, mAssistManager::get);
-
- mProviders.put(SecurityController.class, mSecurityController::get);
-
- mProviders.put(LeakDetector.class, mLeakDetector::get);
-
- mProviders.put(LEAK_REPORT_EMAIL, mLeakReportEmail::get);
-
- mProviders.put(LeakReporter.class, mLeakReporter::get);
-
- mProviders.put(GarbageMonitor.class, mGarbageMonitor::get);
-
mProviders.put(TunerService.class, mTunerService::get);
-
- mProviders.put(NotificationShadeWindowController.class,
- mNotificationShadeWindowController::get);
-
- mProviders.put(StatusBarWindowController.class, mTempStatusBarWindowController::get);
-
mProviders.put(DarkIconDispatcher.class, mDarkIconDispatcher::get);
-
- mProviders.put(StatusBarIconController.class, mStatusBarIconController::get);
-
- mProviders.put(ScreenLifecycle.class, mScreenLifecycle::get);
-
- mProviders.put(WakefulnessLifecycle.class, mWakefulnessLifecycle::get);
-
mProviders.put(FragmentService.class, mFragmentService::get);
-
- mProviders.put(ExtensionController.class, mExtensionController::get);
-
- mProviders.put(PluginDependencyProvider.class, mPluginDependencyProvider::get);
-
- mProviders.put(LocalBluetoothManager.class, mLocalBluetoothManager::get);
-
mProviders.put(VolumeDialogController.class, mVolumeDialogController::get);
-
mProviders.put(MetricsLogger.class, mMetricsLogger::get);
-
- mProviders.put(AccessibilityManagerWrapper.class, mAccessibilityManagerWrapper::get);
-
- mProviders.put(SysuiColorExtractor.class, mSysuiColorExtractor::get);
-
mProviders.put(TunablePaddingService.class, mTunablePaddingService::get);
-
mProviders.put(UiOffloadThread.class, mUiOffloadThread::get);
-
- mProviders.put(PowerUI.WarningsUI.class, mWarningsUI::get);
-
mProviders.put(LightBarController.class, mLightBarController::get);
-
- mProviders.put(IWindowManager.class, mIWindowManager::get);
-
mProviders.put(OverviewProxyService.class, mOverviewProxyService::get);
-
mProviders.put(NavigationModeController.class, mNavBarModeController::get);
-
mProviders.put(AccessibilityButtonModeObserver.class,
mAccessibilityButtonModeObserver::get);
mProviders.put(AccessibilityButtonTargetsObserver.class,
mAccessibilityButtonListController::get);
-
- mProviders.put(EnhancedEstimates.class, mEnhancedEstimates::get);
-
- mProviders.put(VibratorHelper.class, mVibratorHelper::get);
-
mProviders.put(IStatusBarService.class, mIStatusBarService::get);
-
- mProviders.put(DisplayMetrics.class, mDisplayMetrics::get);
-
- mProviders.put(LockscreenGestureLogger.class, mLockscreenGestureLogger::get);
-
- mProviders.put(ShadeController.class, mShadeController::get);
-
mProviders.put(NotificationRemoteInputManager.Callback.class,
mNotificationRemoteInputManagerCallback::get);
-
- mProviders.put(AppOpsController.class, mAppOpsController::get);
-
mProviders.put(NavigationBarController.class, mNavigationBarController::get);
-
- mProviders.put(AccessibilityFloatingMenuController.class,
- mAccessibilityFloatingMenuController::get);
-
mProviders.put(StatusBarStateController.class, mStatusBarStateController::get);
- mProviders.put(NotificationLockscreenUserManager.class,
- mNotificationLockscreenUserManager::get);
mProviders.put(NotificationMediaManager.class, mNotificationMediaManager::get);
- mProviders.put(NotificationRemoteInputManager.class,
- mNotificationRemoteInputManager::get);
- mProviders.put(SmartReplyConstants.class, mSmartReplyConstants::get);
- mProviders.put(NotificationListener.class, mNotificationListener::get);
- mProviders.put(NotificationLogger.class, mNotificationLogger::get);
- mProviders.put(KeyguardDismissUtil.class, mKeyguardDismissUtil::get);
- mProviders.put(SmartReplyController.class, mSmartReplyController::get);
- mProviders.put(RemoteInputQuickSettingsDisabler.class,
- mRemoteInputQuickSettingsDisabler::get);
- mProviders.put(PrivacyItemController.class, mPrivacyItemController::get);
- mProviders.put(ActivityManagerWrapper.class, mActivityManagerWrapper::get);
- mProviders.put(DevicePolicyManagerWrapper.class, mDevicePolicyManagerWrapper::get);
- mProviders.put(PackageManagerWrapper.class, mPackageManagerWrapper::get);
- mProviders.put(SensorPrivacyController.class, mSensorPrivacyController::get);
- mProviders.put(DockManager.class, mDockManager::get);
- mProviders.put(INotificationManager.class, mINotificationManager::get);
mProviders.put(SysUiState.class, mSysUiStateFlagsContainer::get);
- mProviders.put(AlarmManager.class, mAlarmManager::get);
- mProviders.put(KeyguardSecurityModel.class, mKeyguardSecurityModel::get);
- mProviders.put(DozeParameters.class, mDozeParameters::get);
- mProviders.put(IWallpaperManager.class, mWallpaperManager::get);
mProviders.put(CommandQueue.class, mCommandQueue::get);
- mProviders.put(DeviceConfigProxy.class, mDeviceConfigProxy::get);
- mProviders.put(TelephonyListenerManager.class, mTelephonyListenerManager::get);
-
- // TODO(b/118592525): to support multi-display , we start to add something which is
- // per-display, while others may be global. I think it's time to add
- // a new class maybe named DisplayDependency to solve per-display
- // Dependency problem.
- mProviders.put(AutoHideController.class, mAutoHideController::get);
-
- mProviders.put(RecordingController.class, mRecordingController::get);
-
- mProviders.put(MediaOutputDialogFactory.class, mMediaOutputDialogFactory::get);
-
- mProviders.put(SystemStatusAnimationScheduler.class,
- mSystemStatusAnimationSchedulerLazy::get);
- mProviders.put(PrivacyDotViewController.class, mPrivacyDotViewControllerLazy::get);
- mProviders.put(InternetDialogFactory.class, mInternetDialogFactory::get);
- mProviders.put(EdgeBackGestureHandler.Factory.class,
- mEdgeBackGestureHandlerFactoryLazy::get);
mProviders.put(UiEventLogger.class, mUiEventLogger::get);
mProviders.put(FeatureFlags.class, mFeatureFlagsLazy::get);
mProviders.put(StatusBarContentInsetsProvider.class, mContentInsetsProviderLazy::get);
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 54dbf72..6faee8c 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -83,7 +83,7 @@
import com.android.systemui.decor.RoundedCornerResDelegateImpl;
import com.android.systemui.decor.ScreenDecorCommand;
import com.android.systemui.log.ScreenDecorationsLogger;
-import com.android.systemui.qs.SettingObserver;
+import com.android.systemui.qs.UserSettingObserver;
import com.android.systemui.res.R;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserTracker;
@@ -165,7 +165,7 @@
ScreenDecorHwcLayer mScreenDecorHwcLayer;
private WindowManager mWindowManager;
private int mRotation;
- private SettingObserver mColorInversionSetting;
+ private UserSettingObserver mColorInversionSetting;
@Nullable
private DelayableExecutor mExecutor;
private Handler mHandler;
@@ -686,7 +686,7 @@
// Watch color inversion and invert the overlay as needed.
if (mColorInversionSetting == null) {
- mColorInversionSetting = new SettingObserver(mSecureSettings, mHandler,
+ mColorInversionSetting = new UserSettingObserver(mSecureSettings, mHandler,
Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED,
mUserTracker.getUserId()) {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java b/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
index 1ee06cc..56273eb 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
@@ -35,18 +35,19 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.res.R;
import com.android.systemui.assist.AssistLogger;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.assist.AssistantSessionEvent;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.navigationbar.NavigationBarController;
+import com.android.systemui.res.R;
+
+import dagger.Lazy;
import java.util.Locale;
import javax.inject.Inject;
-import dagger.Lazy;
-
/**
* Default UiController implementation. Shows white edge lights along the bottom of the phone,
* expanding from the corners to meet in the center.
@@ -80,7 +81,8 @@
@Inject
public DefaultUiController(Context context, AssistLogger assistLogger,
WindowManager windowManager, MetricsLogger metricsLogger,
- Lazy<AssistManager> assistManagerLazy) {
+ Lazy<AssistManager> assistManagerLazy,
+ NavigationBarController navigationBarController) {
mAssistLogger = assistLogger;
mRoot = new FrameLayout(context);
mWindowManager = windowManager;
@@ -103,6 +105,7 @@
mInvocationLightsView = (InvocationLightsView)
LayoutInflater.from(context).inflate(R.layout.invocation_lights, mRoot, false);
+ mInvocationLightsView.setNavigationBarController(navigationBarController);
mRoot.addView(mInvocationLightsView);
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/ui/InvocationLightsView.java b/packages/SystemUI/src/com/android/systemui/assist/ui/InvocationLightsView.java
index 4d89231..0cdb376 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/ui/InvocationLightsView.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/ui/InvocationLightsView.java
@@ -31,11 +31,10 @@
import android.view.View;
import com.android.settingslib.Utils;
-import com.android.systemui.Dependency;
-import com.android.systemui.res.R;
-import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBar;
+import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarTransitions;
+import com.android.systemui.res.R;
import java.util.ArrayList;
@@ -64,6 +63,8 @@
private final int mLightColor;
@ColorInt
private final int mDarkColor;
+ @Nullable
+ private NavigationBarController mNavigationBarController;
// Allocate variable for screen location lookup to avoid memory alloc onDraw()
private int[] mScreenLocation = new int[2];
@@ -279,12 +280,11 @@
private void attemptRegisterNavBarListener() {
if (!mRegistered) {
- NavigationBarController controller = Dependency.get(NavigationBarController.class);
- if (controller == null) {
+ if (mNavigationBarController == null) {
return;
}
- NavigationBar navBar = controller.getDefaultNavigationBar();
+ NavigationBar navBar = mNavigationBarController.getDefaultNavigationBar();
if (navBar == null) {
return;
}
@@ -296,12 +296,11 @@
private void attemptUnregisterNavBarListener() {
if (mRegistered) {
- NavigationBarController controller = Dependency.get(NavigationBarController.class);
- if (controller == null) {
+ if (mNavigationBarController == null) {
return;
}
- NavigationBar navBar = controller.getDefaultNavigationBar();
+ NavigationBar navBar = mNavigationBarController.getDefaultNavigationBar();
if (navBar == null) {
return;
}
@@ -310,4 +309,8 @@
mRegistered = false;
}
}
+
+ public void setNavigationBarController(NavigationBarController navigationBarController) {
+ mNavigationBarController = navigationBarController;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AlternateUdfpsTouchProvider.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AlternateUdfpsTouchProvider.kt
deleted file mode 100644
index ca4b8ef..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AlternateUdfpsTouchProvider.kt
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * 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.systemui.biometrics
-
-/**
- * Interface for controlling the on finger down & on finger up events.
- */
-interface AlternateUdfpsTouchProvider {
-
- /**
- * onPointerDown:
- *
- * This operation is used to notify the Fingerprint HAL that
- * a fingerprint has been detected on the device's screen.
- *
- * See fingerprint/ISession#onPointerDown for more details.
- */
- fun onPointerDown(pointerId: Long, x: Int, y: Int, minor: Float, major: Float)
-
- /**
- * onPointerUp:
- *
- * This operation can be invoked when the HAL is performing any one of: ISession#authenticate,
- * ISession#enroll, ISession#detectInteraction. This operation is used to indicate
- * that a fingerprint that was previously down, is now up.
- *
- * See fingerprint/ISession#onPointerUp for more details.
- */
- fun onPointerUp(pointerId: Long)
-
- /**
- * onUiReady:
- *
- * This operation is used by the callee to notify the Fingerprint HAL that SystemUI is
- * correctly configured for the fingerprint capture.
- *
- * See fingerprint/ISession#onUiReady for more details.
- */
- fun onUiReady()
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
index bdad413..395f68c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
@@ -37,9 +37,6 @@
private float mDialogSuggestedAlpha = 1f;
private float mNotificationShadeExpansion = 0f;
- // Used for Udfps ellipse detection when flag is true, set by AnimationViewController
- boolean mUseExpandedOverlay = false;
-
// mAlpha takes into consideration the status bar expansion amount and dialog suggested alpha
private int mAlpha;
boolean mPauseAuth;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 79d9c1b..c9e4cbe 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -32,7 +32,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.SensorProperties;
@@ -54,7 +53,6 @@
import android.view.HapticFeedbackConstants;
import android.view.LayoutInflater;
import android.view.MotionEvent;
-import android.view.VelocityTracker;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
@@ -84,7 +82,6 @@
import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter;
@@ -102,7 +99,6 @@
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.Execution;
-import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.time.SystemClock;
import kotlin.Unit;
@@ -110,7 +106,6 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
-import java.util.Optional;
import java.util.Set;
import java.util.concurrent.Executor;
@@ -136,13 +131,8 @@
private static final String TAG = "UdfpsController";
private static final long AOD_SEND_FINGER_UP_DELAY_MILLIS = 1000;
- // Minimum required delay between consecutive touch logs in milliseconds.
- private static final long MIN_TOUCH_LOG_INTERVAL = 50;
private static final long MIN_UNCHANGED_INTERACTION_LOG_INTERVAL = 50;
- // This algorithm checks whether the touch is within the sensor's bounding box.
- private static final int BOUNDING_BOX_TOUCH_CONFIG_ID = 0;
-
private final Context mContext;
private final Execution mExecution;
private final FingerprintManager mFingerprintManager;
@@ -175,8 +165,6 @@
@Nullable private final TouchProcessor mTouchProcessor;
@NonNull private final SessionTracker mSessionTracker;
@NonNull private final AlternateBouncerInteractor mAlternateBouncerInteractor;
- @NonNull private final SecureSettings mSecureSettings;
- @NonNull private final UdfpsUtils mUdfpsUtils;
@NonNull private final InputManager mInputManager;
@NonNull private final UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate;
private final boolean mIgnoreRefreshRate;
@@ -187,11 +175,8 @@
@VisibleForTesting @NonNull UdfpsOverlayParams mOverlayParams = new UdfpsOverlayParams();
// TODO(b/229290039): UDFPS controller should manage its dimensions on its own. Remove this.
@Nullable private Runnable mAuthControllerUpdateUdfpsLocation;
- @Nullable private final AlternateUdfpsTouchProvider mAlternateTouchProvider;
@Nullable private UdfpsDisplayModeProvider mUdfpsDisplayMode;
- // Tracks the velocity of a touch to help filter out the touches that move too fast.
- @Nullable private VelocityTracker mVelocityTracker;
// The ID of the pointer for which ACTION_DOWN has occurred. -1 means no pointer is active.
private int mActivePointerId = -1;
// Whether a pointer has been pilfered for current gesture
@@ -259,8 +244,6 @@
final int touchConfigId = mContext.getResources().getInteger(
com.android.internal.R.integer.config_selected_udfps_touch_detection);
pw.println("mSensorProps=(" + mSensorProps + ")");
- pw.println("Using new touch detection framework: " + mFeatureFlags.isEnabled(
- Flags.UDFPS_NEW_TOUCH_DETECTION));
pw.println("touchConfigId: " + touchConfigId);
}
@@ -272,7 +255,6 @@
mFgExecutor.execute(() -> UdfpsController.this.showUdfpsOverlay(
new UdfpsControllerOverlay(
mContext,
- mFingerprintManager,
mInflater,
mWindowManager,
mAccessibilityManager,
@@ -286,7 +268,6 @@
mKeyguardStateController,
mUnlockedScreenOffAnimationController,
mUdfpsDisplayMode,
- mSecureSettings,
requestId,
reason,
callback,
@@ -299,7 +280,6 @@
mFeatureFlags,
mPrimaryBouncerInteractor,
mAlternateBouncerInteractor,
- mUdfpsUtils,
mUdfpsKeyguardAccessibilityDelegate,
mUdfpsKeyguardViewModels
)));
@@ -376,13 +356,9 @@
* Debug to run onUiReady
*/
public void debugOnUiReady(int sensorId) {
- if (UdfpsController.this.mAlternateTouchProvider != null) {
- UdfpsController.this.mAlternateTouchProvider.onUiReady();
- } else {
- final long requestId = (mOverlay != null) ? mOverlay.getRequestId() : 0L;
- UdfpsController.this.mFingerprintManager.onUdfpsUiEvent(
- FingerprintManager.UDFPS_UI_READY, requestId, sensorId);
- }
+ final long requestId = (mOverlay != null) ? mOverlay.getRequestId() : 0L;
+ UdfpsController.this.mFingerprintManager.onUdfpsUiEvent(
+ FingerprintManager.UDFPS_UI_READY, requestId, sensorId);
}
}
@@ -423,23 +399,6 @@
mUdfpsDisplayMode = udfpsDisplayMode;
}
- /**
- * Calculate the pointer speed given a velocity tracker and the pointer id.
- * This assumes that the velocity tracker has already been passed all relevant motion events.
- */
- public static float computePointerSpeed(@NonNull VelocityTracker tracker, int pointerId) {
- final float vx = tracker.getXVelocity(pointerId);
- final float vy = tracker.getYVelocity(pointerId);
- return (float) Math.sqrt(Math.pow(vx, 2.0) + Math.pow(vy, 2.0));
- }
-
- /**
- * Whether the velocity exceeds the acceptable UDFPS debouncing threshold.
- */
- public static boolean exceedsVelocityThreshold(float velocity) {
- return velocity > 750f;
- }
-
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -457,38 +416,6 @@
}
};
- /**
- * Forwards touches to the udfps controller / view
- */
- public boolean onTouch(MotionEvent event) {
- if (mOverlay == null || mOverlay.isHiding()) {
- return false;
- }
- // TODO(b/225068271): may not be correct but no way to get the id yet
- return onTouch(mOverlay.getRequestId(), event, false);
- }
-
- /**
- * @param x coordinate
- * @param y coordinate
- * @param relativeToUdfpsView true if the coordinates are relative to the udfps view; else,
- * calculate from the display dimensions in portrait orientation
- */
- private boolean isWithinSensorArea(UdfpsView udfpsView, float x, float y,
- boolean relativeToUdfpsView) {
- if (relativeToUdfpsView) {
- // TODO: move isWithinSensorArea to UdfpsController.
- return udfpsView.isWithinSensorArea(x, y);
- }
-
- if (mOverlay == null || mOverlay.getAnimationViewController() == null) {
- return false;
- }
-
- return !mOverlay.getAnimationViewController().shouldPauseAuth()
- && mOverlayParams.getSensorBounds().contains((int) x, (int) y);
- }
-
private void tryDismissingKeyguard() {
if (!mOnFingerDown) {
playStartHaptic();
@@ -497,15 +424,6 @@
mAttemptedToDismissKeyguard = true;
}
- @VisibleForTesting
- boolean onTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) {
- if (mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) {
- return newOnTouch(requestId, event, fromUdfpsView);
- } else {
- return oldOnTouch(requestId, event, fromUdfpsView);
- }
- }
-
private int getBiometricSessionType() {
if (mOverlay == null) {
return -1;
@@ -566,7 +484,7 @@
}
}
- private boolean newOnTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) {
+ private boolean onTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) {
if (!fromUdfpsView) {
Log.e(TAG, "ignoring the touch injected from outside of UdfpsView");
return false;
@@ -580,7 +498,6 @@
+ mOverlay.getRequestId());
return false;
}
-
if ((mLockscreenShadeTransitionController.getQSDragProgress() != 0f
&& !mAlternateBouncerInteractor.isVisibleState())
|| mPrimaryBouncerInteractor.isInTransit()) {
@@ -654,8 +571,7 @@
break;
case UNCHANGED:
- if (!isWithinSensorArea(mOverlay.getOverlayView(), event.getRawX(), event.getRawY(),
- true) && mActivePointerId == MotionEvent.INVALID_POINTER_ID
+ if (mActivePointerId == MotionEvent.INVALID_POINTER_ID
&& mAlternateBouncerInteractor.isVisibleState()) {
// No pointer on sensor, forward to keyguard if alternateBouncer is visible
mKeyguardViewManager.onTouch(event);
@@ -667,7 +583,7 @@
logBiometricTouch(processedTouch.getEvent(), data);
// Always pilfer pointers that are within sensor area or when alternate bouncer is showing
- if (isWithinSensorArea(mOverlay.getOverlayView(), event.getRawX(), event.getRawY(), true)
+ if (mActivePointerId != MotionEvent.INVALID_POINTER_ID
|| mAlternateBouncerInteractor.isVisibleState()) {
shouldPilfer = true;
}
@@ -680,146 +596,7 @@
mPointerPilfered = true;
}
- return processedTouch.getTouchData().isWithinBounds(mOverlayParams.getNativeSensorBounds());
- }
-
- private boolean oldOnTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) {
- if (mOverlay == null) {
- Log.w(TAG, "ignoring onTouch with null overlay");
- return false;
- }
- if (!mOverlay.matchesRequestId(requestId)) {
- Log.w(TAG, "ignoring stale touch event: " + requestId + " current: "
- + mOverlay.getRequestId());
- return false;
- }
-
- final UdfpsView udfpsView = mOverlay.getOverlayView();
- boolean handled = false;
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- case MotionEvent.ACTION_HOVER_ENTER:
- Trace.beginSection("UdfpsController.onTouch.ACTION_DOWN");
- // To simplify the lifecycle of the velocity tracker, make sure it's never null
- // after ACTION_DOWN, and always null after ACTION_CANCEL or ACTION_UP.
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- } else {
- // ACTION_UP or ACTION_CANCEL is not guaranteed to be called before a new
- // ACTION_DOWN, in that case we should just reuse the old instance.
- mVelocityTracker.clear();
- }
-
- final boolean withinSensorArea =
- isWithinSensorArea(udfpsView, event.getX(), event.getY(), fromUdfpsView);
- if (withinSensorArea) {
- Trace.beginAsyncSection("UdfpsController.e2e.onPointerDown", 0);
- Log.v(TAG, "onTouch | action down");
- // The pointer that causes ACTION_DOWN is always at index 0.
- // We need to persist its ID to track it during ACTION_MOVE that could include
- // data for many other pointers because of multi-touch support.
- mActivePointerId = event.getPointerId(0);
- mVelocityTracker.addMovement(event);
- handled = true;
- mAcquiredReceived = false;
- }
- if ((withinSensorArea || fromUdfpsView) && shouldTryToDismissKeyguard()) {
- Log.v(TAG, "onTouch | dismiss keyguard ACTION_DOWN");
- tryDismissingKeyguard();
- }
-
- Trace.endSection();
- break;
-
- case MotionEvent.ACTION_MOVE:
- case MotionEvent.ACTION_HOVER_MOVE:
- Trace.beginSection("UdfpsController.onTouch.ACTION_MOVE");
- final int idx = mActivePointerId == -1
- ? event.getPointerId(0)
- : event.findPointerIndex(mActivePointerId);
- if (idx == event.getActionIndex()) {
- final boolean actionMoveWithinSensorArea =
- isWithinSensorArea(udfpsView, event.getX(idx), event.getY(idx),
- fromUdfpsView);
- if ((fromUdfpsView || actionMoveWithinSensorArea)
- && shouldTryToDismissKeyguard()) {
- Log.v(TAG, "onTouch | dismiss keyguard ACTION_MOVE");
- tryDismissingKeyguard();
- break;
- }
- // Map the touch to portrait mode if the device is in landscape mode.
- final Point scaledTouch = mUdfpsUtils.getTouchInNativeCoordinates(
- idx, event, mOverlayParams);
- if (actionMoveWithinSensorArea) {
- if (mVelocityTracker == null) {
- // touches could be injected, so the velocity tracker may not have
- // been initialized (via ACTION_DOWN).
- mVelocityTracker = VelocityTracker.obtain();
- }
- mVelocityTracker.addMovement(event);
- // Compute pointer velocity in pixels per second.
- mVelocityTracker.computeCurrentVelocity(1000);
- // Compute pointer speed from X and Y velocities.
- final float v = computePointerSpeed(mVelocityTracker, mActivePointerId);
- final float minor = event.getTouchMinor(idx);
- final float major = event.getTouchMajor(idx);
- final boolean exceedsVelocityThreshold = exceedsVelocityThreshold(v);
- final String touchInfo = String.format(
- "minor: %.1f, major: %.1f, v: %.1f, exceedsVelocityThreshold: %b",
- minor, major, v, exceedsVelocityThreshold);
- final long sinceLastLog = mSystemClock.elapsedRealtime() - mTouchLogTime;
-
- if (!mOnFingerDown && !mAcquiredReceived && !exceedsVelocityThreshold) {
- final float scale = mOverlayParams.getScaleFactor();
- float scaledMinor = minor / scale;
- float scaledMajor = major / scale;
- onFingerDown(requestId, scaledTouch.x, scaledTouch.y, scaledMinor,
- scaledMajor);
-
- Log.v(TAG, "onTouch | finger down: " + touchInfo);
- mTouchLogTime = mSystemClock.elapsedRealtime();
- handled = true;
- } else if (sinceLastLog >= MIN_TOUCH_LOG_INTERVAL) {
- Log.v(TAG, "onTouch | finger move: " + touchInfo);
- mTouchLogTime = mSystemClock.elapsedRealtime();
- }
- } else {
- Log.v(TAG, "onTouch | finger outside");
- onFingerUp(requestId, udfpsView);
- // Maybe announce for accessibility.
- mFgExecutor.execute(() -> {
- if (mOverlay == null) {
- Log.e(TAG, "touch outside sensor area received"
- + "but serverRequest is null");
- return;
- }
- mOverlay.onTouchOutsideOfSensorArea(scaledTouch);
- });
- }
- }
- Trace.endSection();
- break;
-
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_HOVER_EXIT:
- Trace.beginSection("UdfpsController.onTouch.ACTION_UP");
- mActivePointerId = -1;
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
- Log.v(TAG, "onTouch | finger up");
- mAttemptedToDismissKeyguard = false;
- onFingerUp(requestId, udfpsView);
- mFalsingManager.isFalseTouch(UDFPS_AUTHENTICATION);
- Trace.endSection();
- break;
-
- default:
- // Do nothing.
- }
- return handled;
+ return mActivePointerId != MotionEvent.INVALID_POINTER_ID;
}
private boolean shouldTryToDismissKeyguard() {
@@ -859,15 +636,12 @@
@NonNull SystemUIDialogManager dialogManager,
@NonNull LatencyTracker latencyTracker,
@NonNull ActivityLaunchAnimator activityLaunchAnimator,
- @NonNull Optional<Provider<AlternateUdfpsTouchProvider>> alternateTouchProvider,
@NonNull @BiometricsBackground Executor biometricsExecutor,
@NonNull PrimaryBouncerInteractor primaryBouncerInteractor,
@NonNull SinglePointerTouchProcessor singlePointerTouchProcessor,
@NonNull SessionTracker sessionTracker,
@NonNull AlternateBouncerInteractor alternateBouncerInteractor,
- @NonNull SecureSettings secureSettings,
@NonNull InputManager inputManager,
- @NonNull UdfpsUtils udfpsUtils,
@NonNull KeyguardFaceAuthInteractor keyguardFaceAuthInteractor,
@NonNull UdfpsKeyguardAccessibilityDelegate udfpsKeyguardAccessibilityDelegate,
@NonNull Provider<UdfpsKeyguardViewModels> udfpsKeyguardViewModelsProvider) {
@@ -900,7 +674,6 @@
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
mLatencyTracker = latencyTracker;
mActivityLaunchAnimator = activityLaunchAnimator;
- mAlternateTouchProvider = alternateTouchProvider.map(Provider::get).orElse(null);
mSensorProps = new FingerprintSensorPropertiesInternal(
-1 /* sensorId */,
SensorProperties.STRENGTH_CONVENIENCE,
@@ -912,13 +685,10 @@
mBiometricExecutor = biometricsExecutor;
mPrimaryBouncerInteractor = primaryBouncerInteractor;
mAlternateBouncerInteractor = alternateBouncerInteractor;
- mSecureSettings = secureSettings;
- mUdfpsUtils = udfpsUtils;
mInputManager = inputManager;
mUdfpsKeyguardAccessibilityDelegate = udfpsKeyguardAccessibilityDelegate;
- mTouchProcessor = mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)
- ? singlePointerTouchProcessor : null;
+ mTouchProcessor = singlePointerTouchProcessor;
mSessionTracker = sessionTracker;
mDumpManager.registerDumpable(TAG, this);
@@ -1172,16 +942,9 @@
}
private void dispatchOnUiReady(long requestId) {
- if (mAlternateTouchProvider != null) {
- mBiometricExecutor.execute(() -> {
- mAlternateTouchProvider.onUiReady();
- mLatencyTracker.onActionEnd(LatencyTracker.ACTION_UDFPS_ILLUMINATE);
- });
- } else {
- mFingerprintManager.onUdfpsUiEvent(FingerprintManager.UDFPS_UI_READY, requestId,
- mSensorProps.sensorId);
- mLatencyTracker.onActionEnd(LatencyTracker.ACTION_UDFPS_ILLUMINATE);
- }
+ mFingerprintManager.onUdfpsUiEvent(FingerprintManager.UDFPS_UI_READY, requestId,
+ mSensorProps.sensorId);
+ mLatencyTracker.onActionEnd(LatencyTracker.ACTION_UDFPS_ILLUMINATE);
}
private void onFingerDown(
@@ -1241,24 +1004,8 @@
}
}
mOnFingerDown = true;
- if (mAlternateTouchProvider != null) {
- mBiometricExecutor.execute(() -> {
- mAlternateTouchProvider.onPointerDown(requestId, (int) x, (int) y, minor, major);
- });
- mFgExecutor.execute(() -> {
- if (mKeyguardUpdateMonitor.isFingerprintDetectionRunning()) {
- mKeyguardUpdateMonitor.onUdfpsPointerDown((int) requestId);
- }
- });
- } else {
- if (mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) {
- mFingerprintManager.onPointerDown(requestId, mSensorProps.sensorId, pointerId, x, y,
- minor, major, orientation, time, gestureStart, isAod);
- } else {
- mFingerprintManager.onPointerDown(requestId, mSensorProps.sensorId, (int) x,
- (int) y, minor, major);
- }
- }
+ mFingerprintManager.onPointerDown(requestId, mSensorProps.sensorId, pointerId, x, y,
+ minor, major, orientation, time, gestureStart, isAod);
Trace.endAsyncSection("UdfpsController.e2e.onPointerDown", 0);
final UdfpsView view = mOverlay.getOverlayView();
if (view != null && isOptical()) {
@@ -1305,23 +1052,8 @@
mActivePointerId = -1;
mAcquiredReceived = false;
if (mOnFingerDown) {
- if (mAlternateTouchProvider != null) {
- mBiometricExecutor.execute(() -> {
- mAlternateTouchProvider.onPointerUp(requestId);
- });
- mFgExecutor.execute(() -> {
- if (mKeyguardUpdateMonitor.isFingerprintDetectionRunning()) {
- mKeyguardUpdateMonitor.onUdfpsPointerUp((int) requestId);
- }
- });
- } else {
- if (mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) {
- mFingerprintManager.onPointerUp(requestId, mSensorProps.sensorId, pointerId, x,
- y, minor, major, orientation, time, gestureStart, isAod);
- } else {
- mFingerprintManager.onPointerUp(requestId, mSensorProps.sensorId);
- }
- }
+ mFingerprintManager.onPointerUp(requestId, mSensorProps.sensorId, pointerId, x,
+ y, minor, major, orientation, time, gestureStart, isAod);
for (Callback cb : mCallbacks) {
cb.onFingerUp();
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index 34a0d8a..7130bfb 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -20,7 +20,6 @@
import android.annotation.UiThread
import android.content.Context
import android.graphics.PixelFormat
-import android.graphics.Point
import android.graphics.Rect
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_BP
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
@@ -29,7 +28,6 @@
import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROLLING
import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR
import android.hardware.biometrics.BiometricOverlayConstants.ShowReason
-import android.hardware.fingerprint.FingerprintManager
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback
import android.os.Build
import android.os.RemoteException
@@ -54,7 +52,6 @@
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.flags.Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS
import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter
import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels
@@ -65,7 +62,6 @@
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.util.settings.SecureSettings
import kotlinx.coroutines.ExperimentalCoroutinesApi
import javax.inject.Provider
@@ -83,7 +79,6 @@
@UiThread
class UdfpsControllerOverlay @JvmOverloads constructor(
private val context: Context,
- fingerprintManager: FingerprintManager,
private val inflater: LayoutInflater,
private val windowManager: WindowManager,
private val accessibilityManager: AccessibilityManager,
@@ -97,7 +92,6 @@
private val keyguardStateController: KeyguardStateController,
private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController,
private var udfpsDisplayModeProvider: UdfpsDisplayModeProvider,
- private val secureSettings: SecureSettings,
val requestId: Long,
@ShowReason val requestReason: Int,
private val controllerCallback: IUdfpsOverlayControllerCallback,
@@ -107,7 +101,6 @@
private val primaryBouncerInteractor: PrimaryBouncerInteractor,
private val alternateBouncerInteractor: AlternateBouncerInteractor,
private val isDebuggable: Boolean = Build.IS_DEBUGGABLE,
- private val udfpsUtils: UdfpsUtils,
private val udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate,
private val udfpsKeyguardViewModels: Provider<UdfpsKeyguardViewModels>,
) {
@@ -134,10 +127,7 @@
privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
// Avoid announcing window title.
accessibilityTitle = " "
-
- if (featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) {
- inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY
- }
+ inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY
}
/** If the overlay is currently showing. */
@@ -206,7 +196,6 @@
overlayTouchListener!!
)
overlayTouchListener?.onTouchExplorationStateChanged(true)
- useExpandedOverlay = featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)
}
} catch (e: RuntimeException) {
Log.e(TAG, "showUdfpsOverlay | failed to add window", e)
@@ -331,25 +320,6 @@
return wasShowing
}
- /**
- * This function computes the angle of touch relative to the sensor and maps
- * the angle to a list of help messages which are announced if accessibility is enabled.
- *
- */
- fun onTouchOutsideOfSensorArea(scaledTouch: Point) {
- val theStr =
- udfpsUtils.onTouchOutsideOfSensorArea(
- touchExplorationEnabled,
- context,
- scaledTouch.x,
- scaledTouch.y,
- overlayParams
- )
- if (theStr != null) {
- animationViewController?.doAnnounceForAccessibility(theStr)
- }
- }
-
/** Cancel this request. */
fun cancel() {
try {
@@ -367,10 +337,6 @@
): WindowManager.LayoutParams {
val paddingX = animation?.paddingX ?: 0
val paddingY = animation?.paddingY ?: 0
- if (!featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION) && animation != null &&
- animation.listenForTouchesOutsideView()) {
- flags = flags or WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
- }
val isEnrollment = when (requestReason) {
REASON_ENROLL_FIND_SENSOR, REASON_ENROLL_ENROLLING -> true
@@ -379,19 +345,15 @@
// Use expanded overlay unless touchExploration enabled
var rotatedBounds =
- if (featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) {
- if (accessibilityManager.isTouchExplorationEnabled && isEnrollment) {
- Rect(overlayParams.sensorBounds)
- } else {
- Rect(
- 0,
- 0,
- overlayParams.naturalDisplayWidth,
- overlayParams.naturalDisplayHeight
- )
- }
- } else {
+ if (accessibilityManager.isTouchExplorationEnabled && isEnrollment) {
Rect(overlayParams.sensorBounds)
+ } else {
+ Rect(
+ 0,
+ 0,
+ overlayParams.naturalDisplayWidth,
+ overlayParams.naturalDisplayHeight
+ )
}
val rot = overlayParams.rotation
@@ -399,9 +361,9 @@
if (!shouldRotate(animation)) {
Log.v(
TAG, "Skip rotating UDFPS bounds " + Surface.rotationToString(rot) +
- " animation=$animation" +
- " isGoingToSleep=${keyguardUpdateMonitor.isGoingToSleep}" +
- " isOccluded=${keyguardStateController.isOccluded}"
+ " animation=$animation" +
+ " isGoingToSleep=${keyguardUpdateMonitor.isGoingToSleep}" +
+ " isOccluded=${keyguardStateController.isOccluded}"
)
} else {
Log.v(TAG, "Rotate UDFPS bounds " + Surface.rotationToString(rot))
@@ -412,14 +374,12 @@
rot
)
- if (featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) {
- RotationUtils.rotateBounds(
- sensorBounds,
- overlayParams.naturalDisplayWidth,
- overlayParams.naturalDisplayHeight,
- rot
- )
- }
+ RotationUtils.rotateBounds(
+ sensorBounds,
+ overlayParams.naturalDisplayWidth,
+ overlayParams.naturalDisplayHeight,
+ rot
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.kt
index 8cc15da..afe37d4 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.kt
@@ -43,10 +43,6 @@
return fingerprintDrawablePlaceHolder
}
- fun useExpandedOverlay(useExpandedOverlay: Boolean) {
- mUseExpandedOverlay = useExpandedOverlay
- }
-
fun isVisible(): Boolean {
return visible
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
index 8ce98a9..3d5be6f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
@@ -19,7 +19,6 @@
import android.animation.ValueAnimator
import android.content.res.Configuration
import android.util.MathUtils
-import android.view.MotionEvent
import android.view.View
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.Lifecycle
@@ -27,16 +26,15 @@
import com.android.app.animation.Interpolators
import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress
import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.res.R
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.res.R
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.notification.stack.StackStateAnimator
@@ -80,8 +78,6 @@
),
UdfpsKeyguardViewControllerAdapter {
private val uniqueIdentifier = this.toString()
- private val useExpandedOverlay: Boolean =
- featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)
private var showingUdfpsBouncer = false
private var udfpsRequested = false
private var qsExpansion = 0f
@@ -192,24 +188,6 @@
updateAlpha()
updatePauseAuth()
}
-
- /**
- * Forward touches to the UdfpsController. This allows the touch to start from outside
- * the sensor area and then slide their finger into the sensor area.
- */
- override fun onTouch(event: MotionEvent) {
- // Don't forward touches if the shade has already started expanding.
- if (transitionToFullShadeProgress != 0f) {
- return
- }
-
- // Forwarding touches not needed with expanded overlay
- if (useExpandedOverlay) {
- return
- } else {
- udfpsController.onTouch(event)
- }
- }
}
private val occludingAppBiometricUI: OccludingAppBiometricUI =
@@ -294,7 +272,6 @@
keyguardViewManager.setOccludingAppBiometricUI(occludingAppBiometricUI)
lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy = this
activityLaunchAnimator.addListener(activityLaunchAnimatorListener)
- view.mUseExpandedOverlay = useExpandedOverlay
view.startIconAsyncInflate {
val animationViewInternal: View =
view.requireViewById(R.id.udfps_animation_view_internal)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java
index 36a42f9..95e3a76 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java
@@ -298,45 +298,34 @@
pw.println(" mUdfpsRequested=" + mUdfpsRequested);
pw.println(" mInterpolatedDarkAmount=" + mInterpolatedDarkAmount);
pw.println(" mAnimationType=" + mAnimationType);
- pw.println(" mUseExpandedOverlay=" + mUseExpandedOverlay);
}
private final AsyncLayoutInflater.OnInflateFinishedListener mLayoutInflaterFinishListener =
new AsyncLayoutInflater.OnInflateFinishedListener() {
- @Override
- public void onInflateFinished(View view, int resid, ViewGroup parent) {
- mFullyInflated = true;
- mAodFp = view.findViewById(R.id.udfps_aod_fp);
- mLockScreenFp = view.findViewById(R.id.udfps_lockscreen_fp);
- mBgProtection = view.findViewById(R.id.udfps_keyguard_fp_bg);
+ @Override
+ public void onInflateFinished(View view, int resid, ViewGroup parent) {
+ mFullyInflated = true;
+ mAodFp = view.findViewById(R.id.udfps_aod_fp);
+ mLockScreenFp = view.findViewById(R.id.udfps_lockscreen_fp);
+ mBgProtection = view.findViewById(R.id.udfps_keyguard_fp_bg);
- updatePadding();
- updateColor();
- updateAlpha();
+ updatePadding();
+ updateColor();
+ updateAlpha();
- if (mUseExpandedOverlay) {
- final LayoutParams lp = (LayoutParams) view.getLayoutParams();
- lp.width = mSensorBounds.width();
- lp.height = mSensorBounds.height();
- RectF relativeToView = getBoundsRelativeToView(new RectF(mSensorBounds));
- lp.setMarginsRelative(
- (int) relativeToView.left,
- (int) relativeToView.top,
- (int) relativeToView.right,
- (int) relativeToView.bottom
- );
- parent.addView(view, lp);
- } else {
- parent.addView(view);
- }
+ final LayoutParams lp = (LayoutParams) view.getLayoutParams();
+ lp.width = mSensorBounds.width();
+ lp.height = mSensorBounds.height();
+ RectF relativeToView = getBoundsRelativeToView(new RectF(mSensorBounds));
+ lp.setMarginsRelative((int) relativeToView.left, (int) relativeToView.top,
+ (int) relativeToView.right, (int) relativeToView.bottom);
+ parent.addView(view, lp);
- // requires call to invalidate to update the color
- mLockScreenFp.addValueCallback(
- new KeyPath("**"), LottieProperty.COLOR_FILTER,
- frameInfo -> new PorterDuffColorFilter(mTextColorPrimary,
- PorterDuff.Mode.SRC_ATOP)
- );
- mOnFinishInflateRunnable.run();
- }
- };
+ // requires call to invalidate to update the color
+ mLockScreenFp.addValueCallback(new KeyPath("**"), LottieProperty.COLOR_FILTER,
+ frameInfo -> new PorterDuffColorFilter(mTextColorPrimary,
+ PorterDuff.Mode.SRC_ATOP));
+ mOnFinishInflateRunnable.run();
+ }
+ };
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt
index 6ce6172..76bcd6e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt
@@ -19,14 +19,12 @@
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
-import android.graphics.PointF
import android.graphics.Rect
import android.graphics.RectF
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.widget.FrameLayout
-import com.android.systemui.res.R
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.doze.DozeReceiver
@@ -39,10 +37,6 @@
context: Context,
attrs: AttributeSet?
) : FrameLayout(context, attrs), DozeReceiver {
-
- // Use expanded overlay when feature flag is true, set by UdfpsViewController
- var useExpandedOverlay: Boolean = false
-
// sensorRect may be bigger than the sensor. True sensor dimensions are defined in
// overlayParams.sensorBounds
var sensorRect = Rect()
@@ -53,14 +47,6 @@
textSize = 32f
}
- private val sensorTouchAreaCoefficient: Float =
- context.theme.obtainStyledAttributes(attrs, R.styleable.UdfpsView, 0, 0).use { a ->
- require(a.hasValue(R.styleable.UdfpsView_sensorTouchAreaCoefficient)) {
- "UdfpsView must contain sensorTouchAreaCoefficient"
- }
- a.getFloat(R.styleable.UdfpsView_sensorTouchAreaCoefficient, 0f)
- }
-
/** View controller (can be different for enrollment, BiometricPrompt, Keyguard, etc.). */
var animationViewController: UdfpsAnimationViewController<*>? = null
@@ -94,22 +80,8 @@
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
- val paddingX = animationViewController?.paddingX ?: 0
- val paddingY = animationViewController?.paddingY ?: 0
-
// Updates sensor rect in relation to the overlay view
- if (useExpandedOverlay) {
- animationViewController?.onSensorRectUpdated(RectF(sensorRect))
- } else {
- sensorRect.set(
- paddingX,
- paddingY,
- (overlayParams.sensorBounds.width() + paddingX),
- (overlayParams.sensorBounds.height() + paddingY)
- )
-
- animationViewController?.onSensorRectUpdated(RectF(sensorRect))
- }
+ animationViewController?.onSensorRectUpdated(RectF(sensorRect))
}
override fun onAttachedToWindow() {
@@ -131,22 +103,6 @@
}
}
- fun isWithinSensorArea(x: Float, y: Float): Boolean {
- // The X and Y coordinates of the sensor's center.
- val translation = animationViewController?.touchTranslation ?: PointF(0f, 0f)
- val cx = sensorRect.centerX() + translation.x
- val cy = sensorRect.centerY() + translation.y
- // Radii along the X and Y axes.
- val rx = (sensorRect.right - sensorRect.left) / 2.0f
- val ry = (sensorRect.bottom - sensorRect.top) / 2.0f
-
- return x > cx - rx * sensorTouchAreaCoefficient &&
- x < cx + rx * sensorTouchAreaCoefficient &&
- y > cy - ry * sensorTouchAreaCoefficient &&
- y < cy + ry * sensorTouchAreaCoefficient &&
- !(animationViewController?.shouldPauseAuth() ?: false)
- }
-
fun configureDisplay(onDisplayConfigured: Runnable) {
isDisplayConfigured = true
animationViewController?.onDisplayConfiguring()
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalTutorialRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalTutorialRepository.kt
new file mode 100644
index 0000000..9a9b0e2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalTutorialRepository.kt
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.communal.data.repository
+
+import android.provider.Settings
+import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_NOT_STARTED
+import android.provider.Settings.Secure.HubModeTutorialState
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.dagger.CommunalLog
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.user.data.repository.UserRepository
+import com.android.systemui.util.settings.SecureSettings
+import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.withContext
+
+/**
+ * Repository for the current state of hub mode tutorial. Valid states are defined in
+ * [HubModeTutorialState].
+ */
+interface CommunalTutorialRepository {
+ /** Emits the tutorial state stored in Settings */
+ val tutorialSettingState: StateFlow<Int>
+
+ /** Update the tutorial state */
+ suspend fun setTutorialState(@HubModeTutorialState state: Int)
+}
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class CommunalTutorialRepositoryImpl
+@Inject
+constructor(
+ @Application private val applicationScope: CoroutineScope,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+ userRepository: UserRepository,
+ private val secureSettings: SecureSettings,
+ private val userTracker: UserTracker,
+ @CommunalLog logBuffer: LogBuffer,
+) : CommunalTutorialRepository {
+
+ companion object {
+ private const val TAG = "CommunalTutorialRepository"
+ }
+
+ private data class SettingsState(
+ @HubModeTutorialState val hubModeTutorialState: Int? = null,
+ )
+
+ private val logger = Logger(logBuffer, TAG)
+
+ private val settingsState: Flow<SettingsState> =
+ userRepository.selectedUserInfo
+ .flatMapLatest { observeSettings() }
+ .shareIn(scope = applicationScope, started = SharingStarted.WhileSubscribed())
+
+ /** Emits the state of tutorial state in settings */
+ override val tutorialSettingState: StateFlow<Int> =
+ settingsState
+ .map { it.hubModeTutorialState }
+ .filterNotNull()
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = HUB_MODE_TUTORIAL_NOT_STARTED
+ )
+
+ private fun observeSettings(): Flow<SettingsState> =
+ secureSettings
+ .observerFlow(
+ userId = userTracker.userId,
+ names =
+ arrayOf(
+ Settings.Secure.HUB_MODE_TUTORIAL_STATE,
+ )
+ )
+ // Force an update
+ .onStart { emit(Unit) }
+ .map { readFromSettings() }
+
+ private suspend fun readFromSettings(): SettingsState =
+ withContext(backgroundDispatcher) {
+ val userId = userTracker.userId
+ val hubModeTutorialState =
+ secureSettings.getIntForUser(
+ Settings.Secure.HUB_MODE_TUTORIAL_STATE,
+ HUB_MODE_TUTORIAL_NOT_STARTED,
+ userId,
+ )
+ val settingsState = SettingsState(hubModeTutorialState)
+ logger.d({ "Communal tutorial state for user $int1 in settings: $str1" }) {
+ int1 = userId
+ str1 = settingsState.hubModeTutorialState.toString()
+ }
+
+ settingsState
+ }
+
+ override suspend fun setTutorialState(state: Int): Unit =
+ withContext(backgroundDispatcher) {
+ val userId = userTracker.userId
+ if (tutorialSettingState.value == state) {
+ return@withContext
+ }
+ logger.d({ "Update communal tutorial state to $int1 for user $int2" }) {
+ int1 = state
+ int2 = userId
+ }
+ secureSettings.putIntForUser(
+ Settings.Secure.HUB_MODE_TUTORIAL_STATE,
+ state,
+ userId,
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalTutorialRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalTutorialRepositoryModule.kt
new file mode 100644
index 0000000..69b0a27
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalTutorialRepositoryModule.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.communal.data.repository
+
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface CommunalTutorialRepositoryModule {
+ @Binds
+ fun communalTutorialRepository(impl: CommunalTutorialRepositoryImpl): CommunalTutorialRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
new file mode 100644
index 0000000..276df4e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.domain.interactor
+
+import android.provider.Settings
+import com.android.systemui.communal.data.repository.CommunalTutorialRepository
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+
+/** Encapsulates business-logic related to communal tutorial state. */
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class CommunalTutorialInteractor
+@Inject
+constructor(
+ communalTutorialRepository: CommunalTutorialRepository,
+ keyguardInteractor: KeyguardInteractor,
+) {
+ /** An observable for whether the tutorial is available. */
+ val isTutorialAvailable: Flow<Boolean> =
+ combine(
+ keyguardInteractor.isKeyguardVisible,
+ communalTutorialRepository.tutorialSettingState,
+ ) { isKeyguardVisible, tutorialSettingState ->
+ isKeyguardVisible &&
+ tutorialSettingState != Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED
+ }
+ .distinctUntilChanged()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalTutorialIndicatorViewBinder.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalTutorialIndicatorViewBinder.kt
new file mode 100644
index 0000000..dab6819
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalTutorialIndicatorViewBinder.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.communal.ui.binder
+
+import android.widget.TextView
+import androidx.core.view.isGone
+import androidx.core.view.isVisible
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.communal.ui.viewmodel.CommunalTutorialIndicatorViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.launch
+
+/** View binder for communal tutorial indicator shown on keyguard. */
+object CommunalTutorialIndicatorViewBinder {
+ fun bind(
+ view: TextView,
+ viewModel: CommunalTutorialIndicatorViewModel,
+ ): DisposableHandle {
+ val disposableHandle =
+ view.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ launch {
+ viewModel.showIndicator.collect { isVisible ->
+ updateView(
+ view = view,
+ isIndicatorVisible = isVisible,
+ )
+ }
+ }
+ }
+ }
+
+ return disposableHandle
+ }
+
+ private fun updateView(
+ isIndicatorVisible: Boolean,
+ view: TextView,
+ ) {
+ if (!isIndicatorVisible) {
+ view.isGone = true
+ return
+ }
+
+ if (!view.isVisible) {
+ view.isVisible = true
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt
index 3ff1f09..d8d1dc0 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt
@@ -16,6 +16,7 @@
package com.android.systemui.communal.ui.view.layout.blueprints
+import com.android.systemui.communal.ui.view.layout.sections.DefaultCommunalHubSection
import com.android.systemui.communal.ui.view.layout.sections.DefaultCommunalWidgetSection
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
@@ -28,10 +29,15 @@
class DefaultCommunalBlueprint
@Inject
constructor(
+ defaultCommunalHubSection: DefaultCommunalHubSection,
defaultCommunalWidgetSection: DefaultCommunalWidgetSection,
) : KeyguardBlueprint {
override val id: String = COMMUNAL
- override val sections: Set<KeyguardSection> = setOf(defaultCommunalWidgetSection)
+ override val sections: Set<KeyguardSection> =
+ setOf(
+ defaultCommunalHubSection,
+ defaultCommunalWidgetSection,
+ )
companion object {
const val COMMUNAL = "communal"
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalTutorialIndicatorSection.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalTutorialIndicatorSection.kt
new file mode 100644
index 0000000..027cc96
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalTutorialIndicatorSection.kt
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.communal.ui.view.layout.sections
+
+import android.content.res.Resources
+import android.graphics.Typeface
+import android.graphics.Typeface.NORMAL
+import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
+import android.widget.TextView
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.core.content.res.ResourcesCompat
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.ui.binder.CommunalTutorialIndicatorViewBinder
+import com.android.systemui.communal.ui.viewmodel.CommunalTutorialIndicatorViewModel
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.keyguard.ui.view.layout.sections.removeView
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlinx.coroutines.DisposableHandle
+
+class CommunalTutorialIndicatorSection
+@Inject
+constructor(
+ @Main private val resources: Resources,
+ private val communalTutorialIndicatorViewModel: CommunalTutorialIndicatorViewModel,
+ private val communalInteractor: CommunalInteractor,
+) : KeyguardSection() {
+ private var communalTutorialIndicatorHandle: DisposableHandle? = null
+
+ override fun addViews(constraintLayout: ConstraintLayout) {
+ if (!communalInteractor.isCommunalEnabled) {
+ return
+ }
+ val padding =
+ constraintLayout.resources.getDimensionPixelSize(
+ R.dimen.communal_tutorial_indicator_padding
+ )
+ val view =
+ TextView(constraintLayout.context).apply {
+ id = R.id.communal_tutorial_indicator
+ visibility = View.GONE
+ background =
+ ResourcesCompat.getDrawable(
+ context.resources,
+ R.drawable.keyguard_bottom_affordance_bg,
+ context.theme
+ )
+ foreground =
+ ResourcesCompat.getDrawable(
+ context.resources,
+ R.drawable.keyguard_bottom_affordance_selected_border,
+ context.theme
+ )
+ gravity = Gravity.CENTER_VERTICAL
+ typeface = Typeface.create("google-sans", NORMAL)
+ text = constraintLayout.context.getString(R.string.communal_tutorial_indicator_text)
+ setPadding(padding, padding, padding, padding)
+ }
+ constraintLayout.addView(view)
+ }
+
+ override fun bindData(constraintLayout: ConstraintLayout) {
+ if (!communalInteractor.isCommunalEnabled) {
+ return
+ }
+ communalTutorialIndicatorHandle =
+ CommunalTutorialIndicatorViewBinder.bind(
+ constraintLayout.requireViewById(R.id.communal_tutorial_indicator),
+ communalTutorialIndicatorViewModel,
+ )
+ }
+
+ override fun applyConstraints(constraintSet: ConstraintSet) {
+ if (!communalInteractor.isCommunalEnabled) {
+ return
+ }
+ val tutorialIndicatorId = R.id.communal_tutorial_indicator
+ val width = resources.getDimensionPixelSize(R.dimen.communal_tutorial_indicator_fixed_width)
+ val horizontalOffsetMargin =
+ resources.getDimensionPixelSize(R.dimen.communal_tutorial_indicator_horizontal_offset)
+
+ constraintSet.apply {
+ constrainWidth(tutorialIndicatorId, width)
+ constrainHeight(tutorialIndicatorId, WRAP_CONTENT)
+ connect(
+ tutorialIndicatorId,
+ ConstraintSet.RIGHT,
+ ConstraintSet.PARENT_ID,
+ ConstraintSet.RIGHT,
+ horizontalOffsetMargin
+ )
+ connect(
+ tutorialIndicatorId,
+ ConstraintSet.TOP,
+ ConstraintSet.PARENT_ID,
+ ConstraintSet.TOP
+ )
+ connect(
+ tutorialIndicatorId,
+ ConstraintSet.BOTTOM,
+ ConstraintSet.PARENT_ID,
+ ConstraintSet.BOTTOM
+ )
+ }
+ }
+
+ override fun removeViews(constraintLayout: ConstraintLayout) {
+ communalTutorialIndicatorHandle?.dispose()
+ constraintLayout.removeView(R.id.communal_tutorial_indicator)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalHubSection.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalHubSection.kt
new file mode 100644
index 0000000..932dbfb0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalHubSection.kt
@@ -0,0 +1,57 @@
+package com.android.systemui.communal.ui.view.layout.sections
+
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
+import com.android.systemui.compose.ComposeFacade
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.keyguard.ui.view.layout.sections.removeView
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+/** A keyguard section that hosts the communal hub. */
+class DefaultCommunalHubSection @Inject constructor() : KeyguardSection() {
+ private val communalHubViewId = R.id.communal_hub
+
+ override fun addViews(constraintLayout: ConstraintLayout) {
+ constraintLayout.addView(
+ ComposeFacade.createCommunalView(constraintLayout.context).apply {
+ id = communalHubViewId
+ },
+ )
+ }
+
+ override fun bindData(constraintLayout: ConstraintLayout) {}
+
+ override fun applyConstraints(constraintSet: ConstraintSet) {
+ constraintSet.apply {
+ connect(
+ communalHubViewId,
+ ConstraintSet.START,
+ ConstraintSet.PARENT_ID,
+ ConstraintSet.START,
+ )
+ connect(
+ communalHubViewId,
+ ConstraintSet.TOP,
+ ConstraintSet.PARENT_ID,
+ ConstraintSet.TOP,
+ )
+ connect(
+ communalHubViewId,
+ ConstraintSet.END,
+ ConstraintSet.PARENT_ID,
+ ConstraintSet.END,
+ )
+ connect(
+ communalHubViewId,
+ ConstraintSet.BOTTOM,
+ ConstraintSet.PARENT_ID,
+ ConstraintSet.BOTTOM,
+ )
+ }
+ }
+
+ override fun removeViews(constraintLayout: ConstraintLayout) {
+ constraintLayout.removeView(communalHubViewId)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTutorialIndicatorViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTutorialIndicatorViewModel.kt
new file mode 100644
index 0000000..eaf9550
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTutorialIndicatorViewModel.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.communal.ui.viewmodel
+
+import com.android.systemui.communal.domain.interactor.CommunalTutorialInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+/** View model for communal tutorial indicator on keyguard */
+class CommunalTutorialIndicatorViewModel
+@Inject
+constructor(
+ communalTutorialInteractor: CommunalTutorialInteractor,
+) {
+ /** An observable for whether the tutorial indicator view should be visible. */
+ val showIndicator: Flow<Boolean> = communalTutorialInteractor.isTutorialAvailable
+}
diff --git a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt b/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt
index 1a6f7e1..5c1539a 100644
--- a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt
+++ b/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt
@@ -72,4 +72,9 @@
windowInsets: StateFlow<WindowInsets?>,
sceneByKey: Map<SceneKey, Scene>,
): View
+
+ /** Create a [View] that represents the communal hub. */
+ fun createCommunalView(
+ context: Context,
+ ): View
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImpl.kt
index 8e3b510..436b8cb 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImpl.kt
@@ -22,7 +22,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.qs.SettingObserver
+import com.android.systemui.qs.UserSettingObserver
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.settings.SecureSettings
import javax.inject.Inject
@@ -64,7 +64,8 @@
.flatMapLatest { userInfo ->
conflatedCallbackFlow {
val observer =
- object : SettingObserver(secureSettings, null, setting, userInfo.id) {
+ object :
+ UserSettingObserver(secureSettings, null, setting, userInfo.id) {
override fun handleValueChanged(
value: Int,
observedChange: Boolean
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
index ca725c0..5c38264 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
@@ -64,6 +64,7 @@
* @deprecated Deprecdated because {@link Display#getMetrics} is deprecated.
*/
@Provides
+ @Deprecated
public DisplayMetrics provideDisplayMetrics(Context context) {
DisplayMetrics displayMetrics = new DisplayMetrics();
context.getDisplay().getMetrics(displayMetrics);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index d5f0e64..7d4e1a1 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -33,7 +33,6 @@
import com.android.systemui.appops.dagger.AppOpsModule;
import com.android.systemui.assist.AssistModule;
import com.android.systemui.authentication.AuthenticationModule;
-import com.android.systemui.biometrics.AlternateUdfpsTouchProvider;
import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
import com.android.systemui.biometrics.FingerprintReEnrollNotification;
import com.android.systemui.biometrics.UdfpsDisplayModeProvider;
@@ -300,9 +299,6 @@
abstract UdfpsDisplayModeProvider optionalUdfpsDisplayModeProvider();
@BindsOptionalOf
- abstract AlternateUdfpsTouchProvider optionalUdfpsTouchProvider();
-
- @BindsOptionalOf
abstract FingerprintInteractiveToAuthProvider optionalFingerprintInteractiveToAuthProvider();
@BindsOptionalOf
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java
index 6bbd40c..6946950 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java
@@ -31,7 +31,6 @@
import android.content.IntentFilter;
import android.content.res.Resources;
import android.os.Bundle;
-import android.os.UserHandle;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -56,15 +55,15 @@
/**
* Concrete implementation of the a Flag manager that returns default values for debug builds
- *
+ * <p>
* Flags can be set (or unset) via the following adb command:
- *
+ * <p>
* adb shell cmd statusbar flag <id> <on|off|toggle|erase>
- *
+ * <p>
* Alternatively, you can change flags via a broadcast intent:
- *
+ * <p>
* adb shell am broadcast -a com.android.systemui.action.SET_FLAG --ei id <id> [--ez value <0|1>]
- *
+ * <p>
* To restore a flag back to its default, leave the `--ez value <0|1>` off of the command.
*/
@SysUISingleton
@@ -319,8 +318,7 @@
Log.w(TAG, "Failed to set flag " + name + " to " + value);
return;
}
- mGlobalSettings.putStringForUser(mFlagManager.nameToSettingsKey(name), data,
- UserHandle.USER_CURRENT);
+ mGlobalSettings.putString(mFlagManager.nameToSettingsKey(name), data);
}
<T> void eraseFlag(Flag<T> flag) {
@@ -354,8 +352,7 @@
/** Works just like {@link #eraseFlag(String)} except that it doesn't restart SystemUI. */
private void eraseInternal(String name) {
// We can't actually "erase" things from settings, but we can set them to empty!
- mGlobalSettings.putStringForUser(mFlagManager.nameToSettingsKey(name), "",
- UserHandle.USER_CURRENT);
+ mGlobalSettings.putString(mFlagManager.nameToSettingsKey(name), "");
Log.i(TAG, "Erase name " + name);
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index f6f24e0..11ac39f 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -473,6 +473,9 @@
// TODO(b/270437894): Tracking Bug
val MEDIA_REMOTE_RESUME = unreleasedFlag("media_remote_resume")
+ // TODO(b/304506662): Tracking Bug
+ val MEDIA_DEVICE_NAME_FIX = unreleasedFlag("media_device_name_fix", teamfood = true)
+
// 1000 - dock
val SIMULATE_DOCK_THROUGH_CHARGING = releasedFlag("simulate_dock_through_charging")
@@ -667,8 +670,6 @@
@JvmField val NOTE_TASKS = releasedFlag("keycode_flag")
// 2200 - biometrics (udfps, sfps, BiometricPrompt, etc.)
- // TODO(b/259264861): Tracking Bug
- @JvmField val UDFPS_NEW_TOUCH_DETECTION = releasedFlag("udfps_new_touch_detection")
// 2300 - stylus
@JvmField val TRACK_STYLUS_EVER_USED = releasedFlag("track_stylus_ever_used")
@@ -786,8 +787,7 @@
// TODO(b/290213663): Tracking Bug
@JvmField
- val ONE_WAY_HAPTICS_API_MIGRATION =
- unreleasedFlag("oneway_haptics_api_migration", teamfood = true)
+ val ONE_WAY_HAPTICS_API_MIGRATION = releasedFlag("oneway_haptics_api_migration")
/** TODO(b/296223317): Enables the new keyguard presentation containing a clock. */
@JvmField
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index ac40230..c6c1f79 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -749,7 +749,7 @@
@VisibleForTesting
boolean shouldDisplayBugReport(@Nullable UserInfo user) {
return user != null && user.isAdmin()
- && mGlobalSettings.getIntForUser(Settings.Secure.BUGREPORT_IN_POWER_MENU, 0,
+ && mSecureSettings.getIntForUser(Settings.Secure.BUGREPORT_IN_POWER_MENU, 0,
user.id) != 0;
}
@@ -1091,7 +1091,7 @@
@Override
public boolean showBeforeProvisioning() {
- return Build.isDebuggable() && mGlobalSettings.getIntForUser(
+ return Build.isDebuggable() && mSecureSettings.getIntForUser(
Settings.Secure.BUGREPORT_IN_POWER_MENU, 0, getCurrentUser().id) != 0
&& getCurrentUser().isAdmin();
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index a1f0e77..b45613e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -269,6 +269,11 @@
}
}
+ @Override
+ public void onTransitionConsumed(IBinder transition, boolean aborted)
+ throws RemoteException {
+ }
+
private static void initAlphaForAnimationTargets(@NonNull SurfaceControl.Transaction t,
@NonNull RemoteAnimationTarget[] targets) {
for (RemoteAnimationTarget target : targets) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 41bde91..081edd1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -39,6 +39,7 @@
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.classifier.FalsingModule;
import com.android.systemui.communal.data.repository.CommunalRepositoryModule;
+import com.android.systemui.communal.data.repository.CommunalTutorialRepositoryModule;
import com.android.systemui.communal.data.repository.CommunalWidgetRepositoryModule;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
@@ -96,6 +97,7 @@
KeyguardUserSwitcherComponent.class},
includes = {
CommunalRepositoryModule.class,
+ CommunalTutorialRepositoryModule.class,
CommunalWidgetRepositoryModule.class,
FalsingModule.class,
KeyguardDataQuickAffordanceModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
index 75aa4b60f..ca882e5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
@@ -29,9 +29,7 @@
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
@SysUISingleton
@@ -64,29 +62,14 @@
private fun listenForDreamingToOccluded() {
scope.launch {
- keyguardInteractor.isDreaming
- // Add a slight delay, as dreaming and occluded events will arrive with a small gap
- // in time. This prevents a transition to OCCLUSION happening prematurely.
- .onEach { delay(50) }
- .sample(
- combine(
- keyguardInteractor.isKeyguardOccluded,
- transitionInteractor.startedKeyguardTransitionStep,
- ::Pair,
- ),
- ::toTriple
- )
- .collect { (isDreaming, isOccluded, lastStartedTransition) ->
+ combine(keyguardInteractor.isKeyguardOccluded, keyguardInteractor.isDreaming, ::Pair)
+ .sample(transitionInteractor.startedKeyguardTransitionStep, ::toTriple)
+ .collect { (isOccluded, isDreaming, lastStartedTransition) ->
if (
isOccluded &&
!isDreaming &&
- (lastStartedTransition.to == KeyguardState.DREAMING ||
- lastStartedTransition.to == KeyguardState.LOCKSCREEN)
+ lastStartedTransition.to == KeyguardState.DREAMING
) {
- // At the moment, checking for LOCKSCREEN state above provides a corrective
- // action. There's no great signal to determine when the dream is ending
- // and a transition to OCCLUDED is beginning directly. For now, the solution
- // is DREAMING->LOCKSCREEN->OCCLUDED
startTransitionTo(KeyguardState.OCCLUDED)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index ffa1a49..660bd84 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -318,16 +318,9 @@
private fun listenForLockscreenToOccluded() {
scope.launch {
keyguardInteractor.isKeyguardOccluded
- .sample(
- combine(
- transitionInteractor.startedKeyguardState,
- keyguardInteractor.isDreaming,
- ::Pair
- ),
- ::toTriple
- )
- .collect { (isOccluded, keyguardState, isDreaming) ->
- if (isOccluded && !isDreaming && keyguardState == KeyguardState.LOCKSCREEN) {
+ .sample(transitionInteractor.startedKeyguardState, ::Pair)
+ .collect { (isOccluded, keyguardState) ->
+ if (isOccluded && keyguardState == KeyguardState.LOCKSCREEN) {
startTransitionTo(KeyguardState.OCCLUDED)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
index 6d08456..122c4c4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
@@ -29,10 +29,8 @@
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
/** Handles key events arriving when the keyguard is showing or device is dozing. */
-@ExperimentalCoroutinesApi
@SysUISingleton
class KeyguardKeyEventInteractor
@Inject
@@ -57,11 +55,7 @@
if (event.handleAction()) {
when (event.keyCode) {
KeyEvent.KEYCODE_MENU -> return dispatchMenuKeyEvent()
- KeyEvent.KEYCODE_SPACE,
- KeyEvent.KEYCODE_ENTER ->
- if (isDeviceAwake()) {
- return collapseShadeLockedOrShowPrimaryBouncer()
- }
+ KeyEvent.KEYCODE_SPACE -> return dispatchSpaceEvent()
}
}
return false
@@ -97,24 +91,16 @@
(statusBarStateController.state != StatusBarState.SHADE) &&
statusBarKeyguardViewManager.shouldDismissOnMenuPressed()
if (shouldUnlockOnMenuPressed) {
- return collapseShadeLockedOrShowPrimaryBouncer()
+ shadeController.animateCollapseShadeForced()
+ return true
}
return false
}
- private fun collapseShadeLockedOrShowPrimaryBouncer(): Boolean {
- when (statusBarStateController.state) {
- StatusBarState.SHADE -> return false
- StatusBarState.SHADE_LOCKED -> {
- shadeController.animateCollapseShadeForced()
- return true
- }
- StatusBarState.KEYGUARD -> {
- if (!statusBarKeyguardViewManager.primaryBouncerIsShowing()) {
- statusBarKeyguardViewManager.showPrimaryBouncer(true)
- return true
- }
- }
+ private fun dispatchSpaceEvent(): Boolean {
+ if (isDeviceAwake() && statusBarStateController.state != StatusBarState.SHADE) {
+ shadeController.animateCollapseShadeForced()
+ return true
}
return false
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardViewBinder.kt
index a0e0da4..475d26f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardViewBinder.kt
@@ -23,7 +23,6 @@
import androidx.asynclayoutinflater.view.AsyncLayoutInflater
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.res.R
import com.android.systemui.biometrics.UdfpsKeyguardView
import com.android.systemui.biometrics.ui.binder.UdfpsKeyguardInternalViewBinder
import com.android.systemui.keyguard.ui.viewmodel.BackgroundViewModel
@@ -32,6 +31,7 @@
import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardInternalViewModel
import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.res.R
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
@@ -52,8 +52,6 @@
fingerprintViewModel: FingerprintViewModel,
backgroundViewModel: BackgroundViewModel,
) {
- view.useExpandedOverlay(viewModel.useExpandedOverlay())
-
val layoutInflaterFinishListener =
AsyncLayoutInflater.OnInflateFinishedListener { inflatedInternalView, _, parent ->
UdfpsKeyguardInternalViewBinder.bind(
@@ -63,25 +61,21 @@
fingerprintViewModel,
backgroundViewModel,
)
- if (viewModel.useExpandedOverlay()) {
- val lp = inflatedInternalView.layoutParams as FrameLayout.LayoutParams
- lp.width = viewModel.sensorBounds.width()
- lp.height = viewModel.sensorBounds.height()
- val relativeToView =
- getBoundsRelativeToView(
- inflatedInternalView,
- RectF(viewModel.sensorBounds),
- )
- lp.setMarginsRelative(
- relativeToView.left.toInt(),
- relativeToView.top.toInt(),
- relativeToView.right.toInt(),
- relativeToView.bottom.toInt(),
+ val lp = inflatedInternalView.layoutParams as FrameLayout.LayoutParams
+ lp.width = viewModel.sensorBounds.width()
+ lp.height = viewModel.sensorBounds.height()
+ val relativeToView =
+ getBoundsRelativeToView(
+ inflatedInternalView,
+ RectF(viewModel.sensorBounds),
)
- parent!!.addView(inflatedInternalView, lp)
- } else {
- parent!!.addView(inflatedInternalView)
- }
+ lp.setMarginsRelative(
+ relativeToView.left.toInt(),
+ relativeToView.top.toInt(),
+ relativeToView.right.toInt(),
+ relativeToView.bottom.toInt(),
+ )
+ parent!!.addView(inflatedInternalView, lp)
}
val inflater = AsyncLayoutInflater(view.context)
inflater.inflate(R.layout.udfps_keyguard_view_internal, view, layoutInflaterFinishListener)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index c6d8ec7..4a2954d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -29,12 +29,12 @@
import android.os.IBinder
import android.view.Display
import android.view.Display.DEFAULT_DISPLAY
+import android.view.DisplayInfo
import android.view.LayoutInflater
import android.view.SurfaceControlViewHost
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
-import android.view.WindowManager.LayoutParams.TYPE_KEYGUARD
import android.widget.FrameLayout
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.isInvisible
@@ -129,7 +129,7 @@
bundle.getBoolean(ClockPreviewConstants.KEY_HIDE_CLOCK, false)
private val wallpaperColors: WallpaperColors? = bundle.getParcelable(KEY_COLORS)
private val displayId = bundle.getInt(KEY_DISPLAY_ID, DEFAULT_DISPLAY)
- private val display: Display = displayManager.getDisplay(displayId)
+ private val display: Display? = displayManager.getDisplay(displayId)
private var host: SurfaceControlViewHost
@@ -179,7 +179,7 @@
fun render() {
mainHandler.post {
- val previewContext = context.createDisplayContext(display)
+ val previewContext = display?.let { context.createDisplayContext(it) } ?: context
val rootView = FrameLayout(previewContext)
@@ -189,16 +189,18 @@
setUpBottomArea(rootView)
}
- val windowContext = context.createWindowContext(display, TYPE_KEYGUARD, null)
- val windowManagerOfDisplay = windowContext.getSystemService(WindowManager::class.java)
+ var displayInfo: DisplayInfo? = null
+ display?.let {
+ displayInfo = DisplayInfo()
+ it.getDisplayInfo(displayInfo)
+ }
rootView.measure(
View.MeasureSpec.makeMeasureSpec(
- windowManagerOfDisplay?.currentWindowMetrics?.bounds?.width()
- ?: windowManager.currentWindowMetrics.bounds.width(),
+ displayInfo?.logicalWidth ?: windowManager.currentWindowMetrics.bounds.width(),
View.MeasureSpec.EXACTLY
),
View.MeasureSpec.makeMeasureSpec(
- windowManagerOfDisplay?.currentWindowMetrics?.bounds?.height()
+ displayInfo?.logicalHeight
?: windowManager.currentWindowMetrics.bounds.height(),
View.MeasureSpec.EXACTLY
),
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
index e8df1a6..d8e4396 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.ui.view.layout.blueprints
+import com.android.systemui.communal.ui.view.layout.sections.CommunalTutorialIndicatorSection
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.ui.view.layout.sections.AodBurnInSection
@@ -53,6 +54,7 @@
splitShadeGuidelines: SplitShadeGuidelines,
aodNotificationIconsSection: AodNotificationIconsSection,
aodBurnInSection: AodBurnInSection,
+ communalTutorialIndicatorSection: CommunalTutorialIndicatorSection,
) : KeyguardBlueprint {
override val id: String = DEFAULT
@@ -69,6 +71,7 @@
splitShadeGuidelines,
aodNotificationIconsSection,
aodBurnInSection,
+ communalTutorialIndicatorSection,
)
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardViewModel.kt
index 929f27f..dca151d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardViewModel.kt
@@ -17,20 +17,10 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.graphics.Rect
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
@ExperimentalCoroutinesApi
-class UdfpsKeyguardViewModel
-@Inject
-constructor(
- private val featureFlags: FeatureFlags,
-) {
+class UdfpsKeyguardViewModel @Inject constructor() {
var sensorBounds: Rect = Rect()
-
- fun useExpandedOverlay(): Boolean {
- return featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
index 3d4fca1..1b3b473 100644
--- a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
@@ -51,11 +51,12 @@
Uri uri;
boolean looping;
AudioAttributes attributes;
+ float volume;
long requestTime;
public String toString() {
return "{ code=" + code + " looping=" + looping + " attributes=" + attributes
- + " uri=" + uri + " }";
+ + " volume=" + volume + " uri=" + uri + " }";
}
}
@@ -101,6 +102,7 @@
player.setAudioAttributes(mCmd.attributes);
player.setDataSource(mCmd.context, mCmd.uri);
player.setLooping(mCmd.looping);
+ player.setVolume(mCmd.volume);
player.setOnCompletionListener(NotificationPlayer.this);
player.setOnErrorListener(NotificationPlayer.this);
player.prepare();
@@ -401,10 +403,11 @@
* (see {@link MediaPlayer#setLooping(boolean)})
* @param stream the AudioStream to use.
* (see {@link MediaPlayer#setAudioStreamType(int)})
+ * @param volume the volume for the audio with values in range [0.0, 1.0]
* @deprecated use {@link #play(Context, Uri, boolean, AudioAttributes)} instead.
*/
@Deprecated
- public void play(Context context, Uri uri, boolean looping, int stream) {
+ public void play(Context context, Uri uri, boolean looping, int stream, float volume) {
if (DEBUG) { Log.d(mTag, "play uri=" + uri.toString()); }
PlayerBase.deprecateStreamTypeForPlayback(stream, "NotificationPlayer", "play");
Command cmd = new Command();
@@ -414,6 +417,7 @@
cmd.uri = uri;
cmd.looping = looping;
cmd.attributes = new AudioAttributes.Builder().setInternalLegacyStreamType(stream).build();
+ cmd.volume = volume;
synchronized (mCmdQueue) {
enqueueLocked(cmd);
mState = PLAY;
@@ -432,8 +436,10 @@
* (see {@link MediaPlayer#setLooping(boolean)})
* @param attributes the AudioAttributes to use.
* (see {@link MediaPlayer#setAudioAttributes(AudioAttributes)})
+ * @param volume the volume for the audio with values in range [0.0, 1.0]
*/
- public void play(Context context, Uri uri, boolean looping, AudioAttributes attributes) {
+ public void play(Context context, Uri uri, boolean looping, AudioAttributes attributes,
+ float volume) {
if (DEBUG) { Log.d(mTag, "play uri=" + uri.toString()); }
Command cmd = new Command();
cmd.requestTime = SystemClock.uptimeMillis();
@@ -442,6 +448,7 @@
cmd.uri = uri;
cmd.looping = looping;
cmd.attributes = attributes;
+ cmd.volume = volume;
synchronized (mCmdQueue) {
enqueueLocked(cmd);
mState = PLAY;
diff --git a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
index 80be766..7a488365 100644
--- a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
@@ -285,7 +285,8 @@
}
@Override
- public void playAsync(Uri uri, UserHandle user, boolean looping, AudioAttributes aa) {
+ public void playAsync(Uri uri, UserHandle user, boolean looping, AudioAttributes aa,
+ float volume) {
if (LOGD) Log.d(TAG, "playAsync(uri=" + uri + ", user=" + user + ")");
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("Async playback only available from system UID.");
@@ -293,7 +294,7 @@
if (UserHandle.ALL.equals(user)) {
user = UserHandle.SYSTEM;
}
- mAsyncPlayer.play(getContextForUser(user), uri, looping, aa);
+ mAsyncPlayer.play(getContextForUser(user), uri, looping, aa, volume);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt
index a1291a4..724241d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.os.SystemProperties
import android.util.Log
+import com.android.internal.annotations.KeepForWeakReference
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.dagger.qualifiers.Main
@@ -82,6 +83,8 @@
private var smartspaceMediaData: SmartspaceMediaData = EMPTY_SMARTSPACE_MEDIA_DATA
private var reactivatedKey: String? = null
+ // Ensure the field (and associated reference) isn't removed during optimization.
+ @KeepForWeakReference
private val userTrackerCallback =
object : UserTracker.Callback {
override fun onUserChanged(newUser: Int, userContext: Context) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
index 0f3e0ac..2a32ddf 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
@@ -60,7 +60,6 @@
import com.android.internal.logging.InstanceId
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.Dumpable
-import com.android.systemui.res.R
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
@@ -83,6 +82,7 @@
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.BcSmartspaceDataPlugin
+import com.android.systemui.res.R
import com.android.systemui.statusbar.NotificationMediaManager.isConnectingState
import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState
import com.android.systemui.statusbar.notification.row.HybridGroupManager
@@ -90,6 +90,7 @@
import com.android.systemui.util.Assert
import com.android.systemui.util.Utils
import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.concurrency.ThreadFactory
import com.android.systemui.util.time.SystemClock
import com.android.systemui.util.traceSection
import java.io.IOException
@@ -245,7 +246,7 @@
@Inject
constructor(
context: Context,
- @Background backgroundExecutor: Executor,
+ threadFactory: ThreadFactory,
@Main uiExecutor: Executor,
@Main foregroundExecutor: DelayableExecutor,
mediaControllerFactory: MediaControllerFactory,
@@ -267,7 +268,9 @@
keyguardUpdateMonitor: KeyguardUpdateMonitor,
) : this(
context,
- backgroundExecutor,
+ // Loading bitmap for UMO background can take longer time, so it cannot run on the default
+ // background thread. Use a custom thread for media.
+ threadFactory.buildExecutorOnNewThread(TAG),
uiExecutor,
foregroundExecutor,
mediaControllerFactory,
@@ -1429,8 +1432,6 @@
}
private fun onSessionDestroyed(key: String) {
- if (!mediaFlags.isRetainingPlayersEnabled()) return
-
if (DEBUG) Log.d(TAG, "session destroyed for $key")
val entry = mediaEntries.remove(key) ?: return
// Clear token since the session is no longer valid
@@ -1474,7 +1475,7 @@
if (DEBUG) Log.d(TAG, "Removing still-active player $key")
notifyMediaDataRemoved(key)
logger.logMediaRemoved(removed.appUid, removed.packageName, removed.instanceId)
- } else {
+ } else if (mediaFlags.isRetainingPlayersEnabled() || isAbleToResume(removed)) {
// Convert to resume
if (DEBUG) {
Log.d(
@@ -1484,6 +1485,11 @@
)
}
convertToResumePlayer(key, removed)
+ } else {
+ // Retaining players flag is off and app doesn't support resume: remove player.
+ if (DEBUG) Log.d(TAG, "Removing player $key")
+ notifyMediaDataRemoved(key)
+ logger.logMediaRemoved(removed.appUid, removed.packageName, removed.instanceId)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt
index 1fe93ed..1db31ae 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt
@@ -21,6 +21,7 @@
import android.content.Context
import android.graphics.drawable.Drawable
import android.media.MediaRouter2Manager
+import android.media.RoutingSessionInfo
import android.media.session.MediaController
import android.text.TextUtils
import android.util.Log
@@ -31,17 +32,20 @@
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
+import com.android.settingslib.media.PhoneMediaDevice
import com.android.systemui.Dumpable
-import com.android.systemui.res.R
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
import com.android.systemui.media.controls.models.player.MediaData
import com.android.systemui.media.controls.models.player.MediaDeviceData
import com.android.systemui.media.controls.util.MediaControllerFactory
import com.android.systemui.media.controls.util.MediaDataUtils
import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionManager
import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionManagerFactory
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.ConfigurationController
import java.io.PrintWriter
import java.util.concurrent.Executor
@@ -64,7 +68,8 @@
private val localBluetoothManager: LocalBluetoothManager?,
@Main private val fgExecutor: Executor,
@Background private val bgExecutor: Executor,
- dumpManager: DumpManager
+ dumpManager: DumpManager,
+ private val featureFlags: FeatureFlagsClassic,
) : MediaDataManager.Listener, Dumpable {
private val listeners: MutableSet<Listener> = mutableSetOf()
@@ -215,6 +220,7 @@
println(" volumeControlId=$volumeControlId cached= $playbackVolumeControlId")
println(" routingSession=$routingSession")
println(" selectedRoutes=$selectedRoutes")
+ println(" currentConnectedDevice=${localMediaManager.currentConnectedDevice}")
}
}
@@ -348,16 +354,16 @@
}
val device =
aboutToConnect?.fullMediaDevice ?: localMediaManager.currentConnectedDevice
- val route = controller?.let { mr2manager.getRoutingSessionForMediaController(it) }
+ val routingSession =
+ controller?.let { mr2manager.getRoutingSessionForMediaController(it) }
// If we have a controller but get a null route, then don't trust the device
- val enabled = device != null && (controller == null || route != null)
- val name =
- if (controller == null || route != null) {
- route?.name?.toString() ?: device?.name
- } else {
- null
- }
+ val enabled = device != null && (controller == null || routingSession != null)
+
+ val name = getDeviceName(device, routingSession)
+ if (DEBUG) {
+ Log.d(TAG, "new device name $name")
+ }
current =
MediaDeviceData(
enabled,
@@ -369,6 +375,57 @@
}
}
+ /** Return a display name for the current device / route, or null if not possible */
+ private fun getDeviceName(
+ device: MediaDevice?,
+ routingSession: RoutingSessionInfo?,
+ ): String? {
+ val selectedRoutes = routingSession?.let { mr2manager.getSelectedRoutes(it) }
+
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "device is $device, controller $controller," +
+ " routingSession ${routingSession?.name}" +
+ " or ${selectedRoutes?.firstOrNull()?.name}"
+ )
+ }
+
+ if (!featureFlags.isEnabled(Flags.MEDIA_DEVICE_NAME_FIX)) {
+ if (controller == null || routingSession != null) {
+ return routingSession?.name?.toString() ?: device?.name
+ }
+ return null
+ }
+
+ if (controller == null) {
+ // In resume state, we don't have a controller - just use the device name
+ return device?.name
+ }
+
+ if (routingSession == null) {
+ // This happens when casting from apps that do not support MediaRouter2
+ // The output switcher can't show anything useful here, so set to null
+ return null
+ }
+
+ // If this is a user route (app / cast provided), use the provided name
+ if (!routingSession.isSystemSession) {
+ return routingSession.name?.toString() ?: device?.name
+ }
+
+ selectedRoutes?.firstOrNull()?.let {
+ if (device is PhoneMediaDevice) {
+ // Get the (localized) name for this phone device
+ return PhoneMediaDevice.getSystemRouteNameFromType(context, it)
+ } else {
+ // If it's another type of device (in practice, Bluetooth), use the route name
+ return it.name.toString()
+ }
+ }
+ return null
+ }
+
private fun isLeAudioBroadcastEnabled(): Boolean {
if (localBluetoothManager != null) {
val profileManager = localBluetoothManager.profileManager
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
index e61650f..fced117a 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
@@ -20,10 +20,15 @@
import android.os.UserHandle
import com.android.systemui.mediaprojection.appselector.data.RecentTask
import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider
+import com.android.systemui.mediaprojection.appselector.data.RecentTaskThumbnailLoader
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver
+import com.android.systemui.shared.recents.model.ThumbnailData
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.async
import kotlinx.coroutines.cancel
+import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
@MediaProjectionAppSelectorScope
@@ -36,7 +41,8 @@
@HostUserHandle private val hostUserHandle: UserHandle,
@MediaProjectionAppSelector private val scope: CoroutineScope,
@MediaProjectionAppSelector private val appSelectorComponentName: ComponentName,
- @MediaProjectionAppSelector private val callerPackageName: String?
+ @MediaProjectionAppSelector private val callerPackageName: String?,
+ private val thumbnailLoader: RecentTaskThumbnailLoader,
) {
fun init() {
@@ -46,6 +52,11 @@
val tasks =
recentTasks.filterDevicePolicyRestrictedTasks().filterAppSelector().sortedTasks()
+ // Thumbnails are not fresh for the foreground task(s). They are only refreshed at
+ // launch, going to home, or going to overview.
+ // For this reason, we need to refresh them here.
+ refreshForegroundTaskThumbnails(tasks)
+
view.bind(tasks)
}
}
@@ -54,6 +65,16 @@
scope.cancel()
}
+ private suspend fun refreshForegroundTaskThumbnails(tasks: List<RecentTask>) {
+ coroutineScope {
+ val thumbnails: List<Deferred<ThumbnailData?>> =
+ tasks
+ .filter { it.isForegroundTask }
+ .map { async { thumbnailLoader.captureThumbnail(it.taskId) } }
+ thumbnails.forEach { thumbnail -> thumbnail.await() }
+ }
+ }
+
/** Removes all recent tasks that should be blocked according to the policy */
private fun List<RecentTask>.filterDevicePolicyRestrictedTasks(): List<RecentTask> = filter {
devicePolicyResolver.isScreenCaptureAllowed(
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
index 41e2286..a9e6c53 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
@@ -25,5 +25,6 @@
@UserIdInt val userId: Int,
val topActivityComponent: ComponentName?,
val baseIntentComponent: ComponentName?,
- @ColorInt val colorBackground: Int?
+ @ColorInt val colorBackground: Int?,
+ val isForegroundTask: Boolean,
)
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
index 01398cf..aa4c4e5 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
@@ -48,9 +48,14 @@
override suspend fun loadRecentTasks(): List<RecentTask> =
withContext(coroutineDispatcher) {
- val rawRecentTasks: List<GroupedRecentTaskInfo> = recents?.getTasks() ?: emptyList()
-
- rawRecentTasks
+ val groupedTasks: List<GroupedRecentTaskInfo> = recents?.getTasks() ?: emptyList()
+ // Note: the returned task list is from the most-recent to least-recent order.
+ // The last foreground task is at index 1, because at index 0 will be our app selector.
+ val foregroundGroup = groupedTasks.elementAtOrNull(1)
+ val foregroundTaskId1 = foregroundGroup?.taskInfo1?.taskId
+ val foregroundTaskId2 = foregroundGroup?.taskInfo2?.taskId
+ val foregroundTaskIds = listOfNotNull(foregroundTaskId1, foregroundTaskId2)
+ groupedTasks
.flatMap { listOfNotNull(it.taskInfo1, it.taskInfo2) }
.map {
RecentTask(
@@ -58,7 +63,8 @@
it.userId,
it.topActivity,
it.baseIntent?.component,
- it.taskDescription?.backgroundColor
+ it.taskDescription?.backgroundColor,
+ isForegroundTask = it.taskId in foregroundTaskIds
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskThumbnailLoader.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskThumbnailLoader.kt
index 47faaed..ccf272c 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskThumbnailLoader.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskThumbnailLoader.kt
@@ -25,6 +25,8 @@
interface RecentTaskThumbnailLoader {
suspend fun loadThumbnail(taskId: Int): ThumbnailData?
+
+ suspend fun captureThumbnail(taskId: Int): ThumbnailData?
}
class ActivityTaskManagerThumbnailLoader
@@ -36,8 +38,13 @@
override suspend fun loadThumbnail(taskId: Int): ThumbnailData? =
withContext(coroutineDispatcher) {
- val thumbnailData =
- activityManager.getTaskThumbnail(taskId, /* isLowResolution= */ false)
- if (thumbnailData.thumbnail == null) null else thumbnailData
+ activityManager.getTaskThumbnail(taskId, /* isLowResolution= */ false).takeIf {
+ it.thumbnail != null
+ }
+ }
+
+ override suspend fun captureThumbnail(taskId: Int): ThumbnailData? =
+ withContext(coroutineDispatcher) {
+ activityManager.takeTaskThumbnail(taskId).takeIf { it.thumbnail != null }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/ui/view/PeopleViewBinder.kt b/packages/SystemUI/src/com/android/systemui/people/ui/view/PeopleViewBinder.kt
index c567d56..10a2b3c 100644
--- a/packages/SystemUI/src/com/android/systemui/people/ui/view/PeopleViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/people/ui/view/PeopleViewBinder.kt
@@ -31,10 +31,10 @@
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.res.R
import com.android.systemui.people.PeopleSpaceTileView
import com.android.systemui.people.ui.viewmodel.PeopleTileViewModel
import com.android.systemui.people.ui.viewmodel.PeopleViewModel
+import com.android.systemui.res.R
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
@@ -101,10 +101,10 @@
view,
priorityTiles,
recentTiles,
- viewModel::onTileClicked,
+ viewModel.onTileClicked,
)
} else {
- setNoConversationsContent(view, viewModel::onUserJourneyCancelled)
+ setNoConversationsContent(view, viewModel.onUserJourneyCancelled)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/ui/viewmodel/PeopleViewModel.kt b/packages/SystemUI/src/com/android/systemui/people/ui/viewmodel/PeopleViewModel.kt
index ed7c21b..b847e95 100644
--- a/packages/SystemUI/src/com/android/systemui/people/ui/viewmodel/PeopleViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/people/ui/viewmodel/PeopleViewModel.kt
@@ -23,35 +23,32 @@
import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
-import com.android.systemui.res.R
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.people.PeopleSpaceUtils
import com.android.systemui.people.PeopleTileViewHelper
import com.android.systemui.people.data.model.PeopleTileModel
import com.android.systemui.people.data.repository.PeopleTileRepository
import com.android.systemui.people.data.repository.PeopleWidgetRepository
+import com.android.systemui.res.R
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+private const val TAG = "PeopleViewModel"
+
/**
* Models UI state for the people space, allowing the user to select which conversation should be
* associated to a new or existing Conversation widget.
*/
class PeopleViewModel(
- @Application private val context: Context,
- private val tileRepository: PeopleTileRepository,
- private val widgetRepository: PeopleWidgetRepository,
-) : ViewModel() {
/**
* The list of the priority tiles/conversations.
*
* Important: Even though this is a Flow, the underlying API used to populate this Flow is not
* reactive and you have to manually call [onTileRefreshRequested] to refresh the tiles.
*/
- private val _priorityTiles = MutableStateFlow(priorityTiles())
- val priorityTiles: StateFlow<List<PeopleTileViewModel>> = _priorityTiles.asStateFlow()
+ val priorityTiles: StateFlow<List<PeopleTileViewModel>>,
/**
* The list of the priority tiles/conversations.
@@ -59,84 +56,29 @@
* Important: Even though this is a Flow, the underlying API used to populate this Flow is not
* reactive and you have to manually call [onTileRefreshRequested] to refresh the tiles.
*/
- private val _recentTiles = MutableStateFlow(recentTiles())
- val recentTiles: StateFlow<List<PeopleTileViewModel>> = _recentTiles.asStateFlow()
+ val recentTiles: StateFlow<List<PeopleTileViewModel>>,
/** The ID of the widget currently being edited/added. */
- private val _appWidgetId = MutableStateFlow(INVALID_APPWIDGET_ID)
- val appWidgetId: StateFlow<Int> = _appWidgetId.asStateFlow()
+ val appWidgetId: StateFlow<Int>,
/** The result of this user journey. */
- private val _result = MutableStateFlow<Result?>(null)
- val result: StateFlow<Result?> = _result.asStateFlow()
+ val result: StateFlow<Result?>,
/** Refresh the [priorityTiles] and [recentTiles]. */
- fun onTileRefreshRequested() {
- _priorityTiles.value = priorityTiles()
- _recentTiles.value = recentTiles()
- }
+ val onTileRefreshRequested: () -> Unit,
/** Called when the [appWidgetId] should be changed to [widgetId]. */
- fun onWidgetIdChanged(widgetId: Int) {
- _appWidgetId.value = widgetId
- }
+ val onWidgetIdChanged: (widgetId: Int) -> Unit,
/** Clear [result], setting it to null. */
- fun clearResult() {
- _result.value = null
- }
+ val clearResult: () -> Unit,
/** Called when a tile is clicked. */
- fun onTileClicked(tile: PeopleTileViewModel) {
- val widgetId = _appWidgetId.value
- if (PeopleSpaceUtils.DEBUG) {
- Log.d(
- TAG,
- "Put ${tile.username}'s shortcut ID: ${tile.key.shortcutId} for widget ID $widgetId"
- )
- }
- widgetRepository.setWidgetTile(widgetId, tile.key)
- _result.value =
- Result.Success(Intent().apply { putExtra(EXTRA_APPWIDGET_ID, appWidgetId.value) })
- }
+ val onTileClicked: (tile: PeopleTileViewModel) -> Unit,
/** Called when this user journey is cancelled. */
- fun onUserJourneyCancelled() {
- _result.value = Result.Cancelled
- }
-
- private fun priorityTiles(): List<PeopleTileViewModel> {
- return try {
- tileRepository.priorityTiles().map { it.toViewModel() }
- } catch (e: Exception) {
- Log.e(TAG, "Couldn't retrieve priority conversations", e)
- emptyList()
- }
- }
-
- private fun recentTiles(): List<PeopleTileViewModel> {
- return try {
- tileRepository.recentTiles().map { it.toViewModel() }
- } catch (e: Exception) {
- Log.e(TAG, "Couldn't retrieve recent conversations", e)
- emptyList()
- }
- }
-
- private fun PeopleTileModel.toViewModel(): PeopleTileViewModel {
- val icon =
- PeopleTileViewHelper.getPersonIconBitmap(
- context,
- this,
- PeopleTileViewHelper.getSizeInDp(
- context,
- R.dimen.avatar_size_for_medium,
- context.resources.displayMetrics.density,
- )
- )
- return PeopleTileViewModel(key, icon, username)
- }
-
+ val onUserJourneyCancelled: () -> Unit,
+) : ViewModel() {
/** The Factory that should be used to create a [PeopleViewModel]. */
class Factory
@Inject
@@ -153,10 +95,94 @@
sealed class Result {
class Success(val data: Intent) : Result()
+
object Cancelled : Result()
}
+}
- companion object {
- private const val TAG = "PeopleViewModel"
+private fun PeopleViewModel(
+ @Application context: Context,
+ tileRepository: PeopleTileRepository,
+ widgetRepository: PeopleWidgetRepository,
+): PeopleViewModel {
+ fun priorityTiles(): List<PeopleTileViewModel> {
+ return try {
+ tileRepository.priorityTiles().map { it.toViewModel(context) }
+ } catch (e: Exception) {
+ Log.e(TAG, "Couldn't retrieve priority conversations", e)
+ emptyList()
+ }
}
+
+ fun recentTiles(): List<PeopleTileViewModel> {
+ return try {
+ tileRepository.recentTiles().map { it.toViewModel(context) }
+ } catch (e: Exception) {
+ Log.e(TAG, "Couldn't retrieve recent conversations", e)
+ emptyList()
+ }
+ }
+
+ val priorityTiles = MutableStateFlow(priorityTiles())
+ val recentTiles = MutableStateFlow(recentTiles())
+ val appWidgetId = MutableStateFlow(INVALID_APPWIDGET_ID)
+ val result = MutableStateFlow<PeopleViewModel.Result?>(null)
+
+ fun onTileRefreshRequested() {
+ priorityTiles.value = priorityTiles()
+ recentTiles.value = recentTiles()
+ }
+
+ fun onWidgetIdChanged(widgetId: Int) {
+ appWidgetId.value = widgetId
+ }
+
+ fun clearResult() {
+ result.value = null
+ }
+
+ fun onTileClicked(tile: PeopleTileViewModel) {
+ val widgetId = appWidgetId.value
+ if (PeopleSpaceUtils.DEBUG) {
+ Log.d(
+ TAG,
+ "Put ${tile.username}'s shortcut ID: ${tile.key.shortcutId} for widget ID $widgetId"
+ )
+ }
+ widgetRepository.setWidgetTile(widgetId, tile.key)
+ result.value =
+ PeopleViewModel.Result.Success(
+ Intent().apply { putExtra(EXTRA_APPWIDGET_ID, appWidgetId.value) }
+ )
+ }
+
+ fun onUserJourneyCancelled() {
+ result.value = PeopleViewModel.Result.Cancelled
+ }
+
+ return PeopleViewModel(
+ priorityTiles = priorityTiles.asStateFlow(),
+ recentTiles = recentTiles.asStateFlow(),
+ appWidgetId = appWidgetId.asStateFlow(),
+ result = result.asStateFlow(),
+ onTileRefreshRequested = ::onTileRefreshRequested,
+ onWidgetIdChanged = ::onWidgetIdChanged,
+ clearResult = ::clearResult,
+ onTileClicked = ::onTileClicked,
+ onUserJourneyCancelled = ::onUserJourneyCancelled,
+ )
+}
+
+fun PeopleTileModel.toViewModel(@Application context: Context): PeopleTileViewModel {
+ val icon =
+ PeopleTileViewHelper.getPersonIconBitmap(
+ context,
+ this,
+ PeopleTileViewHelper.getSizeInDp(
+ context,
+ R.dimen.avatar_size_for_medium,
+ context.resources.displayMetrics.density,
+ )
+ )
+ return PeopleTileViewModel(key, icon, username)
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt
index d949a2a..67d390d 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt
@@ -25,6 +25,7 @@
import com.android.systemui.dump.DumpManager
import com.android.systemui.util.DeviceConfigProxy
import com.android.systemui.util.asIndenting
+import com.android.systemui.util.annotations.WeaklyReferencedCallback
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.withIncreasedIndent
import java.io.PrintWriter
@@ -144,6 +145,7 @@
ipw.flush()
}
+ @WeaklyReferencedCallback
interface Callback {
fun onFlagMicCameraChanged(flag: Boolean) {}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SettingObserver.java b/packages/SystemUI/src/com/android/systemui/qs/SettingObserver.java
index 7794fa0..eb11568 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SettingObserver.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/SettingObserver.java
@@ -20,15 +20,14 @@
import android.os.Handler;
import com.android.systemui.statusbar.policy.Listenable;
-import com.android.systemui.util.settings.GlobalSettings;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.settings.SettingsProxy;
import com.android.systemui.util.settings.SystemSettings;
/**
- * Helper for managing secure, global, and system settings through use of {@link SettingsProxy},
- * which is the common superclass of {@link SecureSettings}, {@link GlobalSettings}, and
- * {@link SystemSettings}.
+ * Helper for managing global settings through use of {@link SettingsProxy}. This should
+ * <em>not</em> be used for {@link SecureSettings} or {@link SystemSettings} since those must be
+ * user-aware (instead, use {@link UserSettingObserver}).
*/
public abstract class SettingObserver extends ContentObserver implements Listenable {
private final SettingsProxy mSettingsProxy;
@@ -36,23 +35,20 @@
private final int mDefaultValue;
private boolean mListening;
- private int mUserId;
private int mObservedValue;
protected abstract void handleValueChanged(int value, boolean observedChange);
- public SettingObserver(SettingsProxy settingsProxy, Handler handler, String settingName,
- int userId) {
- this(settingsProxy, handler, settingName, userId, 0);
+ public SettingObserver(SettingsProxy settingsProxy, Handler handler, String settingName) {
+ this(settingsProxy, handler, settingName, 0);
}
public SettingObserver(SettingsProxy settingsProxy, Handler handler, String settingName,
- int userId, int defaultValue) {
+ int defaultValue) {
super(handler);
mSettingsProxy = settingsProxy;
mSettingName = settingName;
mObservedValue = mDefaultValue = defaultValue;
- mUserId = userId;
}
public int getValue() {
@@ -65,11 +61,11 @@
* @param value The new value for the setting.
*/
public void setValue(int value) {
- mSettingsProxy.putIntForUser(mSettingName, value, mUserId);
+ mSettingsProxy.putInt(mSettingName, value);
}
private int getValueFromProvider() {
- return mSettingsProxy.getIntForUser(mSettingName, mDefaultValue, mUserId);
+ return mSettingsProxy.getInt(mSettingName, mDefaultValue);
}
@Override
@@ -78,8 +74,8 @@
mListening = listening;
if (listening) {
mObservedValue = getValueFromProvider();
- mSettingsProxy.registerContentObserverForUser(
- mSettingsProxy.getUriFor(mSettingName), false, this, mUserId);
+ mSettingsProxy.registerContentObserver(
+ mSettingsProxy.getUriFor(mSettingName), false, this);
} else {
mSettingsProxy.unregisterContentObserver(this);
mObservedValue = mDefaultValue;
@@ -94,21 +90,6 @@
handleValueChanged(value, changed);
}
- /**
- * Set user handle for which to observe the setting.
- */
- public void setUserId(int userId) {
- mUserId = userId;
- if (mListening) {
- setListening(false);
- setListening(true);
- }
- }
-
- public int getCurrentUser() {
- return mUserId;
- }
-
public String getKey() {
return mSettingName;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/UserSettingObserver.java b/packages/SystemUI/src/com/android/systemui/qs/UserSettingObserver.java
new file mode 100644
index 0000000..539c2d6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/UserSettingObserver.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs;
+
+import android.database.ContentObserver;
+import android.os.Handler;
+
+import com.android.systemui.statusbar.policy.Listenable;
+import com.android.systemui.util.settings.SecureSettings;
+import com.android.systemui.util.settings.SystemSettings;
+import com.android.systemui.util.settings.UserSettingsProxy;
+
+/**
+ * Helper for managing secure and system settings through use of {@link UserSettingsProxy},
+ * which is the common superclass of {@link SecureSettings} and {@link SystemSettings}.
+ */
+public abstract class UserSettingObserver extends ContentObserver implements Listenable {
+ private final UserSettingsProxy mSettingsProxy;
+ private final String mSettingName;
+ private final int mDefaultValue;
+
+ private boolean mListening;
+ private int mUserId;
+ private int mObservedValue;
+
+ protected abstract void handleValueChanged(int value, boolean observedChange);
+
+ public UserSettingObserver(UserSettingsProxy settingsProxy, Handler handler, String settingName,
+ int userId) {
+ this(settingsProxy, handler, settingName, userId, 0);
+ }
+
+ public UserSettingObserver(UserSettingsProxy settingsProxy, Handler handler, String settingName,
+ int userId, int defaultValue) {
+ super(handler);
+ mSettingsProxy = settingsProxy;
+ mSettingName = settingName;
+ mObservedValue = mDefaultValue = defaultValue;
+ mUserId = userId;
+ }
+
+ public int getValue() {
+ return mListening ? mObservedValue : getValueFromProvider();
+ }
+
+ /**
+ * Set the value of the observed setting.
+ *
+ * @param value The new value for the setting.
+ */
+ public void setValue(int value) {
+ mSettingsProxy.putIntForUser(mSettingName, value, mUserId);
+ }
+
+ private int getValueFromProvider() {
+ return mSettingsProxy.getIntForUser(mSettingName, mDefaultValue, mUserId);
+ }
+
+ @Override
+ public void setListening(boolean listening) {
+ if (listening == mListening) return;
+ mListening = listening;
+ if (listening) {
+ mObservedValue = getValueFromProvider();
+ mSettingsProxy.registerContentObserverForUser(
+ mSettingsProxy.getUriFor(mSettingName), false, this, mUserId);
+ } else {
+ mSettingsProxy.unregisterContentObserver(this);
+ mObservedValue = mDefaultValue;
+ }
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ final int value = getValueFromProvider();
+ final boolean changed = value != mObservedValue;
+ mObservedValue = value;
+ handleValueChanged(value, changed);
+ }
+
+ /**
+ * Set user handle for which to observe the setting.
+ */
+ public void setUserId(int userId) {
+ mUserId = userId;
+ if (mListening) {
+ setListening(false);
+ setListening(true);
+ }
+ }
+
+ public int getCurrentUser() {
+ return mUserId;
+ }
+
+ public String getKey() {
+ return mSettingName;
+ }
+
+ public boolean isListening() {
+ return mListening;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
index 2469a98..78f2da5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -17,6 +17,7 @@
import static android.service.quicksettings.TileService.START_ACTIVITY_NEEDS_PENDING_INTENT;
+import android.app.ActivityManager;
import android.app.compat.CompatChanges;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -35,6 +36,7 @@
import android.service.quicksettings.IQSService;
import android.service.quicksettings.IQSTileService;
import android.service.quicksettings.TileService;
+import android.text.format.DateUtils;
import android.util.ArraySet;
import android.util.Log;
@@ -81,7 +83,8 @@
// Bind retry control.
private static final int MAX_BIND_RETRIES = 5;
- private static final int DEFAULT_BIND_RETRY_DELAY = 1000;
+ private static final long DEFAULT_BIND_RETRY_DELAY = 5 * DateUtils.SECOND_IN_MILLIS;
+ private static final long LOW_MEMORY_BIND_RETRY_DELAY = 20 * DateUtils.SECOND_IN_MILLIS;
// Shared prefs that hold tile lifecycle info.
private static final String TILES = "tiles_prefs";
@@ -94,6 +97,7 @@
private final IBinder mToken = new Binder();
private final PackageManagerAdapter mPackageManagerAdapter;
private final BroadcastDispatcher mBroadcastDispatcher;
+ private final ActivityManager mActivityManager;
private Set<Integer> mQueuedMessages = new ArraySet<>();
@Nullable
@@ -102,7 +106,8 @@
private IBinder mClickBinder;
private int mBindTryCount;
- private int mBindRetryDelay = DEFAULT_BIND_RETRY_DELAY;
+ private long mBindRetryDelay = DEFAULT_BIND_RETRY_DELAY;
+ private AtomicBoolean isDeathRebindScheduled = new AtomicBoolean(false);
private AtomicBoolean mBound = new AtomicBoolean(false);
private AtomicBoolean mPackageReceiverRegistered = new AtomicBoolean(false);
private AtomicBoolean mUserReceiverRegistered = new AtomicBoolean(false);
@@ -115,7 +120,7 @@
@AssistedInject
TileLifecycleManager(@Main Handler handler, Context context, IQSService service,
PackageManagerAdapter packageManagerAdapter, BroadcastDispatcher broadcastDispatcher,
- @Assisted Intent intent, @Assisted UserHandle user,
+ @Assisted Intent intent, @Assisted UserHandle user, ActivityManager activityManager,
@Background DelayableExecutor executor) {
mContext = context;
mHandler = handler;
@@ -126,6 +131,7 @@
mExecutor = executor;
mPackageManagerAdapter = packageManagerAdapter;
mBroadcastDispatcher = broadcastDispatcher;
+ mActivityManager = activityManager;
if (DEBUG) Log.d(TAG, "Creating " + mIntent + " " + mUser);
}
@@ -152,10 +158,6 @@
}
}
- public void setBindRetryDelay(int delayMs) {
- mBindRetryDelay = delayMs;
- }
-
public boolean isActiveTile() {
try {
ServiceInfo info = mPackageManagerAdapter.getServiceInfo(mIntent.getComponent(),
@@ -250,19 +252,15 @@
private boolean bindServices() {
String packageName = mIntent.getComponent().getPackageName();
+ int flags = Context.BIND_AUTO_CREATE
+ | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
+ | Context.BIND_WAIVE_PRIORITY;
if (CompatChanges.isChangeEnabled(START_ACTIVITY_NEEDS_PENDING_INTENT, packageName,
mUser)) {
- return mContext.bindServiceAsUser(mIntent, this,
- Context.BIND_AUTO_CREATE
- | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
- | Context.BIND_WAIVE_PRIORITY,
- mUser);
+ return mContext.bindServiceAsUser(mIntent, this, flags, mUser);
}
return mContext.bindServiceAsUser(mIntent, this,
- Context.BIND_AUTO_CREATE
- | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
- | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS
- | Context.BIND_WAIVE_PRIORITY,
+ flags | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS,
mUser);
}
@@ -352,10 +350,34 @@
if (!mBound.get()) return;
if (DEBUG) Log.d(TAG, "handleDeath");
if (checkComponentState()) {
- mExecutor.executeDelayed(() -> setBindService(true), mBindRetryDelay);
+ if (isDeathRebindScheduled.compareAndSet(false, true)) {
+ mExecutor.executeDelayed(() -> {
+ setBindService(true);
+ isDeathRebindScheduled.set(false);
+ }, getRebindDelay());
+ }
}
}
+ /**
+ * @return the delay to automatically rebind after a service died. It provides a longer delay if
+ * the device is a low memory state because the service is likely to get killed again by the
+ * system. In this case we want to rebind later and not to cause a loop of a frequent rebinds.
+ */
+ private long getRebindDelay() {
+ final ActivityManager.MemoryInfo info = new ActivityManager.MemoryInfo();
+ mActivityManager.getMemoryInfo(info);
+
+ final long delay;
+ if (info.lowMemory) {
+ delay = LOW_MEMORY_BIND_RETRY_DELAY;
+ } else {
+ delay = mBindRetryDelay;
+ }
+ Log.i(TAG, "Rebinding with a delay=" + delay);
+ return delay;
+ }
+
private boolean checkComponentState() {
if (!isPackageAvailable() || !isComponentAvailable()) {
startPackageListening();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
index 941a9d6..3ee4a1b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
@@ -75,13 +75,12 @@
private boolean mStarted = false;
TileServiceManager(TileServices tileServices, Handler handler, ComponentName component,
- BroadcastDispatcher broadcastDispatcher, UserTracker userTracker,
- CustomTileAddedRepository customTileAddedRepository, DelayableExecutor executor) {
+ UserTracker userTracker, TileLifecycleManager.Factory tileLifecycleManagerFactory,
+ CustomTileAddedRepository customTileAddedRepository) {
this(tileServices, handler, userTracker, customTileAddedRepository,
- new TileLifecycleManager(handler, tileServices.getContext(), tileServices,
- new PackageManagerAdapter(tileServices.getContext()), broadcastDispatcher,
+ tileLifecycleManagerFactory.create(
new Intent(TileService.ACTION_QS_TILE).setComponent(component),
- userTracker.getUserHandle(), executor));
+ userTracker.getUserHandle()));
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
index acee8e9..c3744df 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -81,6 +81,7 @@
private final UserTracker mUserTracker;
private final StatusBarIconController mStatusBarIconController;
private final PanelInteractor mPanelInteractor;
+ private final TileLifecycleManager.Factory mTileLifecycleManagerFactory;
private final CustomTileAddedRepository mCustomTileAddedRepository;
private final DelayableExecutor mBackgroundExecutor;
@@ -96,6 +97,7 @@
CommandQueue commandQueue,
StatusBarIconController statusBarIconController,
PanelInteractor panelInteractor,
+ TileLifecycleManager.Factory tileLifecycleManagerFactory,
CustomTileAddedRepository customTileAddedRepository,
@Background DelayableExecutor backgroundExecutor) {
mHost = host;
@@ -109,6 +111,7 @@
mStatusBarIconController = statusBarIconController;
mCommandQueue.addCallback(mRequestListeningCallback);
mPanelInteractor = panelInteractor;
+ mTileLifecycleManagerFactory = tileLifecycleManagerFactory;
mCustomTileAddedRepository = customTileAddedRepository;
mBackgroundExecutor = backgroundExecutor;
}
@@ -137,8 +140,8 @@
protected TileServiceManager onCreateTileService(ComponentName component,
BroadcastDispatcher broadcastDispatcher) {
- return new TileServiceManager(this, mHandlerProvider.get(), component,
- broadcastDispatcher, mUserTracker, mCustomTileAddedRepository, mBackgroundExecutor);
+ return new TileServiceManager(this, mHandlerProvider.get(), component, mUserTracker,
+ mTileLifecycleManagerFactory, mCustomTileAddedRepository);
}
public void freeService(CustomTileInterface tile, TileServiceManager service) {
@@ -323,7 +326,7 @@
if (info.applicationInfo.isSystemApp()) {
final StatusBarIcon statusIcon = icon != null
? new StatusBarIcon(userHandle, packageName, icon, 0, 0,
- contentDescription)
+ contentDescription)
: null;
final String slot = getStatusBarIconSlotName(componentName);
mMainHandler.post(new Runnable() {
@@ -356,11 +359,11 @@
synchronized (mServices) {
mTokenMap.forEach((iBinder, customTile) ->
sb.append(iBinder.toString())
- .append(":")
- .append(customTile.getComponent().flattenToShortString())
- .append(":")
- .append(customTile.getUser())
- .append(","));
+ .append(":")
+ .append(customTile.getComponent().flattenToShortString())
+ .append(":")
+ .append(customTile.getUser())
+ .append(","));
}
sb.append("]");
return sb.toString();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
index 64fa33c..eff3e76 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
@@ -33,6 +33,7 @@
import com.android.systemui.qs.dagger.QSFlagsModule.PM_LITE_ENABLED
import com.android.systemui.qs.footer.data.model.UserSwitcherStatusModel
import com.android.systemui.qs.footer.domain.interactor.FooterActionsInteractor
+import com.android.systemui.qs.footer.domain.model.SecurityButtonConfig
import com.android.systemui.res.R
import com.android.systemui.util.icuMessageFormat
import javax.inject.Inject
@@ -47,17 +48,35 @@
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
+private const val TAG = "FooterActionsViewModel"
+
/** A ViewModel for the footer actions. */
class FooterActionsViewModel(
- @Application appContext: Context,
- private val footerActionsInteractor: FooterActionsInteractor,
- private val falsingManager: FalsingManager,
- private val globalActionsDialogLite: GlobalActionsDialogLite,
- showPowerButton: Boolean,
-) {
- /** The context themed with the Quick Settings colors. */
- private val context = ContextThemeWrapper(appContext, R.style.Theme_SystemUI_QuickSettings)
+ /** The model for the security button. */
+ val security: Flow<FooterActionsSecurityButtonViewModel?>,
+ /** The model for the foreground services button. */
+ val foregroundServices: Flow<FooterActionsForegroundServicesButtonViewModel?>,
+
+ /** The model for the user switcher button. */
+ val userSwitcher: Flow<FooterActionsButtonViewModel?>,
+
+ /** The model for the settings button. */
+ val settings: FooterActionsButtonViewModel,
+
+ /** The model for the power button. */
+ val power: FooterActionsButtonViewModel?,
+
+ /**
+ * Observe the device monitoring dialog requests and show the dialog accordingly. This function
+ * will suspend indefinitely and will need to be cancelled to stop observing.
+ *
+ * Important: [quickSettingsContext] must be the [Context] associated to the
+ * [Quick Settings fragment][com.android.systemui.qs.QSFragmentLegacy], and the call to this
+ * function must be cancelled when that fragment is destroyed.
+ */
+ val observeDeviceMonitoringDialogRequests: suspend (quickSettingsContext: Context) -> Unit,
+) {
/**
* Whether the UI rendering this ViewModel should be visible. Note that even when this is false,
* the UI should still participate to the layout it is included in (i.e. in the View world it
@@ -74,107 +93,6 @@
private val _backgroundAlpha = MutableStateFlow(1f)
val backgroundAlpha: StateFlow<Float> = _backgroundAlpha.asStateFlow()
- /** The model for the security button. */
- val security: Flow<FooterActionsSecurityButtonViewModel?> =
- footerActionsInteractor.securityButtonConfig
- .map { config ->
- val (icon, text, isClickable) = config ?: return@map null
- FooterActionsSecurityButtonViewModel(
- icon,
- text,
- if (isClickable) this::onSecurityButtonClicked else null,
- )
- }
- .distinctUntilChanged()
-
- /** The model for the foreground services button. */
- val foregroundServices: Flow<FooterActionsForegroundServicesButtonViewModel?> =
- combine(
- footerActionsInteractor.foregroundServicesCount,
- footerActionsInteractor.hasNewForegroundServices,
- security,
- ) { foregroundServicesCount, hasNewChanges, securityModel ->
- if (foregroundServicesCount <= 0) {
- return@combine null
- }
-
- val text =
- icuMessageFormat(
- context.resources,
- R.string.fgs_manager_footer_label,
- foregroundServicesCount,
- )
- FooterActionsForegroundServicesButtonViewModel(
- foregroundServicesCount,
- text = text,
- displayText = securityModel == null,
- hasNewChanges = hasNewChanges,
- this::onForegroundServiceButtonClicked,
- )
- }
- .distinctUntilChanged()
-
- /** The model for the user switcher button. */
- val userSwitcher: Flow<FooterActionsButtonViewModel?> =
- footerActionsInteractor.userSwitcherStatus
- .map { userSwitcherStatus ->
- when (userSwitcherStatus) {
- UserSwitcherStatusModel.Disabled -> null
- is UserSwitcherStatusModel.Enabled -> {
- if (userSwitcherStatus.currentUserImage == null) {
- Log.e(
- TAG,
- "Skipped the addition of user switcher button because " +
- "currentUserImage is missing",
- )
- return@map null
- }
-
- userSwitcherButton(userSwitcherStatus)
- }
- }
- }
- .distinctUntilChanged()
-
- /** The model for the settings button. */
- val settings: FooterActionsButtonViewModel =
- FooterActionsButtonViewModel(
- id = R.id.settings_button_container,
- Icon.Resource(
- R.drawable.ic_settings,
- ContentDescription.Resource(R.string.accessibility_quick_settings_settings)
- ),
- iconTint =
- Utils.getColorAttrDefaultColor(
- context,
- R.attr.onShadeInactiveVariant,
- ),
- backgroundColor = R.attr.shadeInactive,
- this::onSettingsButtonClicked,
- )
-
- /** The model for the power button. */
- val power: FooterActionsButtonViewModel? =
- if (showPowerButton) {
- FooterActionsButtonViewModel(
- id = R.id.pm_lite,
- Icon.Resource(
- android.R.drawable.ic_lock_power_off,
- ContentDescription.Resource(R.string.accessibility_quick_settings_power_menu)
- ),
- iconTint =
- Utils.getColorAttrDefaultColor(
- context,
- R.attr.onShadeActive,
- ),
- backgroundColor = R.attr.shadeActive,
- this::onPowerButtonClicked,
- )
- } else {
- null
- }
-
- /** Called when the visibility of the UI rendering this model should be changed. */
fun onVisibilityChangeRequested(visible: Boolean) {
_isVisible.value = visible
}
@@ -195,89 +113,6 @@
}
}
- /**
- * Observe the device monitoring dialog requests and show the dialog accordingly. This function
- * will suspend indefinitely and will need to be cancelled to stop observing.
- *
- * Important: [quickSettingsContext] must be the [Context] associated to the
- * [Quick Settings fragment][com.android.systemui.qs.QSFragmentLegacy], and the call to this
- * function must be cancelled when that fragment is destroyed.
- */
- suspend fun observeDeviceMonitoringDialogRequests(quickSettingsContext: Context) {
- footerActionsInteractor.deviceMonitoringDialogRequests.collect {
- footerActionsInteractor.showDeviceMonitoringDialog(
- quickSettingsContext,
- expandable = null,
- )
- }
- }
-
- private fun onSecurityButtonClicked(quickSettingsContext: Context, expandable: Expandable) {
- if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- return
- }
-
- footerActionsInteractor.showDeviceMonitoringDialog(quickSettingsContext, expandable)
- }
-
- private fun onForegroundServiceButtonClicked(expandable: Expandable) {
- if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- return
- }
-
- footerActionsInteractor.showForegroundServicesDialog(expandable)
- }
-
- private fun onUserSwitcherClicked(expandable: Expandable) {
- if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- return
- }
-
- footerActionsInteractor.showUserSwitcher(expandable)
- }
-
- private fun onSettingsButtonClicked(expandable: Expandable) {
- if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- return
- }
-
- footerActionsInteractor.showSettings(expandable)
- }
-
- private fun onPowerButtonClicked(expandable: Expandable) {
- if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- return
- }
-
- footerActionsInteractor.showPowerMenuDialog(globalActionsDialogLite, expandable)
- }
-
- private fun userSwitcherButton(
- status: UserSwitcherStatusModel.Enabled
- ): FooterActionsButtonViewModel {
- val icon = status.currentUserImage!!
-
- return FooterActionsButtonViewModel(
- id = R.id.multi_user_switch,
- icon =
- Icon.Loaded(
- icon,
- ContentDescription.Loaded(
- userSwitcherContentDescription(status.currentUserName)
- ),
- ),
- iconTint = null,
- backgroundColor = R.attr.shadeInactive,
- onClick = this::onUserSwitcherClicked,
- )
- }
-
- private fun userSwitcherContentDescription(currentUser: String?): String? {
- return currentUser?.let { user ->
- context.getString(R.string.accessibility_quick_settings_user, user)
- }
- }
-
@SysUISingleton
class Factory
@Inject
@@ -315,8 +150,237 @@
)
}
}
+}
- companion object {
- private const val TAG = "FooterActionsViewModel"
+fun FooterActionsViewModel(
+ @Application appContext: Context,
+ footerActionsInteractor: FooterActionsInteractor,
+ falsingManager: FalsingManager,
+ globalActionsDialogLite: GlobalActionsDialogLite,
+ showPowerButton: Boolean,
+): FooterActionsViewModel {
+ suspend fun observeDeviceMonitoringDialogRequests(quickSettingsContext: Context) {
+ footerActionsInteractor.deviceMonitoringDialogRequests.collect {
+ footerActionsInteractor.showDeviceMonitoringDialog(
+ quickSettingsContext,
+ expandable = null,
+ )
+ }
}
+
+ fun onSecurityButtonClicked(quickSettingsContext: Context, expandable: Expandable) {
+ if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ return
+ }
+
+ footerActionsInteractor.showDeviceMonitoringDialog(quickSettingsContext, expandable)
+ }
+
+ fun onForegroundServiceButtonClicked(expandable: Expandable) {
+ if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ return
+ }
+
+ footerActionsInteractor.showForegroundServicesDialog(expandable)
+ }
+
+ fun onUserSwitcherClicked(expandable: Expandable) {
+ if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ return
+ }
+
+ footerActionsInteractor.showUserSwitcher(expandable)
+ }
+
+ fun onSettingsButtonClicked(expandable: Expandable) {
+ if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ return
+ }
+
+ footerActionsInteractor.showSettings(expandable)
+ }
+
+ fun onPowerButtonClicked(expandable: Expandable) {
+ if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ return
+ }
+
+ footerActionsInteractor.showPowerMenuDialog(globalActionsDialogLite, expandable)
+ }
+
+ val qsThemedContext = ContextThemeWrapper(appContext, R.style.Theme_SystemUI_QuickSettings)
+
+ val security =
+ footerActionsInteractor.securityButtonConfig
+ .map { config ->
+ config?.let { securityButtonViewModel(it, ::onSecurityButtonClicked) }
+ }
+ .distinctUntilChanged()
+
+ val foregroundServices =
+ combine(
+ footerActionsInteractor.foregroundServicesCount,
+ footerActionsInteractor.hasNewForegroundServices,
+ security,
+ ) { foregroundServicesCount, hasNewChanges, securityModel ->
+ if (foregroundServicesCount <= 0) {
+ return@combine null
+ }
+
+ foregroundServicesButtonViewModel(
+ qsThemedContext,
+ foregroundServicesCount,
+ securityModel,
+ hasNewChanges,
+ ::onForegroundServiceButtonClicked,
+ )
+ }
+ .distinctUntilChanged()
+
+ val userSwitcher =
+ footerActionsInteractor.userSwitcherStatus
+ .map { userSwitcherStatus ->
+ when (userSwitcherStatus) {
+ UserSwitcherStatusModel.Disabled -> null
+ is UserSwitcherStatusModel.Enabled -> {
+ if (userSwitcherStatus.currentUserImage == null) {
+ Log.e(
+ TAG,
+ "Skipped the addition of user switcher button because " +
+ "currentUserImage is missing",
+ )
+ return@map null
+ }
+
+ userSwitcherButtonViewModel(
+ qsThemedContext,
+ userSwitcherStatus,
+ ::onUserSwitcherClicked
+ )
+ }
+ }
+ }
+ .distinctUntilChanged()
+
+ val settings = settingsButtonViewModel(qsThemedContext, ::onSettingsButtonClicked)
+ val power =
+ if (showPowerButton) {
+ powerButtonViewModel(qsThemedContext, ::onPowerButtonClicked)
+ } else {
+ null
+ }
+
+ return FooterActionsViewModel(
+ security = security,
+ foregroundServices = foregroundServices,
+ userSwitcher = userSwitcher,
+ settings = settings,
+ power = power,
+ observeDeviceMonitoringDialogRequests = ::observeDeviceMonitoringDialogRequests,
+ )
+}
+
+fun securityButtonViewModel(
+ config: SecurityButtonConfig,
+ onSecurityButtonClicked: (Context, Expandable) -> Unit,
+): FooterActionsSecurityButtonViewModel {
+ val (icon, text, isClickable) = config
+ return FooterActionsSecurityButtonViewModel(
+ icon,
+ text,
+ if (isClickable) onSecurityButtonClicked else null,
+ )
+}
+
+fun foregroundServicesButtonViewModel(
+ qsThemedContext: Context,
+ foregroundServicesCount: Int,
+ securityModel: FooterActionsSecurityButtonViewModel?,
+ hasNewChanges: Boolean,
+ onForegroundServiceButtonClicked: (Expandable) -> Unit,
+): FooterActionsForegroundServicesButtonViewModel {
+ val text =
+ icuMessageFormat(
+ qsThemedContext.resources,
+ R.string.fgs_manager_footer_label,
+ foregroundServicesCount,
+ )
+
+ return FooterActionsForegroundServicesButtonViewModel(
+ foregroundServicesCount,
+ text = text,
+ displayText = securityModel == null,
+ hasNewChanges = hasNewChanges,
+ onForegroundServiceButtonClicked,
+ )
+}
+
+fun userSwitcherButtonViewModel(
+ qsThemedContext: Context,
+ status: UserSwitcherStatusModel.Enabled,
+ onUserSwitcherClicked: (Expandable) -> Unit,
+): FooterActionsButtonViewModel {
+ val icon = status.currentUserImage!!
+ return FooterActionsButtonViewModel(
+ id = R.id.multi_user_switch,
+ icon =
+ Icon.Loaded(
+ icon,
+ ContentDescription.Loaded(
+ userSwitcherContentDescription(qsThemedContext, status.currentUserName)
+ ),
+ ),
+ iconTint = null,
+ backgroundColor = R.attr.shadeInactive,
+ onClick = onUserSwitcherClicked,
+ )
+}
+
+private fun userSwitcherContentDescription(
+ qsThemedContext: Context,
+ currentUser: String?
+): String? {
+ return currentUser?.let { user ->
+ qsThemedContext.getString(R.string.accessibility_quick_settings_user, user)
+ }
+}
+
+fun settingsButtonViewModel(
+ qsThemedContext: Context,
+ onSettingsButtonClicked: (Expandable) -> Unit,
+): FooterActionsButtonViewModel {
+ return FooterActionsButtonViewModel(
+ id = R.id.settings_button_container,
+ Icon.Resource(
+ R.drawable.ic_settings,
+ ContentDescription.Resource(R.string.accessibility_quick_settings_settings)
+ ),
+ iconTint =
+ Utils.getColorAttrDefaultColor(
+ qsThemedContext,
+ R.attr.onShadeInactiveVariant,
+ ),
+ backgroundColor = R.attr.shadeInactive,
+ onSettingsButtonClicked,
+ )
+}
+
+fun powerButtonViewModel(
+ qsThemedContext: Context,
+ onPowerButtonClicked: (Expandable) -> Unit,
+): FooterActionsButtonViewModel {
+ return FooterActionsButtonViewModel(
+ id = R.id.pm_lite,
+ Icon.Resource(
+ android.R.drawable.ic_lock_power_off,
+ ContentDescription.Resource(R.string.accessibility_quick_settings_power_menu)
+ ),
+ iconTint =
+ Utils.getColorAttrDefaultColor(
+ qsThemedContext,
+ R.attr.onShadeActive,
+ ),
+ backgroundColor = R.attr.shadeActive,
+ onPowerButtonClicked,
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 5a9c0a1..f8e0159 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -23,7 +23,10 @@
import android.content.res.ColorStateList
import android.content.res.Configuration
import android.content.res.Resources.ID_NULL
+import android.graphics.Color
+import android.graphics.PorterDuff
import android.graphics.drawable.Drawable
+import android.graphics.drawable.LayerDrawable
import android.graphics.drawable.RippleDrawable
import android.os.Trace
import android.service.quicksettings.Tile
@@ -44,7 +47,6 @@
import androidx.annotation.VisibleForTesting
import com.android.settingslib.Utils
import com.android.systemui.FontSizeUtils
-import com.android.systemui.res.R
import com.android.systemui.animation.LaunchableView
import com.android.systemui.animation.LaunchableViewDelegate
import com.android.systemui.plugins.qs.QSIconView
@@ -53,6 +55,7 @@
import com.android.systemui.plugins.qs.QSTileView
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSIconViewImpl.QS_ANIM_LENGTH
+import com.android.systemui.res.R
import java.util.Objects
private const val TAG = "QSTileViewImpl"
@@ -67,6 +70,7 @@
private const val LABEL_NAME = "label"
private const val SECONDARY_LABEL_NAME = "secondaryLabel"
private const val CHEVRON_NAME = "chevron"
+ private const val OVERLAY_NAME = "overlay"
const val UNAVAILABLE_ALPHA = 0.3f
@VisibleForTesting
internal const val TILE_STATE_RES_PREFIX = "tile_states_"
@@ -97,6 +101,13 @@
private val colorInactive = Utils.getColorAttrDefaultColor(context, R.attr.shadeInactive)
private val colorUnavailable = Utils.getColorAttrDefaultColor(context, R.attr.shadeDisabled)
+ private val overlayColorActive = Utils.applyAlpha(
+ /* alpha= */ 0.11f,
+ Utils.getColorAttrDefaultColor(context, R.attr.onShadeActive))
+ private val overlayColorInactive = Utils.applyAlpha(
+ /* alpha= */ 0.08f,
+ Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactive))
+
private val colorLabelActive = Utils.getColorAttrDefaultColor(context, R.attr.onShadeActive)
private val colorLabelInactive = Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactive)
private val colorLabelUnavailable =
@@ -123,8 +134,13 @@
protected var showRippleEffect = true
private lateinit var ripple: RippleDrawable
- private lateinit var colorBackgroundDrawable: Drawable
- private var paintColor: Int = 0
+ private lateinit var backgroundDrawable: LayerDrawable
+ private lateinit var backgroundBaseDrawable: Drawable
+ private lateinit var backgroundOverlayDrawable: Drawable
+
+ private var backgroundColor: Int = 0
+ private var backgroundOverlayColor: Int = 0
+
private val singleAnimator: ValueAnimator = ValueAnimator().apply {
setDuration(QS_ANIM_LENGTH)
addUpdateListener { animation ->
@@ -134,7 +150,8 @@
animation.getAnimatedValue(BACKGROUND_NAME) as Int,
animation.getAnimatedValue(LABEL_NAME) as Int,
animation.getAnimatedValue(SECONDARY_LABEL_NAME) as Int,
- animation.getAnimatedValue(CHEVRON_NAME) as Int
+ animation.getAnimatedValue(CHEVRON_NAME) as Int,
+ animation.getAnimatedValue(OVERLAY_NAME) as Int,
)
}
}
@@ -263,7 +280,12 @@
fun createTileBackground(): Drawable {
ripple = mContext.getDrawable(R.drawable.qs_tile_background) as RippleDrawable
- colorBackgroundDrawable = ripple.findDrawableByLayerId(R.id.background)
+ backgroundDrawable = ripple.findDrawableByLayerId(R.id.background) as LayerDrawable
+ backgroundBaseDrawable =
+ backgroundDrawable.findDrawableByLayerId(R.id.qs_tile_background_base)
+ backgroundOverlayDrawable =
+ backgroundDrawable.findDrawableByLayerId(R.id.qs_tile_background_overlay)
+ backgroundOverlayDrawable.mutate().setTintMode(PorterDuff.Mode.SRC)
return ripple
}
@@ -343,10 +365,10 @@
ripple.also {
// In case that the colorBackgroundDrawable was used as the background, make sure
// it has the correct callback instead of null
- colorBackgroundDrawable.callback = it
+ backgroundDrawable.callback = it
}
} else {
- colorBackgroundDrawable
+ backgroundDrawable
}
}
@@ -512,7 +534,7 @@
singleAnimator.setValues(
colorValuesHolder(
BACKGROUND_NAME,
- paintColor,
+ backgroundColor,
getBackgroundColorForState(state.state, state.disabledByPolicy)
),
colorValuesHolder(
@@ -529,6 +551,11 @@
CHEVRON_NAME,
chevronView.imageTintList?.defaultColor ?: 0,
getChevronColorForState(state.state, state.disabledByPolicy)
+ ),
+ colorValuesHolder(
+ OVERLAY_NAME,
+ backgroundOverlayColor,
+ getOverlayColorForState(state.state)
)
)
singleAnimator.start()
@@ -537,7 +564,8 @@
getBackgroundColorForState(state.state, state.disabledByPolicy),
getLabelColorForState(state.state, state.disabledByPolicy),
getSecondaryLabelColorForState(state.state, state.disabledByPolicy),
- getChevronColorForState(state.state, state.disabledByPolicy)
+ getChevronColorForState(state.state, state.disabledByPolicy),
+ getOverlayColorForState(state.state)
)
}
}
@@ -555,17 +583,19 @@
backgroundColor: Int,
labelColor: Int,
secondaryLabelColor: Int,
- chevronColor: Int
+ chevronColor: Int,
+ overlayColor: Int,
) {
setColor(backgroundColor)
setLabelColor(labelColor)
setSecondaryLabelColor(secondaryLabelColor)
setChevronColor(chevronColor)
+ setOverlayColor(overlayColor)
}
private fun setColor(color: Int) {
- colorBackgroundDrawable.mutate().setTint(color)
- paintColor = color
+ backgroundBaseDrawable.mutate().setTint(color)
+ backgroundColor = color
}
private fun setLabelColor(color: Int) {
@@ -580,6 +610,11 @@
chevronView.imageTintList = ColorStateList.valueOf(color)
}
+ private fun setOverlayColor(overlayColor: Int) {
+ backgroundOverlayDrawable.setTint(overlayColor)
+ backgroundOverlayColor = overlayColor
+ }
+
private fun loadSideViewDrawableIfNecessary(state: QSTile.State) {
if (state.sideViewCustomDrawable != null) {
customDrawableView.setImageDrawable(state.sideViewCustomDrawable)
@@ -654,9 +689,17 @@
private fun getChevronColorForState(state: Int, disabledByPolicy: Boolean = false): Int =
getSecondaryLabelColorForState(state, disabledByPolicy)
+ private fun getOverlayColorForState(state: Int): Int {
+ return when (state) {
+ Tile.STATE_ACTIVE -> overlayColorActive
+ Tile.STATE_INACTIVE -> overlayColorInactive
+ else -> Color.TRANSPARENT
+ }
+ }
+
@VisibleForTesting
internal fun getCurrentColors(): List<Int> = listOf(
- paintColor,
+ backgroundColor,
label.currentTextColor,
secondaryLabel.currentTextColor,
chevronView.imageTintList?.defaultColor ?: 0
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index fb71cef..17251c3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -36,7 +36,6 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.res.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
@@ -49,6 +48,7 @@
import com.android.systemui.qs.SettingObserver;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.util.settings.GlobalSettings;
@@ -56,8 +56,6 @@
import javax.inject.Inject;
-
-
/** Quick settings tile: Airplane mode **/
public class AirplaneModeTile extends QSTileImpl<BooleanState> {
@@ -90,8 +88,7 @@
mBroadcastDispatcher = broadcastDispatcher;
mLazyConnectivityManager = lazyConnectivityManager;
- mSetting = new SettingObserver(globalSettings, mHandler, Global.AIRPLANE_MODE_ON,
- userTracker.getUserId()) {
+ mSetting = new SettingObserver(globalSettings, mHandler, Global.AIRPLANE_MODE_ON) {
@Override
protected void handleValueChanged(int value, boolean observedChange) {
// mHandler is the background handler so calling this is OK
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
index 18d472b..426aa55 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
@@ -29,7 +29,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
@@ -38,9 +37,10 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
-import com.android.systemui.qs.SettingObserver;
+import com.android.systemui.qs.UserSettingObserver;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.util.settings.SecureSettings;
@@ -53,7 +53,7 @@
private final BatteryController mBatteryController;
@VisibleForTesting
- protected final SettingObserver mSetting;
+ protected final UserSettingObserver mSetting;
private int mLevel;
private boolean mPowerSave;
@@ -79,7 +79,7 @@
mBatteryController = batteryController;
mBatteryController.observe(getLifecycle(), this);
int currentUser = host.getUserContext().getUserId();
- mSetting = new SettingObserver(
+ mSetting = new UserSettingObserver(
secureSettings,
mHandler,
Secure.LOW_POWER_WARNING_ACKNOWLEDGED,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index d862f56..18d2f30 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -39,7 +39,6 @@
import com.android.settingslib.Utils;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
@@ -53,6 +52,7 @@
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialogViewModel;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.BluetoothController;
import java.util.List;
@@ -198,6 +198,7 @@
}
state.expandedAccessibilityClassName = Switch.class.getName();
+ state.forceExpandIcon = mFeatureFlags.isEnabled(Flags.BLUETOOTH_QS_TILE_DIALOG);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java
index ee57b2b..c8adbfc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java
@@ -28,8 +28,6 @@
import androidx.annotation.Nullable;
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.res.R;
-import com.android.systemui.res.R.drawable;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
@@ -38,9 +36,10 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
-import com.android.systemui.qs.SettingObserver;
+import com.android.systemui.qs.UserSettingObserver;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.util.settings.SecureSettings;
@@ -51,8 +50,8 @@
public static final String TILE_SPEC = "color_correction";
- private final Icon mIcon = ResourceIcon.get(drawable.ic_qs_color_correction);
- private final SettingObserver mSetting;
+ private final Icon mIcon = ResourceIcon.get(R.drawable.ic_qs_color_correction);
+ private final UserSettingObserver mSetting;
@Inject
public ColorCorrectionTile(
@@ -71,7 +70,7 @@
super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
- mSetting = new SettingObserver(secureSettings, mHandler,
+ mSetting = new UserSettingObserver(secureSettings, mHandler,
Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, userTracker.getUserId()) {
@Override
protected void handleValueChanged(int value, boolean observedChange) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index 993ada6..c34a584 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -29,8 +29,6 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.res.R;
-import com.android.systemui.res.R.drawable;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
@@ -39,9 +37,10 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
-import com.android.systemui.qs.SettingObserver;
+import com.android.systemui.qs.UserSettingObserver;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.util.settings.SecureSettings;
@@ -51,7 +50,7 @@
public class ColorInversionTile extends QSTileImpl<BooleanState> {
public static final String TILE_SPEC = "inversion";
- private final SettingObserver mSetting;
+ private final UserSettingObserver mSetting;
@Inject
public ColorInversionTile(
@@ -70,7 +69,7 @@
super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
- mSetting = new SettingObserver(secureSettings, mHandler,
+ mSetting = new UserSettingObserver(secureSettings, mHandler,
Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, userTracker.getUserId()) {
@Override
protected void handleValueChanged(int value, boolean observedChange) {
@@ -126,8 +125,8 @@
state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
state.label = mContext.getString(R.string.quick_settings_inversion_label);
state.icon = ResourceIcon.get(state.value
- ? drawable.qs_invert_colors_icon_on
- : drawable.qs_invert_colors_icon_off);
+ ? R.drawable.qs_invert_colors_icon_on
+ : R.drawable.qs_invert_colors_icon_off);
state.expandedAccessibilityClassName = Switch.class.getName();
state.contentDescription = state.label;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 0617b30..f6518d1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -45,7 +45,6 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.notification.EnableZenModeDialog;
import com.android.systemui.Prefs;
-import com.android.systemui.res.R;
import com.android.systemui.animation.DialogCuj;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.dagger.qualifiers.Background;
@@ -56,10 +55,11 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
-import com.android.systemui.qs.SettingObserver;
+import com.android.systemui.qs.UserSettingObserver;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.qs.tiles.dialog.QSZenModeDialogMetricsLogger;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.settings.SecureSettings;
@@ -81,7 +81,7 @@
private final ZenModeController mController;
private final SharedPreferences mSharedPreferences;
- private final SettingObserver mSettingZenDuration;
+ private final UserSettingObserver mSettingZenDuration;
private final DialogLaunchAnimator mDialogLaunchAnimator;
private final QSZenModeDialogMetricsLogger mQSZenDialogMetricsLogger;
@@ -109,7 +109,7 @@
mSharedPreferences = sharedPreferences;
mController.observe(getLifecycle(), mZenCallback);
mDialogLaunchAnimator = dialogLaunchAnimator;
- mSettingZenDuration = new SettingObserver(secureSettings, mUiHandler,
+ mSettingZenDuration = new UserSettingObserver(secureSettings, mUiHandler,
Settings.Secure.ZEN_DURATION, getHost().getUserId()) {
@Override
protected void handleValueChanged(int value, boolean observedChange) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java
index a08b3fc..4f0a63b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java
@@ -38,7 +38,6 @@
import androidx.annotation.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.res.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
@@ -49,9 +48,10 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
-import com.android.systemui.qs.SettingObserver;
+import com.android.systemui.qs.UserSettingObserver;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.util.settings.SecureSettings;
@@ -69,8 +69,8 @@
private final Icon mIconUndocked = ResourceIcon.get(R.drawable.ic_qs_screen_saver_undocked);
private final IDreamManager mDreamManager;
private final BroadcastDispatcher mBroadcastDispatcher;
- private final SettingObserver mEnabledSettingObserver;
- private final SettingObserver mDreamSettingObserver;
+ private final UserSettingObserver mEnabledSettingObserver;
+ private final UserSettingObserver mDreamSettingObserver;
private final UserTracker mUserTracker;
private final boolean mDreamSupported;
private final boolean mDreamOnlyEnabledForDockUser;
@@ -111,14 +111,14 @@
statusBarStateController, activityStarter, qsLogger);
mDreamManager = dreamManager;
mBroadcastDispatcher = broadcastDispatcher;
- mEnabledSettingObserver = new SettingObserver(secureSettings, mHandler,
+ mEnabledSettingObserver = new UserSettingObserver(secureSettings, mHandler,
Settings.Secure.SCREENSAVER_ENABLED, userTracker.getUserId()) {
@Override
protected void handleValueChanged(int value, boolean observedChange) {
refreshState();
}
};
- mDreamSettingObserver = new SettingObserver(secureSettings, mHandler,
+ mDreamSettingObserver = new UserSettingObserver(secureSettings, mHandler,
Settings.Secure.SCREENSAVER_COMPONENTS, userTracker.getUserId()) {
@Override
protected void handleValueChanged(int value, boolean observedChange) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java
index 78af976..b08e6a5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java
@@ -28,7 +28,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
@@ -37,9 +36,10 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
-import com.android.systemui.qs.SettingObserver;
+import com.android.systemui.qs.UserSettingObserver;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.util.settings.SecureSettings;
import com.android.wm.shell.onehanded.OneHanded;
@@ -53,7 +53,7 @@
private final Icon mIcon = ResourceIcon.get(
com.android.internal.R.drawable.ic_qs_one_handed_mode);
- private final SettingObserver mSetting;
+ private final UserSettingObserver mSetting;
@Inject
public OneHandedModeTile(
@@ -70,7 +70,7 @@
SecureSettings secureSettings) {
super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
- mSetting = new SettingObserver(secureSettings, mHandler,
+ mSetting = new UserSettingObserver(secureSettings, mHandler,
Settings.Secure.ONE_HANDED_MODE_ENABLED, userTracker.getUserId()) {
@Override
protected void handleValueChanged(int value, boolean observedChange) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index 5a95004..f1d8f9f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -36,7 +36,6 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
@@ -45,9 +44,10 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
-import com.android.systemui.qs.SettingObserver;
+import com.android.systemui.qs.UserSettingObserver;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.RotationLockController;
import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback;
@@ -67,7 +67,7 @@
private final RotationLockController mController;
private final SensorPrivacyManager mPrivacyManager;
private final BatteryController mBatteryController;
- private final SettingObserver mSetting;
+ private final UserSettingObserver mSetting;
private final boolean mAllowRotationResolver;
@Inject
@@ -93,7 +93,7 @@
mPrivacyManager = privacyManager;
mBatteryController = batteryController;
int currentUser = host.getUserContext().getUserId();
- mSetting = new SettingObserver(
+ mSetting = new UserSettingObserver(
secureSettings,
mHandler,
Secure.CAMERA_AUTOROTATE,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt
index 8ae2dc2..80af76d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt
@@ -102,9 +102,10 @@
showSeeAll: Boolean,
showPairNewDevice: Boolean
) {
- seeAllViewGroup.visibility = if (showSeeAll) VISIBLE else GONE
- pairNewDeviceViewGroup.visibility = if (showPairNewDevice) VISIBLE else GONE
- deviceItemAdapter.refreshDeviceItemList(deviceItem)
+ deviceItemAdapter.refreshDeviceItemList(deviceItem) {
+ seeAllViewGroup.visibility = if (showSeeAll) VISIBLE else GONE
+ pairNewDeviceViewGroup.visibility = if (showPairNewDevice) VISIBLE else GONE
+ }
}
internal fun onBluetoothStateUpdated(isEnabled: Boolean, subtitleResId: Int) {
@@ -173,8 +174,8 @@
internal fun getItem(position: Int) = asyncListDiffer.currentList[position]
- internal fun refreshDeviceItemList(updated: List<DeviceItem>) {
- asyncListDiffer.submitList(updated)
+ internal fun refreshDeviceItemList(updated: List<DeviceItem>, callback: () -> Unit) {
+ asyncListDiffer.submitList(updated, callback)
}
internal inner class DeviceItemViewHolder(view: View) : RecyclerView.ViewHolder(view) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt
index 97e1783..8e27493 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt
@@ -113,7 +113,6 @@
.launchIn(this)
deviceItemInteractor.deviceItemUpdate
- .filterNotNull()
.onEach {
dialog!!.onDeviceItemUpdated(
it.take(MAX_DEVICE_ITEM_ENTRY),
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt
index 14d24f9..e196c6c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt
@@ -34,10 +34,10 @@
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.withContext
@@ -55,10 +55,10 @@
@Background private val backgroundDispatcher: CoroutineDispatcher,
) {
- private val mutableDeviceItemUpdate: MutableStateFlow<List<DeviceItem>?> =
- MutableStateFlow(null)
+ private val mutableDeviceItemUpdate: MutableSharedFlow<List<DeviceItem>> =
+ MutableSharedFlow(extraBufferCapacity = 1)
internal val deviceItemUpdate
- get() = mutableDeviceItemUpdate.asStateFlow()
+ get() = mutableDeviceItemUpdate.asSharedFlow()
internal val deviceItemUpdateRequest: SharedFlow<Unit> =
conflatedCallbackFlow {
@@ -119,16 +119,15 @@
internal suspend fun updateDeviceItems(context: Context) {
withContext(backgroundDispatcher) {
- val mostRecentlyConnectedDevices = bluetoothAdapter?.mostRecentlyConnectedDevices
-
- mutableDeviceItemUpdate.value =
+ mutableDeviceItemUpdate.tryEmit(
bluetoothTileDialogRepository.cachedDevices
.mapNotNull { cachedDevice ->
deviceItemFactoryList
.firstOrNull { it.isFilterMatched(cachedDevice, audioManager) }
?.create(context, cachedDevice)
}
- .sort(displayPriority, mostRecentlyConnectedDevices)
+ .sort(displayPriority, bluetoothAdapter?.mostRecentlyConnectedDevices)
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt
index f704894..3927873 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt
@@ -42,13 +42,6 @@
* scene, this value will remain true after the pointer is no longer touching the screen and
* will be true in any transition created to animate back to the original position.
*/
- val isInitiatedByUserInput: Boolean,
-
- /**
- * Whether user input is currently driving the transition. For example, if a user is
- * dragging a pointer, this emits true. Once they lift their finger, this emits false while
- * the transition completes/settles.
- */
- val isUserInputOngoing: Flow<Boolean>,
+ val isUserInputDriven: Boolean,
) : ObservableTransitionState()
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
index 9325e18..00d480a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
@@ -19,6 +19,7 @@
import android.app.Activity;
import android.app.ActivityOptions;
import android.content.ComponentName;
+import android.content.ContentProvider;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.HardwareRenderer;
@@ -44,10 +45,10 @@
import com.android.internal.app.ChooserActivity;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.view.OneShotPreDrawListener;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.res.R;
import com.android.systemui.screenshot.CropView.CropBoundary;
import com.android.systemui.screenshot.ScrollCaptureController.LongScreenshot;
import com.android.systemui.settings.UserTracker;
@@ -421,13 +422,15 @@
Log.e(TAG, "failed to export", e);
return;
}
+ Uri exported = ContentProvider.getUriWithoutUserId(result.uri);
+ Log.e(TAG, action + " uri=" + exported);
switch (action) {
case EDIT:
- doEdit(result.uri);
+ doEdit(exported);
break;
case SHARE:
- doShare(result.uri);
+ doShare(exported);
break;
case SAVE:
// Nothing more to do
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
index bd592c9..cf1fbe3 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
@@ -16,6 +16,8 @@
package com.android.systemui.settings
+import com.android.systemui.util.annotations.WeaklyReferencedCallback
+
import android.content.Context
import android.content.pm.UserInfo
import android.os.UserHandle
@@ -64,6 +66,7 @@
/**
* Callback for notifying of changes.
*/
+ @WeaklyReferencedCallback
interface Callback {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
index 6117f9f..e487a6f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
@@ -261,7 +261,7 @@
when (state) {
is ObservableTransitionState.Idle -> false
is ObservableTransitionState.Transition ->
- state.isInitiatedByUserInput &&
+ state.isUserInputDriven &&
(state.toScene == sceneKey || state.fromScene == sceneKey)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java
index 9d56a8e..362786e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java
@@ -115,6 +115,7 @@
*
* @deprecated Use {@link #onRankingApplied()} instead.
*/
+ @Deprecated
default void onRankingUpdate(NotificationListenerService.RankingMap rankingMap) {
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 847d948..661768d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -351,12 +351,21 @@
@Override
public long performRemoveAnimation(long duration, long delay, float translationDirection,
- boolean isHeadsUpAnimation, Runnable onFinishedRunnable,
+ boolean isHeadsUpAnimation, Runnable onStartedRunnable, Runnable onFinishedRunnable,
AnimatorListenerAdapter animationListener) {
enableAppearDrawing(true);
mIsHeadsUpAnimation = isHeadsUpAnimation;
- startAppearAnimation(false /* isAppearing */, translationDirection,
- delay, duration, onFinishedRunnable, animationListener);
+ if (mDrawingAppearAnimation) {
+ startAppearAnimation(false /* isAppearing */, translationDirection,
+ delay, duration, onStartedRunnable, onFinishedRunnable, animationListener);
+ } else {
+ if (onStartedRunnable != null) {
+ onStartedRunnable.run();
+ }
+ if (onFinishedRunnable != null) {
+ onFinishedRunnable.run();
+ }
+ }
return 0;
}
@@ -365,12 +374,14 @@
Runnable onFinishRunnable) {
enableAppearDrawing(true);
mIsHeadsUpAnimation = isHeadsUpAppear;
- startAppearAnimation(true /* isAppearing */, isHeadsUpAppear ? 0.0f : -1.0f, delay,
- duration, null, null);
+ if (mDrawingAppearAnimation) {
+ startAppearAnimation(true /* isAppearing */, isHeadsUpAppear ? 0.0f : -1.0f, delay,
+ duration, null, null, null);
+ }
}
private void startAppearAnimation(boolean isAppearing, float translationDirection, long delay,
- long duration, final Runnable onFinishedRunnable,
+ long duration, final Runnable onStartedRunnable, final Runnable onFinishedRunnable,
AnimatorListenerAdapter animationListener) {
mAnimationTranslationY = translationDirection * getActualHeight();
cancelAppearAnimation();
@@ -434,6 +445,9 @@
@Override
public void onAnimationStart(Animator animation) {
+ if (onStartedRunnable != null) {
+ onStartedRunnable.run();
+ }
mRunWithoutInterruptions = true;
Configuration.Builder builder = Configuration.Builder
.withView(getCujType(isAppearing), ActivatableNotificationView.this);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index bc570f2..9340b85 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -2927,6 +2927,7 @@
long delay,
float translationDirection,
boolean isHeadsUpAnimation,
+ Runnable onStartedRunnable,
Runnable onFinishedRunnable,
AnimatorListenerAdapter animationListener) {
if (mMenuRow != null && mMenuRow.isMenuVisible()) {
@@ -2934,10 +2935,16 @@
if (anim != null) {
anim.addListener(new AnimatorListenerAdapter() {
@Override
+ public void onAnimationStart(Animator animation) {
+ if (onStartedRunnable != null) {
+ onStartedRunnable.run();
+ }
+ }
+ @Override
public void onAnimationEnd(Animator animation) {
ExpandableNotificationRow.super.performRemoveAnimation(
duration, delay, translationDirection, isHeadsUpAnimation,
- onFinishedRunnable, animationListener);
+ null, onFinishedRunnable, animationListener);
}
});
anim.start();
@@ -2945,7 +2952,7 @@
}
}
return super.performRemoveAnimation(duration, delay, translationDirection,
- isHeadsUpAnimation, onFinishedRunnable, animationListener);
+ isHeadsUpAnimation, onStartedRunnable, onFinishedRunnable, animationListener);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index f2f55a8..6edab4d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -69,6 +69,9 @@
private boolean mClipToActualHeight = true;
private boolean mChangingPosition = false;
private ViewGroup mTransientContainer;
+
+ // Needs to be added as transient view when removed from parent, because it's in animation
+ private boolean mInRemovalAnimation;
private boolean mInShelf;
private boolean mTransformingInShelf;
protected float mContentTransformationAmount;
@@ -381,6 +384,7 @@
*/
public abstract long performRemoveAnimation(long duration,
long delay, float translationDirection, boolean isHeadsUpAnimation,
+ Runnable onStartedRunnable,
Runnable onFinishedRunnable,
AnimatorListenerAdapter animationListener);
@@ -604,6 +608,25 @@
}
/**
+ * Add the view to a transient container.
+ */
+ public void addToTransientContainer(ViewGroup container, int index) {
+ container.addTransientView(this, index);
+ setTransientContainer(container);
+ }
+
+ /**
+ * @return If the view is in a process of removal animation.
+ */
+ public boolean inRemovalAnimation() {
+ return mInRemovalAnimation;
+ }
+
+ public void setInRemovalAnimation(boolean inRemovalAnimation) {
+ mInRemovalAnimation = inRemovalAnimation;
+ }
+
+ /**
* @return true if the group's expansion state is changing, false otherwise.
*/
public boolean isGroupExpansionChanging() {
@@ -837,6 +860,7 @@
pw.println();
}
if (DUMP_VERBOSE) {
+ pw.println("mInRemovalAnimation: " + mInRemovalAnimation);
pw.println("mClipTopAmount: " + mClipTopAmount);
pw.println("mClipBottomAmount " + mClipBottomAmount);
pw.println("mClipToActualHeight: " + mClipToActualHeight);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
index e200b90..aabf295 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
@@ -237,9 +237,13 @@
@Override
public long performRemoveAnimation(long duration, long delay,
float translationDirection, boolean isHeadsUpAnimation,
+ Runnable onStartedRunnable,
Runnable onFinishedRunnable,
AnimatorListenerAdapter animationListener) {
// TODO: Use duration
+ if (onStartedRunnable != null) {
+ onStartedRunnable.run();
+ }
setContentVisible(false, true /* animate */, (cancelled) -> onFinishedRunnable.run());
return 0;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
index 5d46f52..bae5baa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
@@ -25,9 +25,7 @@
import com.android.systemui.res.R
import com.android.systemui.statusbar.notification.row.ExpandableView
-/**
- * Root view to insert Lock screen media controls into the notification stack.
- */
+/** Root view to insert Lock screen media controls into the notification stack. */
class MediaContainerView(context: Context, attrs: AttributeSet?) : ExpandableView(context, attrs) {
override var clipHeight = 0
@@ -46,8 +44,8 @@
}
private fun updateResources() {
- cornerRadius = context.resources
- .getDimensionPixelSize(R.dimen.notification_corner_radius).toFloat()
+ cornerRadius =
+ context.resources.getDimensionPixelSize(R.dimen.notification_corner_radius).toFloat()
}
public override fun updateClipping() {
@@ -70,18 +68,23 @@
}
override fun performRemoveAnimation(
- duration: Long,
- delay: Long,
- translationDirection: Float,
- isHeadsUpAnimation: Boolean,
- onFinishedRunnable: Runnable?,
- animationListener: AnimatorListenerAdapter?
+ duration: Long,
+ delay: Long,
+ translationDirection: Float,
+ isHeadsUpAnimation: Boolean,
+ onStartedRunnable: Runnable?,
+ onFinishedRunnable: Runnable?,
+ animationListener: AnimatorListenerAdapter?
): Long {
return 0
}
- override fun performAddAnimation(delay: Long, duration: Long, isHeadsUpAppear: Boolean,
- onEnd: Runnable?) {
+ override fun performAddAnimation(
+ delay: Long,
+ duration: Long,
+ isHeadsUpAppear: Boolean,
+ onEnd: Runnable?
+ ) {
// No animation, it doesn't need it, this would be local
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 6f3cd5d..79f8f22 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -20,6 +20,7 @@
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_SHADE_CLEAR_ALL;
+import static com.android.systemui.flags.Flags.UNCLEARED_TRANSIENT_HUN_FIX;
import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_SILENT;
import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE;
import static com.android.systemui.util.DumpUtilsKt.println;
@@ -576,6 +577,7 @@
mSplitShadeStateController = splitShadeStateController;
updateSplitNotificationShade();
}
+ private FeatureFlags mFeatureFlags;
private final ExpandableView.OnHeightChangedListener mOnChildHeightChangedListener =
new ExpandableView.OnHeightChangedListener() {
@@ -628,16 +630,16 @@
public NotificationStackScrollLayout(Context context, AttributeSet attrs) {
super(context, attrs, 0, 0);
Resources res = getResources();
- FeatureFlags featureFlags = Dependency.get(FeatureFlags.class);
- mIsSmallLandscapeLockscreenEnabled = featureFlags.isEnabled(
+ mFeatureFlags = Dependency.get(FeatureFlags.class);
+ mIsSmallLandscapeLockscreenEnabled = mFeatureFlags.isEnabled(
Flags.LOCKSCREEN_ENABLE_LANDSCAPE);
- mDebugLines = featureFlags.isEnabled(Flags.NSSL_DEBUG_LINES);
- mNewAodTransition = featureFlags.isEnabled(Flags.NEW_AOD_TRANSITION);
- mDebugRemoveAnimation = featureFlags.isEnabled(Flags.NSSL_DEBUG_REMOVE_ANIMATION);
- mSensitiveRevealAnimEndabled = featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM);
+ mDebugLines = mFeatureFlags.isEnabled(Flags.NSSL_DEBUG_LINES);
+ mNewAodTransition = mFeatureFlags.isEnabled(Flags.NEW_AOD_TRANSITION);
+ mDebugRemoveAnimation = mFeatureFlags.isEnabled(Flags.NSSL_DEBUG_REMOVE_ANIMATION);
+ mSensitiveRevealAnimEndabled = mFeatureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM);
mAnimatedInsets =
- new RefactorFlag(featureFlags, Flags.ANIMATED_NOTIFICATION_SHADE_INSETS);
- mShelfRefactor = new RefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR);
+ new RefactorFlag(mFeatureFlags, Flags.ANIMATED_NOTIFICATION_SHADE_INSETS);
+ mShelfRefactor = new RefactorFlag(mFeatureFlags, Flags.NOTIFICATION_SHELF_REFACTOR);
mSectionsManager = Dependency.get(NotificationSectionsManager.class);
mScreenOffAnimationController =
Dependency.get(ScreenOffAnimationController.class);
@@ -2779,8 +2781,7 @@
if (animationGenerated) {
if (!mSwipedOutViews.contains(child) || !isFullySwipedOut(child)) {
logAddTransientChild(child, container);
- container.addTransientView(child, 0);
- child.setTransientContainer(container);
+ child.addToTransientContainer(container, 0);
}
} else {
mSwipedOutViews.remove(child);
@@ -2870,7 +2871,8 @@
* Generate a remove animation for a child view.
*
* @param child The view to generate the remove animation for.
- * @return Whether an animation was generated.
+ * @return Whether a new animation was generated or an existing animation was detected by this
+ * method. We need this to determine if a transient view is needed.
*/
boolean generateRemoveAnimation(ExpandableView child) {
String key = "";
@@ -2887,10 +2889,23 @@
mAddedHeadsUpChildren.remove(child);
return false;
}
- if (isClickedHeadsUp(child)) {
- // An animation is already running, add it transiently
- mClearTransientViewsWhenFinished.add(child);
- return true;
+ if (mFeatureFlags.isEnabled(UNCLEARED_TRANSIENT_HUN_FIX)) {
+ // Skip adding animation for clicked heads up notifications when the
+ // Shade is closed, because the animation event is generated in
+ // generateHeadsUpAnimationEvents. Only report that an animation was
+ // actually generated (thus requesting the transient view be added)
+ // if a removal animation is in progress.
+ if (!isExpanded() && isClickedHeadsUp(child)) {
+ // An animation is already running, add it transiently
+ mClearTransientViewsWhenFinished.add(child);
+ return child.inRemovalAnimation();
+ }
+ } else {
+ if (isClickedHeadsUp(child)) {
+ // An animation is already running, add it transiently
+ mClearTransientViewsWhenFinished.add(child);
+ return true;
+ }
}
if (mDebugRemoveAnimation) {
Log.d(TAG, "generateRemove " + key
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index 69453c6..e94258f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -16,6 +16,9 @@
package com.android.systemui.statusbar.notification.stack;
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR;
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
@@ -346,21 +349,19 @@
ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents) {
boolean needsCustomAnimation = false;
for (NotificationStackScrollLayout.AnimationEvent event : animationEvents) {
- final ExpandableView changingView = (ExpandableView) event.mChangingView;
+ final ExpandableView changingView = event.mChangingView;
boolean loggable = false;
boolean isHeadsUp = false;
- boolean isGroupChild = false;
String key = null;
if (changingView instanceof ExpandableNotificationRow && mLogger != null) {
loggable = true;
isHeadsUp = ((ExpandableNotificationRow) changingView).isHeadsUp();
- isGroupChild = changingView.isChildInGroup();
key = ((ExpandableNotificationRow) changingView).getEntry().getKey();
}
if (event.animationType ==
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD) {
- // This item is added, initialize it's properties.
+ // This item is added, initialize its properties.
ExpandableViewState viewState = changingView.getViewState();
if (viewState == null || viewState.gone) {
// The position for this child was never generated, let's continue.
@@ -374,7 +375,11 @@
} else if (event.animationType ==
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE) {
- if (changingView.getVisibility() != View.VISIBLE) {
+ int changingViewVisibility = changingView.getVisibility();
+ if (loggable) {
+ mLogger.processAnimationEventsRemoval(key, changingViewVisibility, isHeadsUp);
+ }
+ if (changingViewVisibility != View.VISIBLE) {
changingView.removeFromTransientContainer();
continue;
}
@@ -410,30 +415,40 @@
translationDirection = Math.max(Math.min(translationDirection, 1.0f),-1.0f);
}
- Runnable postAnimation = changingView::removeFromTransientContainer;
+ Runnable postAnimation;
+ Runnable startAnimation;
if (loggable) {
String finalKey = key;
- if (isHeadsUp) {
- mLogger.logHUNViewDisappearingWithRemoveEvent(key);
- postAnimation = () -> {
- mLogger.disappearAnimationEnded(finalKey);
- changingView.removeFromTransientContainer();
- };
- } else if (isGroupChild) {
- mLogger.groupChildRemovalEventProcessed(key);
- postAnimation = () -> {
- mLogger.groupChildRemovalAnimationEnded(finalKey);
- changingView.removeFromTransientContainer();
- };
- }
+ final boolean finalIsHeadsHp = isHeadsUp;
+ startAnimation = () -> {
+ mLogger.animationStart(finalKey, "ANIMATION_TYPE_REMOVE", finalIsHeadsHp);
+ changingView.setInRemovalAnimation(true);
+ };
+ postAnimation = () -> {
+ mLogger.animationEnd(finalKey, "ANIMATION_TYPE_REMOVE", finalIsHeadsHp);
+ changingView.setInRemovalAnimation(false);
+ changingView.removeFromTransientContainer();
+ };
+ } else {
+ startAnimation = ()-> {
+ changingView.setInRemovalAnimation(true);
+ };
+ postAnimation = () -> {
+ changingView.setInRemovalAnimation(false);
+ changingView.removeFromTransientContainer();
+ };
}
changingView.performRemoveAnimation(ANIMATION_DURATION_APPEAR_DISAPPEAR,
0 /* delay */, translationDirection, false /* isHeadsUpAppear */,
- postAnimation, getGlobalAnimationFinishedListener());
+ startAnimation, postAnimation, getGlobalAnimationFinishedListener());
needsCustomAnimation = true;
} else if (event.animationType ==
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) {
- if (mHostLayout.isFullySwipedOut(changingView)) {
+ boolean isFullySwipedOut = mHostLayout.isFullySwipedOut(changingView);
+ if (loggable) {
+ mLogger.processAnimationEventsRemoveSwipeOut(key, isFullySwipedOut, isHeadsUp);
+ }
+ if (isFullySwipedOut) {
changingView.removeFromTransientContainer();
}
} else if (event.animationType == NotificationStackScrollLayout
@@ -442,7 +457,7 @@
row.prepareExpansionChanged();
} else if (event.animationType == NotificationStackScrollLayout
.AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR) {
- // This item is added, initialize it's properties.
+ // This item is added, initialize its properties.
ExpandableViewState viewState = changingView.getViewState();
mTmpState.copyFrom(viewState);
if (event.headsUpFromBottom) {
@@ -464,22 +479,23 @@
}
mTmpState.applyToView(changingView);
- } else if (event.animationType == NotificationStackScrollLayout
- .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR ||
- event.animationType == NotificationStackScrollLayout
- .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK) {
+ } else if (event.animationType == ANIMATION_TYPE_HEADS_UP_DISAPPEAR
+ || event.animationType == ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK) {
mHeadsUpDisappearChildren.add(changingView);
Runnable endRunnable = null;
if (changingView.getParent() == null) {
- // This notification was actually removed, so we need to add it transiently
+ // This notification was actually removed, so we need to add it
+ // transiently
mHostLayout.addTransientView(changingView, 0);
changingView.setTransientContainer(mHostLayout);
mTmpState.initFrom(changingView);
endRunnable = changingView::removeFromTransientContainer;
}
+
boolean needsAnimation = true;
if (changingView instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) changingView;
+ ExpandableNotificationRow row =
+ (ExpandableNotificationRow) changingView;
if (row.isDismissed()) {
needsAnimation = false;
}
@@ -488,21 +504,43 @@
// We need to add the global animation listener, since once no animations are
// running anymore, the panel will instantly hide itself. We need to wait until
// the animation is fully finished for this though.
- Runnable postAnimation = endRunnable;
+ final Runnable tmpEndRunnable = endRunnable;
+ Runnable postAnimation;
+ Runnable startAnimation;
if (loggable) {
- mLogger.logHUNViewDisappearing(key);
-
- Runnable finalEndRunnable = endRunnable;
String finalKey1 = key;
+ final boolean finalIsHeadsUp = isHeadsUp;
+ final String type =
+ event.animationType == ANIMATION_TYPE_HEADS_UP_DISAPPEAR
+ ? "ANIMATION_TYPE_HEADS_UP_DISAPPEAR"
+ : "ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK";
+ startAnimation = () -> {
+ mLogger.animationStart(finalKey1, type, finalIsHeadsUp);
+ changingView.setInRemovalAnimation(true);
+ };
postAnimation = () -> {
- mLogger.disappearAnimationEnded(finalKey1);
- if (finalEndRunnable != null) finalEndRunnable.run();
+ mLogger.animationEnd(finalKey1, type, finalIsHeadsUp);
+ changingView.setInRemovalAnimation(false);
+ if (tmpEndRunnable != null) {
+ tmpEndRunnable.run();
+ }
+ };
+ } else {
+ postAnimation = () -> {
+ changingView.setInRemovalAnimation(false);
+ if (tmpEndRunnable != null) {
+ tmpEndRunnable.run();
+ }
+ };
+ startAnimation = () -> {
+ changingView.setInRemovalAnimation(true);
};
}
long removeAnimationDelay = changingView.performRemoveAnimation(
ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
0, 0.0f, true /* isHeadsUpAppear */,
- postAnimation, getGlobalAnimationFinishedListener());
+ startAnimation, postAnimation,
+ getGlobalAnimationFinishedListener());
mAnimationProperties.delay += removeAnimationDelay;
} else if (endRunnable != null) {
endRunnable.run();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt
index 0b2c486..d635f89 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt
@@ -5,74 +5,104 @@
import com.android.systemui.log.dagger.NotificationHeadsUpLog
import com.android.systemui.log.dagger.NotificationRenderLog
import com.android.systemui.statusbar.notification.logKey
+import com.android.systemui.util.visibilityString
import javax.inject.Inject
-class StackStateLogger @Inject constructor(
+class StackStateLogger
+@Inject
+constructor(
@NotificationHeadsUpLog private val buffer: LogBuffer,
@NotificationRenderLog private val notificationRenderBuffer: LogBuffer
) {
- fun logHUNViewDisappearing(key: String) {
- buffer.log(TAG, LogLevel.INFO, {
- str1 = logKey(key)
- }, {
- "Heads up view disappearing $str1 "
- })
- }
fun logHUNViewAppearing(key: String) {
- buffer.log(TAG, LogLevel.INFO, {
- str1 = logKey(key)
- }, {
- "Heads up notification view appearing $str1 "
- })
- }
-
- fun logHUNViewDisappearingWithRemoveEvent(key: String) {
- buffer.log(TAG, LogLevel.ERROR, {
- str1 = logKey(key)
- }, {
- "Heads up view disappearing $str1 for ANIMATION_TYPE_REMOVE"
- })
+ buffer.log(
+ TAG,
+ LogLevel.INFO,
+ { str1 = logKey(key) },
+ { "Heads up notification view appearing $str1 " }
+ )
}
fun logHUNViewAppearingWithAddEvent(key: String) {
- buffer.log(TAG, LogLevel.ERROR, {
- str1 = logKey(key)
- }, {
- "Heads up view disappearing $str1 for ANIMATION_TYPE_ADD"
- })
- }
-
- fun disappearAnimationEnded(key: String) {
- buffer.log(TAG, LogLevel.INFO, {
- str1 = logKey(key)
- }, {
- "Heads up notification disappear animation ended $str1 "
- })
+ buffer.log(
+ TAG,
+ LogLevel.ERROR,
+ { str1 = logKey(key) },
+ { "Heads up view disappearing $str1 for ANIMATION_TYPE_ADD" }
+ )
}
fun appearAnimationEnded(key: String) {
- buffer.log(TAG, LogLevel.INFO, {
- str1 = logKey(key)
- }, {
- "Heads up notification appear animation ended $str1 "
- })
+ buffer.log(
+ TAG,
+ LogLevel.INFO,
+ { str1 = logKey(key) },
+ { "Heads up notification appear animation ended $str1 " }
+ )
}
- fun groupChildRemovalEventProcessed(key: String) {
- notificationRenderBuffer.log(TAG, LogLevel.DEBUG, {
- str1 = logKey(key)
- }, {
- "Group Child Notification removal event processed $str1 for ANIMATION_TYPE_REMOVE"
- })
+ fun processAnimationEventsRemoval(key: String, visibility: Int, isHeadsUp: Boolean) {
+ notificationRenderBuffer.log(
+ TAG,
+ LogLevel.INFO,
+ {
+ str1 = logKey(key)
+ int1 = visibility
+ bool1 = isHeadsUp
+ },
+ {
+ "ProcessAnimationEvents ANIMATION_TYPE_REMOVE for: $str1, " +
+ "changingViewVisibility: ${visibilityString(int1)}, isHeadsUp: $bool1"
+ }
+ )
}
- fun groupChildRemovalAnimationEnded(key: String) {
- notificationRenderBuffer.log(TAG, LogLevel.INFO, {
- str1 = logKey(key)
- }, {
- "Group child notification removal animation ended $str1 "
- })
+
+ fun processAnimationEventsRemoveSwipeOut(
+ key: String,
+ isFullySwipedOut: Boolean,
+ isHeadsUp: Boolean
+ ) {
+ notificationRenderBuffer.log(
+ TAG,
+ LogLevel.INFO,
+ {
+ str1 = logKey(key)
+ bool1 = isFullySwipedOut
+ bool2 = isHeadsUp
+ },
+ {
+ "ProcessAnimationEvents ANIMATION_TYPE_REMOVE_SWIPED_OUT for: $str1, " +
+ "isFullySwipedOut: $bool1, isHeadsUp: $bool2"
+ }
+ )
+ }
+
+ fun animationStart(key: String?, animationType: String, isHeadsUp: Boolean) {
+ notificationRenderBuffer.log(
+ TAG,
+ LogLevel.INFO,
+ {
+ str1 = logKey(key)
+ str2 = animationType
+ bool1 = isHeadsUp
+ },
+ { "Animation Start, type: $str2, notif key: $str1, isHeadsUp: $bool1" }
+ )
+ }
+
+ fun animationEnd(key: String, animationType: String, isHeadsUp: Boolean) {
+ notificationRenderBuffer.log(
+ TAG,
+ LogLevel.INFO,
+ {
+ str1 = logKey(key)
+ str2 = animationType
+ bool1 = isHeadsUp
+ },
+ { "Animation End, type: $str2, notif key: $str1, isHeadsUp: $bool1" }
+ )
}
}
-private const val TAG = "StackScroll"
\ No newline at end of file
+private const val TAG = "StackScroll"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index 697d297..3877bb6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -28,15 +28,15 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.NightDisplayListenerModule;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.AutoAddTracker;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.ReduceBrightColorsController;
-import com.android.systemui.qs.SettingObserver;
+import com.android.systemui.qs.UserSettingObserver;
import com.android.systemui.qs.external.CustomTile;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.CastDevice;
import com.android.systemui.statusbar.policy.DataSaverController;
@@ -461,8 +461,8 @@
};
@VisibleForTesting
- protected SettingObserver getSecureSettingForKey(String key) {
- for (SettingObserver s : mAutoAddSettingList) {
+ protected UserSettingObserver getSecureSettingForKey(String key) {
+ for (UserSettingObserver s : mAutoAddSettingList) {
if (Objects.equals(key, s.getKey())) {
return s;
}
@@ -476,7 +476,7 @@
* When the setting changes to a value different from 0, if the tile has not been auto added
* before, it will be added and the listener will be stopped.
*/
- private class AutoAddSetting extends SettingObserver {
+ private class AutoAddSetting extends UserSettingObserver {
private final String mSpec;
AutoAddSetting(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index f380ce5..9fb6c1b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -312,8 +312,8 @@
};
void onStatusBarWindowStateChanged(@WindowVisibleState int state) {
- updateBubblesVisibility();
mStatusBarWindowState = state;
+ updateBubblesVisibility();
}
@Override
@@ -1088,6 +1088,7 @@
* @deprecated use {@link
* WindowRootViewVisibilityInteractor.isLockscreenOrShadeVisible} instead.
*/ @VisibleForTesting
+ @Deprecated
void initShadeVisibilityListener() {
mShadeController.setVisibilityListener(new ShadeController.ShadeVisibilityListener() {
@Override
@@ -1725,7 +1726,8 @@
StatusBarMode mode = mStatusBarModeRepository.getStatusBarMode().getValue();
mBubblesOptional.ifPresent(bubbles -> bubbles.onStatusBarVisibilityChanged(
mode != StatusBarMode.LIGHTS_OUT
- && mode != StatusBarMode.LIGHTS_OUT_TRANSPARENT));
+ && mode != StatusBarMode.LIGHTS_OUT_TRANSPARENT
+ && mStatusBarWindowState != WINDOW_STATE_HIDDEN));
}
void checkBarMode(
@@ -2602,7 +2604,10 @@
mShouldDelayWakeUpAnimation);
updateIsKeyguard();
+ // TODO(b/301913237): can't delay transition if config_displayBlanksAfterDoze=true,
+ // otherwise, the clock will flicker during LOCKSCREEN_TRANSITION_FROM_AOD
mShouldDelayLockscreenTransitionFromAod = mDozeParameters.getAlwaysOn()
+ && !mDozeParameters.getDisplayNeedsBlanking()
&& mFeatureFlags.isEnabled(
Flags.ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD);
if (!mShouldDelayLockscreenTransitionFromAod) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 62b2445..3adf338 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -296,7 +296,6 @@
final Set<KeyguardViewManagerCallback> mCallbacks = new HashSet<>();
private boolean mIsBackAnimationEnabled;
- private final boolean mUdfpsNewTouchDetectionEnabled;
private final UdfpsOverlayInteractor mUdfpsOverlayInteractor;
private final ActivityStarter mActivityStarter;
@@ -398,7 +397,6 @@
mAlternateBouncerInteractor = alternateBouncerInteractor;
mIsBackAnimationEnabled =
featureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM);
- mUdfpsNewTouchDetectionEnabled = featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION);
mUdfpsOverlayInteractor = udfpsOverlayInteractor;
mActivityStarter = activityStarter;
mKeyguardTransitionInteractor = keyguardTransitionInteractor;
@@ -1594,7 +1592,7 @@
final boolean actionDownThenUp = mAlternateBouncerInteractor.getReceivedDownTouch()
&& event.getActionMasked() == MotionEvent.ACTION_UP;
final boolean udfpsOverlayWillForwardEventsOutsideNotificationShade =
- mUdfpsNewTouchDetectionEnabled && mKeyguardUpdateManager.isUdfpsEnrolled();
+ mKeyguardUpdateManager.isUdfpsEnrolled();
final boolean actionOutsideShouldDismissAlternateBouncer =
event.getActionMasked() == MotionEvent.ACTION_OUTSIDE
&& !udfpsOverlayWillForwardEventsOutsideNotificationShade;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java
index 6dc8065..da91d6a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java
@@ -15,6 +15,9 @@
*/
package com.android.systemui.statusbar.phone;
+import com.android.systemui.util.annotations.WeaklyReferencedCallback;
+
+@WeaklyReferencedCallback
public interface StatusBarWindowCallback {
/**
* Invoked when the internal state of NotificationShadeWindowControllerImpl changes.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepository.kt
index 8ff9198..8862c77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepository.kt
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.pipeline.airplane.data.repository
import android.os.Handler
-import android.os.UserHandle
import android.provider.Settings.Global
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
@@ -66,13 +65,7 @@
override val isAirplaneMode: StateFlow<Boolean> =
conflatedCallbackFlow {
val observer =
- object :
- SettingObserver(
- globalSettings,
- bgHandler,
- Global.AIRPLANE_MODE_ON,
- UserHandle.USER_ALL
- ) {
+ object : SettingObserver(globalSettings, bgHandler, Global.AIRPLANE_MODE_ON) {
override fun handleValueChanged(value: Int, observedChange: Boolean) {
trySend(value == 1)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 945cc6b..53b343c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -27,6 +27,7 @@
import android.os.UserManager;
import androidx.annotation.NonNull;
+import androidx.annotation.WorkerThread;
import com.android.internal.annotations.GuardedBy;
import com.android.settingslib.bluetooth.BluetoothCallback;
@@ -36,6 +37,7 @@
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.systemui.bluetooth.BluetoothLogger;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.settings.UserTracker;
@@ -81,6 +83,8 @@
private int mState;
private final BluetoothAdapter mAdapter;
+
+ private final Executor mBackgroundExecutor;
/**
*/
@Inject
@@ -90,6 +94,7 @@
DumpManager dumpManager,
BluetoothLogger logger,
BluetoothRepository bluetoothRepository,
+ @Background Executor executor,
@Main Looper mainLooper,
@Nullable LocalBluetoothManager localBluetoothManager,
@Nullable BluetoothAdapter bluetoothAdapter) {
@@ -98,6 +103,7 @@
mBluetoothRepository = bluetoothRepository;
mLocalBluetoothManager = localBluetoothManager;
mHandler = new H(mainLooper);
+ mBackgroundExecutor = executor;
if (mLocalBluetoothManager != null) {
mLocalBluetoothManager.getEventManager().registerCallback(this);
mLocalBluetoothManager.getProfileManager().addServiceListener(this);
@@ -218,6 +224,7 @@
return mIsActive;
}
+ @WorkerThread
@Override
public void setBluetoothEnabled(boolean enabled) {
if (mLocalBluetoothManager != null) {
@@ -230,6 +237,7 @@
return mLocalBluetoothManager != null;
}
+ @WorkerThread
@Override
public String getConnectedDeviceName() {
synchronized (mConnectedDevices) {
@@ -251,6 +259,7 @@
getDevices(), this::onConnectionStatusFetched);
}
+ // Careful! This may be invoked in the main thread.
private void onConnectionStatusFetched(ConnectionStatusModel status) {
List<CachedBluetoothDevice> newList = status.getConnectedDevices();
int state = status.getMaxConnectionState();
@@ -282,30 +291,33 @@
}
private void updateAudioProfile() {
- boolean audioProfileConnected = false;
- boolean otherProfileConnected = false;
+ // We want this in the background as calls inside `LocalBluetoothProfile` end up being
+ // binder calls
+ mBackgroundExecutor.execute(() -> {
+ boolean audioProfileConnected = false;
+ boolean otherProfileConnected = false;
- for (CachedBluetoothDevice device : getDevices()) {
- for (LocalBluetoothProfile profile : device.getProfiles()) {
- int profileId = profile.getProfileId();
- boolean isConnected = device.isConnectedProfile(profile);
- if (profileId == BluetoothProfile.HEADSET
- || profileId == BluetoothProfile.A2DP
- || profileId == BluetoothProfile.HEARING_AID
- || profileId == BluetoothProfile.LE_AUDIO) {
- audioProfileConnected |= isConnected;
- } else {
- otherProfileConnected |= isConnected;
+ for (CachedBluetoothDevice device : getDevices()) {
+ for (LocalBluetoothProfile profile : device.getProfiles()) {
+ int profileId = profile.getProfileId();
+ boolean isConnected = device.isConnectedProfile(profile);
+ if (profileId == BluetoothProfile.HEADSET
+ || profileId == BluetoothProfile.A2DP
+ || profileId == BluetoothProfile.HEARING_AID
+ || profileId == BluetoothProfile.LE_AUDIO) {
+ audioProfileConnected |= isConnected;
+ } else {
+ otherProfileConnected |= isConnected;
+ }
}
}
- }
- boolean audioProfileOnly = (audioProfileConnected && !otherProfileConnected);
- if (audioProfileOnly != mAudioProfileOnly) {
- mAudioProfileOnly = audioProfileOnly;
- mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
- }
-
+ boolean audioProfileOnly = (audioProfileConnected && !otherProfileConnected);
+ if (audioProfileOnly != mAudioProfileOnly) {
+ mAudioProfileOnly = audioProfileOnly;
+ mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
+ }
+ });
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt
index 8c61ada..8b20283 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt
@@ -62,7 +62,7 @@
}
private val deviceProvisionedUri = globalSettings.getUriFor(Settings.Global.DEVICE_PROVISIONED)
- private val frpActiveUri = secureSettings.getUriFor(Settings.Secure.SECURE_FRP_MODE)
+ private val frpActiveUri = globalSettings.getUriFor(Settings.Global.SECURE_FRP_MODE)
private val userSetupUri = secureSettings.getUriFor(Settings.Secure.USER_SETUP_COMPLETE)
private val deviceProvisioned = AtomicBoolean(false)
@@ -148,7 +148,7 @@
.set(globalSettings.getInt(Settings.Global.DEVICE_PROVISIONED, 0) != 0)
}
if (updateFrp) {
- frpActive.set(globalSettings.getInt(Settings.Secure.SECURE_FRP_MODE, 0) != 0)
+ frpActive.set(globalSettings.getInt(Settings.Global.SECURE_FRP_MODE, 0) != 0)
}
synchronized(lock) {
if (updateUser == ALL_USERS) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/bluetooth/BluetoothRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/bluetooth/BluetoothRepository.kt
index 80f3d76..96717c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/bluetooth/BluetoothRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/bluetooth/BluetoothRepository.kt
@@ -38,7 +38,8 @@
/**
* Fetches the connection statuses for the given [currentDevices] and invokes [callback] once
* those statuses have been fetched. The fetching occurs on a background thread because IPCs may
- * be required to fetch the statuses (see b/271058380).
+ * be required to fetch the statuses (see b/271058380). However, the callback will be invoked in
+ * the main thread.
*/
fun fetchConnectionStatusInBackground(
currentDevices: Collection<CachedBluetoothDevice>,
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
index f404549..5fc435a 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
@@ -23,7 +23,6 @@
import android.os.UserManager
import android.provider.Settings
import androidx.annotation.VisibleForTesting
-import com.android.systemui.res.R
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
@@ -31,6 +30,7 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
import com.android.systemui.user.data.model.SelectedUserModel
import com.android.systemui.user.data.model.SelectionStatus
@@ -132,7 +132,6 @@
Settings.Global.ADD_USERS_WHEN_LOCKED,
Settings.Global.USER_SWITCHER_ENABLED,
),
- userId = UserHandle.USER_SYSTEM,
)
.onStart { emit(Unit) } // Forces an initial update.
.map { getSettings() }
@@ -247,7 +246,7 @@
private suspend fun getSettings(): UserSwitcherSettingsModel {
return withContext(backgroundDispatcher) {
val isSimpleUserSwitcher =
- globalSettings.getIntForUser(
+ globalSettings.getInt(
SETTING_SIMPLE_USER_SWITCHER,
if (
appContext.resources.getBoolean(
@@ -258,18 +257,16 @@
} else {
0
},
- UserHandle.USER_SYSTEM,
) != 0
val isAddUsersFromLockscreen =
- globalSettings.getIntForUser(
+ globalSettings.getInt(
Settings.Global.ADD_USERS_WHEN_LOCKED,
0,
- UserHandle.USER_SYSTEM,
) != 0
val isUserSwitcherEnabled =
- globalSettings.getIntForUser(
+ globalSettings.getInt(
Settings.Global.USER_SWITCHER_ENABLED,
if (
appContext.resources.getBoolean(
@@ -280,7 +277,6 @@
} else {
0
},
- UserHandle.USER_SYSTEM,
) != 0
UserSwitcherSettingsModel(
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index 760fe6a..f5edb7b 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -42,6 +42,7 @@
* list, then list.get(i) could throw an IndexOutOfBoundsException. This method should not be
* used; try using `synchronized` or making a copy of the list instead.
*/
+ @Deprecated
public static <T> void safeForeach(List<T> list, Consumer<T> c) {
for (int i = list.size() - 1; i >= 0; i--) {
T item = list.get(i);
diff --git a/packages/SystemUI/src/com/android/systemui/util/annotations/WeaklyReferencedCallback.java b/packages/SystemUI/src/com/android/systemui/util/annotations/WeaklyReferencedCallback.java
new file mode 100644
index 0000000..855bba6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/annotations/WeaklyReferencedCallback.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.annotations;
+
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+
+/**
+ * Descriptive annotation for clearly tagging callback types that are weakly
+ * referenced during registration.
+ *
+ * This is useful in providing hints to Proguard about certain fields that
+ * should be kept to preserve strong references.
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target({TYPE})
+public @interface WeaklyReferencedCallback {}
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt
index 73e2f97..ffbc10a 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt
@@ -19,6 +19,7 @@
class Utils {
companion object {
fun <A, B, C> toTriple(a: A, bc: Pair<B, C>) = Triple(a, bc.first, bc.second)
+ fun <A, B, C> toTriple(ab: Pair<A, B>, c: C) = Triple(ab.first, ab.second, c)
fun <A, B, C, D> toQuad(a: A, b: B, c: C, d: D) = Quad(a, b, c, d)
fun <A, B, C, D> toQuad(a: A, bcd: Triple<B, C, D>) =
diff --git a/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java b/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java
index 968dcc9..df5162a 100644
--- a/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java
+++ b/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java
@@ -26,6 +26,7 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.util.annotations.WeaklyReferencedCallback;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -64,6 +65,7 @@
* An interface for listening to the connection status.
* @param <T> The wrapper type.
*/
+ @WeaklyReferencedCallback
public interface Callback<T> {
/**
* Invoked when the service has been successfully connected to.
diff --git a/packages/SystemUI/src/com/android/systemui/util/service/Observer.java b/packages/SystemUI/src/com/android/systemui/util/service/Observer.java
index 7687432..425336d 100644
--- a/packages/SystemUI/src/com/android/systemui/util/service/Observer.java
+++ b/packages/SystemUI/src/com/android/systemui/util/service/Observer.java
@@ -16,6 +16,8 @@
package com.android.systemui.util.service;
+import com.android.systemui.util.annotations.WeaklyReferencedCallback;
+
/**
* The {@link Observer} interface specifies an entity which listeners
* can be informed of changes to the source, which will require updating. Note that this deals
@@ -25,6 +27,7 @@
/**
* Callback for receiving updates from the {@link Observer}.
*/
+ @WeaklyReferencedCallback
interface Callback {
/**
* Invoked when the source has changed.
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java
index 85fada2..42389f0 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java
@@ -16,22 +16,23 @@
package com.android.systemui.util.settings;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.net.Uri;
import android.provider.Settings;
-import com.android.systemui.settings.UserTracker;
-
import javax.inject.Inject;
+// use UserHandle.USER_SYSTEM everywhere
+@SuppressLint("StaticSettingsProvider")
class GlobalSettingsImpl implements GlobalSettings {
private final ContentResolver mContentResolver;
- private final UserTracker mUserTracker;
@Inject
- GlobalSettingsImpl(ContentResolver contentResolver, UserTracker userTracker) {
+ GlobalSettingsImpl(ContentResolver contentResolver) {
mContentResolver = contentResolver;
- mUserTracker = userTracker;
}
@Override
@@ -40,43 +41,23 @@
}
@Override
- public UserTracker getUserTracker() {
- return mUserTracker;
- }
-
- @Override
public Uri getUriFor(String name) {
return Settings.Global.getUriFor(name);
}
@Override
- public String getStringForUser(String name, int userHandle) {
- return Settings.Global.getStringForUser(mContentResolver, name,
- getRealUserHandle(userHandle));
+ public String getString(String name) {
+ return Settings.Global.getString(mContentResolver, name);
}
@Override
- public boolean putString(String name, String value, boolean overrideableByRestore) {
- throw new UnsupportedOperationException(
- "This method only exists publicly for Settings.System and Settings.Secure");
+ public boolean putString(String name, String value) {
+ return Settings.Global.putString(mContentResolver, name, value);
}
@Override
- public boolean putStringForUser(String name, String value, int userHandle) {
- return Settings.Global.putStringForUser(mContentResolver, name, value,
- getRealUserHandle(userHandle));
- }
-
- @Override
- public boolean putStringForUser(String name, String value, String tag, boolean makeDefault,
- int userHandle, boolean overrideableByRestore) {
- return Settings.Global.putStringForUser(
- mContentResolver, name, value, tag, makeDefault, getRealUserHandle(userHandle),
- overrideableByRestore);
- }
-
- @Override
- public boolean putString(String name, String value, String tag, boolean makeDefault) {
+ public boolean putString(@NonNull String name, @Nullable String value, @Nullable String tag,
+ boolean makeDefault) {
return Settings.Global.putString(mContentResolver, name, value, tag, makeDefault);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettings.java b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettings.java
index 798033e..6031a4e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettings.java
@@ -22,5 +22,5 @@
* See {@link SettingsProxy} for details.
*/
-public interface SecureSettings extends SettingsProxy {
+public interface SecureSettings extends UserSettingsProxy {
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java
index f995436..6532ce8 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java
@@ -20,6 +20,8 @@
import android.net.Uri;
import android.provider.Settings;
+import androidx.annotation.NonNull;
+
import com.android.systemui.settings.UserTracker;
import javax.inject.Inject;
@@ -75,7 +77,7 @@
}
@Override
- public boolean putString(String name, String value, String tag, boolean makeDefault) {
+ public boolean putString(@NonNull String name, String value, String tag, boolean makeDefault) {
return Settings.Secure.putString(mContentResolver, name, value, tag, makeDefault);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java
index b6846a3..6a9edc1 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java
@@ -18,25 +18,22 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UserIdInt;
import android.content.ContentResolver;
import android.database.ContentObserver;
import android.net.Uri;
-import android.os.UserHandle;
import android.provider.Settings;
-import com.android.systemui.settings.UserTracker;
-
/**
- * Used to interact with Settings.Secure, Settings.Global, and Settings.System.
- *
+ * Used to interact with mainly with Settings.Global, but can also be used for Settings.System
+ * and Settings.Secure. To use the per-user System and Secure settings, {@link UserSettingsProxy}
+ * must be used instead.
+ * <p>
* This interface can be implemented to give instance method (instead of static method) versions
- * of Settings.Secure, Settings.Global, and Settings.System. It can be injected into class
- * constructors and then faked or mocked as needed in tests.
- *
- * You can ask for {@link SecureSettings}, {@link GlobalSettings}, or {@link SystemSettings} to be
- * injected as needed.
- *
+ * of Settings.Global. It can be injected into class constructors and then faked or mocked as needed
+ * in tests.
+ * <p>
+ * You can ask for {@link GlobalSettings} to be injected as needed.
+ * <p>
* This class also provides {@link #registerContentObserver(String, ContentObserver)} methods,
* normally found on {@link ContentResolver} instances, unifying setting related actions in one
* place.
@@ -49,29 +46,6 @@
ContentResolver getContentResolver();
/**
- * Returns that {@link UserTracker} this instance was constructed with.
- */
- UserTracker getUserTracker();
-
- /**
- * Returns the user id for the associated {@link ContentResolver}.
- */
- default int getUserId() {
- return getContentResolver().getUserId();
- }
-
- /**
- * Returns the actual current user handle when querying with the current user. Otherwise,
- * returns the passed in user id.
- */
- default int getRealUserHandle(int userHandle) {
- if (userHandle != UserHandle.USER_CURRENT) {
- return userHandle;
- }
- return getUserTracker().getUserId();
- }
-
- /**
* Construct the content URI for a particular name/value pair,
* useful for monitoring changes with a ContentObserver.
* @param name to look up in the table
@@ -82,7 +56,7 @@
/**
* Convenience wrapper around
* {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver)}.'
- *
+ * <p>
* Implicitly calls {@link #getUriFor(String)} on the passed in name.
*/
default void registerContentObserver(String name, ContentObserver settingsObserver) {
@@ -94,13 +68,13 @@
* {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver)}.'
*/
default void registerContentObserver(Uri uri, ContentObserver settingsObserver) {
- registerContentObserverForUser(uri, settingsObserver, getUserId());
+ registerContentObserver(uri, false, settingsObserver);
}
/**
* Convenience wrapper around
* {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver)}.'
- *
+ * <p>
* Implicitly calls {@link #getUriFor(String)} on the passed in name.
*/
default void registerContentObserver(String name, boolean notifyForDescendants,
@@ -114,53 +88,8 @@
*/
default void registerContentObserver(Uri uri, boolean notifyForDescendants,
ContentObserver settingsObserver) {
- registerContentObserverForUser(uri, notifyForDescendants, settingsObserver, getUserId());
- }
-
- /**
- * Convenience wrapper around
- * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)}
- *
- * Implicitly calls {@link #getUriFor(String)} on the passed in name.
- */
- default void registerContentObserverForUser(
- String name, ContentObserver settingsObserver, int userHandle) {
- registerContentObserverForUser(
- getUriFor(name), settingsObserver, userHandle);
- }
-
- /**
- * Convenience wrapper around
- * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)}
- */
- default void registerContentObserverForUser(
- Uri uri, ContentObserver settingsObserver, int userHandle) {
- registerContentObserverForUser(
- uri, false, settingsObserver, userHandle);
- }
-
- /**
- * Convenience wrapper around
- * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)}
- *
- * Implicitly calls {@link #getUriFor(String)} on the passed in name.
- */
- default void registerContentObserverForUser(
- String name, boolean notifyForDescendants, ContentObserver settingsObserver,
- int userHandle) {
- registerContentObserverForUser(
- getUriFor(name), notifyForDescendants, settingsObserver, userHandle);
- }
-
- /**
- * Convenience wrapper around
- * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)}
- */
- default void registerContentObserverForUser(
- Uri uri, boolean notifyForDescendants, ContentObserver settingsObserver,
- int userHandle) {
getContentResolver().registerContentObserver(
- uri, notifyForDescendants, settingsObserver, getRealUserHandle(userHandle));
+ uri, notifyForDescendants, settingsObserver);
}
/** See {@link ContentResolver#unregisterContentObserver(ContentObserver)}. */
@@ -173,22 +102,7 @@
* @param name to look up in the table
* @return the corresponding value, or null if not present
*/
- default String getString(String name) {
- return getStringForUser(name, getUserId());
- }
-
- /**See {@link #getString(String)}. */
- String getStringForUser(String name, int userHandle);
-
- /**
- * Store a name/value pair into the database. Values written by this method will be
- * overridden if a restore happens in the future.
- *
- * @param name to store
- * @param value to associate with the name
- * @return true if the value was set, false on database errors
- */
- boolean putString(String name, String value, boolean overrideableByRestore);
+ String getString(String name);
/**
* Store a name/value pair into the database.
@@ -196,16 +110,7 @@
* @param value to associate with the name
* @return true if the value was set, false on database errors
*/
- default boolean putString(String name, String value) {
- return putStringForUser(name, value, getUserId());
- }
-
- /** See {@link #putString(String, String)}. */
- boolean putStringForUser(String name, String value, int userHandle);
-
- /** See {@link #putString(String, String)}. */
- boolean putStringForUser(@NonNull String name, @Nullable String value, @Nullable String tag,
- boolean makeDefault, @UserIdInt int userHandle, boolean overrideableByRestore);
+ boolean putString(String name, String value);
/**
* Store a name/value pair into the database.
@@ -262,12 +167,7 @@
* or not a valid integer.
*/
default int getInt(String name, int def) {
- return getIntForUser(name, def, getUserId());
- }
-
- /** See {@link #getInt(String, int)}. */
- default int getIntForUser(String name, int def, int userHandle) {
- String v = getStringForUser(name, userHandle);
+ String v = getString(name);
try {
return v != null ? Integer.parseInt(v) : def;
} catch (NumberFormatException e) {
@@ -292,14 +192,9 @@
*
* @return The setting's current value.
*/
- default int getInt(String name) throws Settings.SettingNotFoundException {
- return getIntForUser(name, getUserId());
- }
-
- /** See {@link #getInt(String)}. */
- default int getIntForUser(String name, int userHandle)
+ default int getInt(String name)
throws Settings.SettingNotFoundException {
- String v = getStringForUser(name, userHandle);
+ String v = getString(name);
try {
return Integer.parseInt(v);
} catch (NumberFormatException e) {
@@ -320,12 +215,7 @@
* @return true if the value was set, false on database errors
*/
default boolean putInt(String name, int value) {
- return putIntForUser(name, value, getUserId());
- }
-
- /** See {@link #putInt(String, int)}. */
- default boolean putIntForUser(String name, int value, int userHandle) {
- return putStringForUser(name, Integer.toString(value), userHandle);
+ return putString(name, Integer.toString(value));
}
/**
@@ -342,12 +232,7 @@
* or not a valid boolean.
*/
default boolean getBool(String name, boolean def) {
- return getBoolForUser(name, def, getUserId());
- }
-
- /** See {@link #getBool(String, boolean)}. */
- default boolean getBoolForUser(String name, boolean def, int userHandle) {
- return getIntForUser(name, def ? 1 : 0, userHandle) != 0;
+ return getInt(name, def ? 1 : 0) != 0;
}
/**
@@ -367,14 +252,9 @@
*
* @return The setting's current value.
*/
- default boolean getBool(String name) throws Settings.SettingNotFoundException {
- return getBoolForUser(name, getUserId());
- }
-
- /** See {@link #getBool(String)}. */
- default boolean getBoolForUser(String name, int userHandle)
+ default boolean getBool(String name)
throws Settings.SettingNotFoundException {
- return getIntForUser(name, userHandle) != 0;
+ return getInt(name) != 0;
}
/**
@@ -390,12 +270,7 @@
* @return true if the value was set, false on database errors
*/
default boolean putBool(String name, boolean value) {
- return putBoolForUser(name, value, getUserId());
- }
-
- /** See {@link #putBool(String, boolean)}. */
- default boolean putBoolForUser(String name, boolean value, int userHandle) {
- return putIntForUser(name, value ? 1 : 0, userHandle);
+ return putInt(name, value ? 1 : 0);
}
/**
@@ -412,12 +287,12 @@
* or not a valid {@code long}.
*/
default long getLong(String name, long def) {
- return getLongForUser(name, def, getUserId());
+ String valString = getString(name);
+ return parseLongOrUseDefault(valString, def);
}
- /** See {@link #getLong(String, long)}. */
- default long getLongForUser(String name, long def, int userHandle) {
- String valString = getStringForUser(name, userHandle);
+ /** Convert a string to a long, or uses a default if the string is malformed or null */
+ static long parseLongOrUseDefault(String valString, long def) {
long value;
try {
value = valString != null ? Long.parseLong(valString) : def;
@@ -443,14 +318,15 @@
* @throws Settings.SettingNotFoundException Thrown if a setting by the given
* name can't be found or the setting value is not an integer.
*/
- default long getLong(String name) throws Settings.SettingNotFoundException {
- return getLongForUser(name, getUserId());
+ default long getLong(String name)
+ throws Settings.SettingNotFoundException {
+ String valString = getString(name);
+ return parseLongOrThrow(name, valString);
}
- /** See {@link #getLong(String)}. */
- default long getLongForUser(String name, int userHandle)
+ /** Convert a string to a long, or throws an exception if the string is malformed or null */
+ static long parseLongOrThrow(String name, String valString)
throws Settings.SettingNotFoundException {
- String valString = getStringForUser(name, userHandle);
try {
return Long.parseLong(valString);
} catch (NumberFormatException e) {
@@ -471,12 +347,7 @@
* @return true if the value was set, false on database errors
*/
default boolean putLong(String name, long value) {
- return putLongForUser(name, value, getUserId());
- }
-
- /** See {@link #putLong(String, long)}. */
- default boolean putLongForUser(String name, long value, int userHandle) {
- return putStringForUser(name, Long.toString(value), userHandle);
+ return putString(name, Long.toString(value));
}
/**
@@ -493,12 +364,12 @@
* or not a valid float.
*/
default float getFloat(String name, float def) {
- return getFloatForUser(name, def, getUserId());
+ String v = getString(name);
+ return parseFloat(v, def);
}
- /** See {@link #getFloat(String)}. */
- default float getFloatForUser(String name, float def, int userHandle) {
- String v = getStringForUser(name, userHandle);
+ /** Convert a string to a float, or uses a default if the string is malformed or null */
+ static float parseFloat(String v, float def) {
try {
return v != null ? Float.parseFloat(v) : def;
} catch (NumberFormatException e) {
@@ -523,14 +394,15 @@
*
* @return The setting's current value.
*/
- default float getFloat(String name) throws Settings.SettingNotFoundException {
- return getFloatForUser(name, getUserId());
+ default float getFloat(String name)
+ throws Settings.SettingNotFoundException {
+ String v = getString(name);
+ return parseFloatOrThrow(name, v);
}
- /** See {@link #getFloat(String, float)}. */
- default float getFloatForUser(String name, int userHandle)
+ /** Convert a string to a float, or throws an exception if the string is malformed or null */
+ static float parseFloatOrThrow(String name, String v)
throws Settings.SettingNotFoundException {
- String v = getStringForUser(name, userHandle);
if (v == null) {
throw new Settings.SettingNotFoundException(name);
}
@@ -554,11 +426,6 @@
* @return true if the value was set, false on database errors
*/
default boolean putFloat(String name, float value) {
- return putFloatForUser(name, value, getUserId());
- }
-
- /** See {@link #putFloat(String, float)} */
- default boolean putFloatForUser(String name, float value, int userHandle) {
- return putStringForUser(name, Float.toString(value), userHandle);
+ return putString(name, Float.toString(value));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxyExt.kt b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxyExt.kt
index 561495e..7484368 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxyExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxyExt.kt
@@ -27,7 +27,7 @@
object SettingsProxyExt {
/** Returns a flow of [Unit] that is invoked each time that content is updated. */
- fun SettingsProxy.observerFlow(
+ fun UserSettingsProxy.observerFlow(
@UserIdInt userId: Int,
vararg names: String,
): Flow<Unit> {
@@ -44,4 +44,22 @@
awaitClose { unregisterContentObserver(observer) }
}
}
+
+ /** Returns a flow of [Unit] that is invoked each time that content is updated. */
+ fun SettingsProxy.observerFlow(
+ vararg names: String,
+ ): Flow<Unit> {
+ return conflatedCallbackFlow {
+ val observer =
+ object : ContentObserver(null) {
+ override fun onChange(selfChange: Boolean) {
+ trySend(Unit)
+ }
+ }
+
+ names.forEach { name -> registerContentObserver(name, observer) }
+
+ awaitClose { unregisterContentObserver(observer) }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettings.java b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettings.java
index d57d749..c67c603 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettings.java
@@ -21,5 +21,5 @@
*
* See {@link SettingsProxy} for details.
*/
-public interface SystemSettings extends SettingsProxy {
+public interface SystemSettings extends UserSettingsProxy {
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java
index fba7ddf..658b299 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java
@@ -20,6 +20,8 @@
import android.net.Uri;
import android.provider.Settings;
+import androidx.annotation.NonNull;
+
import com.android.systemui.settings.UserTracker;
import javax.inject.Inject;
@@ -74,7 +76,7 @@
}
@Override
- public boolean putString(String name, String value, String tag, boolean makeDefault) {
+ public boolean putString(@NonNull String name, String value, String tag, boolean makeDefault) {
throw new UnsupportedOperationException(
"This method only exists publicly for Settings.Secure and Settings.Global");
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.java b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.java
new file mode 100644
index 0000000..0d6c0f5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+import com.android.systemui.settings.UserTracker;
+
+/**
+ * Used to interact with per-user Settings.Secure and Settings.System settings (but not
+ * Settings.Global, since those do not vary per-user)
+ * <p>
+ * This interface can be implemented to give instance method (instead of static method) versions
+ * of Settings.Secure and Settings.System. It can be injected into class constructors and then
+ * faked or mocked as needed in tests.
+ * <p>
+ * You can ask for {@link SecureSettings} or {@link SystemSettings} to be injected as needed.
+ * <p>
+ * This class also provides {@link #registerContentObserver(String, ContentObserver)} methods,
+ * normally found on {@link ContentResolver} instances, unifying setting related actions in one
+ * place.
+ */
+public interface UserSettingsProxy extends SettingsProxy {
+
+ /**
+ * Returns that {@link UserTracker} this instance was constructed with.
+ */
+ UserTracker getUserTracker();
+
+ /**
+ * Returns the user id for the associated {@link ContentResolver}.
+ */
+ default int getUserId() {
+ return getContentResolver().getUserId();
+ }
+
+ /**
+ * Returns the actual current user handle when querying with the current user. Otherwise,
+ * returns the passed in user id.
+ */
+ default int getRealUserHandle(int userHandle) {
+ if (userHandle != UserHandle.USER_CURRENT) {
+ return userHandle;
+ }
+ return getUserTracker().getUserId();
+ }
+
+ @Override
+ default void registerContentObserver(Uri uri, ContentObserver settingsObserver) {
+ registerContentObserverForUser(uri, settingsObserver, getUserId());
+ }
+
+ /**
+ * Convenience wrapper around
+ * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver)}.'
+ */
+ @Override
+ default void registerContentObserver(Uri uri, boolean notifyForDescendants,
+ ContentObserver settingsObserver) {
+ registerContentObserverForUser(uri, notifyForDescendants, settingsObserver, getUserId());
+ }
+
+ /**
+ * Convenience wrapper around
+ * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)}
+ *
+ * Implicitly calls {@link #getUriFor(String)} on the passed in name.
+ */
+ default void registerContentObserverForUser(
+ String name, ContentObserver settingsObserver, int userHandle) {
+ registerContentObserverForUser(
+ getUriFor(name), settingsObserver, userHandle);
+ }
+
+ /**
+ * Convenience wrapper around
+ * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)}
+ */
+ default void registerContentObserverForUser(
+ Uri uri, ContentObserver settingsObserver, int userHandle) {
+ registerContentObserverForUser(
+ uri, false, settingsObserver, userHandle);
+ }
+
+ /**
+ * Convenience wrapper around
+ * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)}
+ *
+ * Implicitly calls {@link #getUriFor(String)} on the passed in name.
+ */
+ default void registerContentObserverForUser(
+ String name, boolean notifyForDescendants, ContentObserver settingsObserver,
+ int userHandle) {
+ registerContentObserverForUser(
+ getUriFor(name), notifyForDescendants, settingsObserver, userHandle);
+ }
+
+ /**
+ * Convenience wrapper around
+ * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)}
+ */
+ default void registerContentObserverForUser(
+ Uri uri, boolean notifyForDescendants, ContentObserver settingsObserver,
+ int userHandle) {
+ getContentResolver().registerContentObserver(
+ uri, notifyForDescendants, settingsObserver, getRealUserHandle(userHandle));
+ }
+
+ /**
+ * Look up a name in the database.
+ * @param name to look up in the table
+ * @return the corresponding value, or null if not present
+ */
+ @Override
+ default String getString(String name) {
+ return getStringForUser(name, getUserId());
+ }
+
+ /**See {@link #getString(String)}. */
+ String getStringForUser(String name, int userHandle);
+
+ /**
+ * Store a name/value pair into the database. Values written by this method will be
+ * overridden if a restore happens in the future.
+ *
+ * @param name to store
+ * @param value to associate with the name
+ * @return true if the value was set, false on database errors
+ */
+ boolean putString(String name, String value, boolean overrideableByRestore);
+
+ @Override
+ default boolean putString(String name, String value) {
+ return putStringForUser(name, value, getUserId());
+ }
+
+ /** See {@link #putString(String, String)}. */
+ boolean putStringForUser(String name, String value, int userHandle);
+
+ /** See {@link #putString(String, String)}. */
+ boolean putStringForUser(@NonNull String name, @Nullable String value, @Nullable String tag,
+ boolean makeDefault, @UserIdInt int userHandle, boolean overrideableByRestore);
+
+ @Override
+ default int getInt(String name, int def) {
+ return getIntForUser(name, def, getUserId());
+ }
+
+ /** See {@link #getInt(String, int)}. */
+ default int getIntForUser(String name, int def, int userHandle) {
+ String v = getStringForUser(name, userHandle);
+ try {
+ return v != null ? Integer.parseInt(v) : def;
+ } catch (NumberFormatException e) {
+ return def;
+ }
+ }
+
+ @Override
+ default int getInt(String name) throws Settings.SettingNotFoundException {
+ return getIntForUser(name, getUserId());
+ }
+
+ /** See {@link #getInt(String)}. */
+ default int getIntForUser(String name, int userHandle)
+ throws Settings.SettingNotFoundException {
+ String v = getStringForUser(name, userHandle);
+ try {
+ return Integer.parseInt(v);
+ } catch (NumberFormatException e) {
+ throw new Settings.SettingNotFoundException(name);
+ }
+ }
+
+ @Override
+ default boolean putInt(String name, int value) {
+ return putIntForUser(name, value, getUserId());
+ }
+
+ /** See {@link #putInt(String, int)}. */
+ default boolean putIntForUser(String name, int value, int userHandle) {
+ return putStringForUser(name, Integer.toString(value), userHandle);
+ }
+
+ @Override
+ default boolean getBool(String name, boolean def) {
+ return getBoolForUser(name, def, getUserId());
+ }
+
+ /** See {@link #getBool(String, boolean)}. */
+ default boolean getBoolForUser(String name, boolean def, int userHandle) {
+ return getIntForUser(name, def ? 1 : 0, userHandle) != 0;
+ }
+
+ @Override
+ default boolean getBool(String name) throws Settings.SettingNotFoundException {
+ return getBoolForUser(name, getUserId());
+ }
+
+ /** See {@link #getBool(String)}. */
+ default boolean getBoolForUser(String name, int userHandle)
+ throws Settings.SettingNotFoundException {
+ return getIntForUser(name, userHandle) != 0;
+ }
+
+ @Override
+ default boolean putBool(String name, boolean value) {
+ return putBoolForUser(name, value, getUserId());
+ }
+
+ /** See {@link #putBool(String, boolean)}. */
+ default boolean putBoolForUser(String name, boolean value, int userHandle) {
+ return putIntForUser(name, value ? 1 : 0, userHandle);
+ }
+
+ /** See {@link #getLong(String, long)}. */
+ default long getLongForUser(String name, long def, int userHandle) {
+ String valString = getStringForUser(name, userHandle);
+ return SettingsProxy.parseLongOrUseDefault(valString, def);
+ }
+
+ /** See {@link #getLong(String)}. */
+ default long getLongForUser(String name, int userHandle)
+ throws Settings.SettingNotFoundException {
+ String valString = getStringForUser(name, userHandle);
+ return SettingsProxy.parseLongOrThrow(name, valString);
+ }
+
+ /** See {@link #putLong(String, long)}. */
+ default boolean putLongForUser(String name, long value, int userHandle) {
+ return putStringForUser(name, Long.toString(value), userHandle);
+ }
+
+ /** See {@link #getFloat(String)}. */
+ default float getFloatForUser(String name, float def, int userHandle) {
+ String v = getStringForUser(name, userHandle);
+ return SettingsProxy.parseFloat(v, def);
+ }
+
+ /** See {@link #getFloat(String, float)}. */
+ default float getFloatForUser(String name, int userHandle)
+ throws Settings.SettingNotFoundException {
+ String v = getStringForUser(name, userHandle);
+ return SettingsProxy.parseFloatOrThrow(name, v);
+ }
+
+ /** See {@link #putFloat(String, float)} */
+ default boolean putFloatForUser(String name, float value, int userHandle) {
+ return putStringForUser(name, Float.toString(value), userHandle);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index 62f9a9d..20d4eb9 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -810,7 +810,6 @@
SceneKey.Bouncer,
flowOf(.5f),
false,
- isUserInputOngoing = flowOf(false),
)
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer, null), "reason")
@@ -826,8 +825,7 @@
SceneKey.Bouncer,
SceneKey.Gone,
flowOf(.5f),
- false,
- isUserInputOngoing = flowOf(false),
+ false
)
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason")
@@ -844,8 +842,7 @@
SceneKey.Gone,
SceneKey.Bouncer,
flowOf(.5f),
- false,
- isUserInputOngoing = flowOf(false),
+ false
)
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer, null), "reason")
@@ -863,8 +860,7 @@
SceneKey.Bouncer,
SceneKey.Gone,
flowOf(.5f),
- false,
- isUserInputOngoing = flowOf(false),
+ false
)
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason")
@@ -880,7 +876,6 @@
SceneKey.Lockscreen,
flowOf(.5f),
false,
- isUserInputOngoing = flowOf(false),
)
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen, null), "reason")
@@ -898,7 +893,6 @@
SceneKey.Gone,
flowOf(.5f),
false,
- isUserInputOngoing = flowOf(false),
)
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index e9e9624..ebe13fe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -29,7 +29,6 @@
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.Surface
-import android.view.Surface.ROTATION_0
import android.view.Surface.Rotation
import android.view.View
import android.view.WindowManager
@@ -45,7 +44,6 @@
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.LockscreenShadeTransitionController
@@ -141,7 +139,6 @@
) {
controllerOverlay = UdfpsControllerOverlay(
context,
- fingerprintManager,
inflater,
windowManager,
accessibilityManager,
@@ -155,7 +152,6 @@
keyguardStateController,
unlockedScreenOffAnimationController,
udfpsDisplayMode,
- secureSettings,
REQUEST_ID,
reason,
controllerCallback,
@@ -165,7 +161,6 @@
primaryBouncerInteractor,
alternateBouncerInteractor,
isDebuggable,
- udfpsUtils,
udfpsKeyguardAccessibilityDelegate,
udfpsKeyguardViewModels,
)
@@ -212,8 +207,8 @@
val lp = layoutParamsCaptor.value
assertThat(lp.x).isEqualTo(0)
assertThat(lp.y).isEqualTo(0)
- assertThat(lp.width).isEqualTo(SENSOR_WIDTH)
- assertThat(lp.height).isEqualTo(SENSOR_HEIGHT)
+ assertThat(lp.width).isEqualTo(DISPLAY_WIDTH)
+ assertThat(lp.height).isEqualTo(DISPLAY_HEIGHT)
}
}
@@ -230,8 +225,8 @@
val lp = layoutParamsCaptor.value
assertThat(lp.x).isEqualTo(0)
assertThat(lp.y).isEqualTo(0)
- assertThat(lp.width).isEqualTo(SENSOR_WIDTH)
- assertThat(lp.height).isEqualTo(SENSOR_HEIGHT)
+ assertThat(lp.width).isEqualTo(DISPLAY_WIDTH)
+ assertThat(lp.height).isEqualTo(DISPLAY_HEIGHT)
}
}
@@ -247,9 +242,9 @@
// Sensor should be in the bottom left corner in ROTATION_90.
val lp = layoutParamsCaptor.value
assertThat(lp.x).isEqualTo(0)
- assertThat(lp.y).isEqualTo(DISPLAY_WIDTH - SENSOR_WIDTH)
- assertThat(lp.width).isEqualTo(SENSOR_HEIGHT)
- assertThat(lp.height).isEqualTo(SENSOR_WIDTH)
+ assertThat(lp.y).isEqualTo(0)
+ assertThat(lp.width).isEqualTo(DISPLAY_HEIGHT)
+ assertThat(lp.height).isEqualTo(DISPLAY_WIDTH)
}
}
@@ -264,10 +259,10 @@
// Sensor should be in the top right corner in ROTATION_270.
val lp = layoutParamsCaptor.value
- assertThat(lp.x).isEqualTo(DISPLAY_HEIGHT - SENSOR_HEIGHT)
+ assertThat(lp.x).isEqualTo(0)
assertThat(lp.y).isEqualTo(0)
- assertThat(lp.width).isEqualTo(SENSOR_HEIGHT)
- assertThat(lp.height).isEqualTo(SENSOR_WIDTH)
+ assertThat(lp.width).isEqualTo(DISPLAY_HEIGHT)
+ assertThat(lp.height).isEqualTo(DISPLAY_WIDTH)
}
}
@@ -345,11 +340,10 @@
}
@Test
- fun smallOverlayOnEnrollmentWithA11y() = withRotation(ROTATION_0) {
+ fun smallOverlayOnEnrollmentWithA11y() = withRotation(Surface.ROTATION_0) {
withReason(REASON_ENROLL_ENROLLING) {
// When a11y enabled during enrollment
whenever(accessibilityManager.isTouchExplorationEnabled).thenReturn(true)
- whenever(featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true)
controllerOverlay.show(udfpsController, overlayParams)
verify(windowManager).addView(
@@ -363,22 +357,4 @@
assertThat(lp.height).isEqualTo(overlayParams.sensorBounds.height())
}
}
-
- @Test
- fun fullScreenOverlayWithNewTouchDetectionEnabled() = withRotation(ROTATION_0) {
- withReason(REASON_AUTH_KEYGUARD) {
- whenever(featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true)
-
- controllerOverlay.show(udfpsController, overlayParams)
- verify(windowManager).addView(
- eq(controllerOverlay.overlayView),
- layoutParamsCaptor.capture()
- )
-
- // Layout params should use natural display width and height
- val lp = layoutParamsCaptor.value
- assertThat(lp.width).isEqualTo(overlayParams.naturalDisplayWidth)
- assertThat(lp.height).isEqualTo(overlayParams.naturalDisplayHeight)
- }
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index a36f4e9..dcb5398 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -88,7 +88,6 @@
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels;
@@ -105,7 +104,6 @@
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecution;
import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.util.time.SystemClock;
@@ -122,7 +120,6 @@
import java.util.ArrayList;
import java.util.List;
-import java.util.Optional;
import javax.inject.Provider;
@@ -206,8 +203,6 @@
@Mock
private ActivityLaunchAnimator mActivityLaunchAnimator;
@Mock
- private AlternateUdfpsTouchProvider mAlternateTouchProvider;
- @Mock
private PrimaryBouncerInteractor mPrimaryBouncerInteractor;
@Mock
private SinglePointerTouchProcessor mSinglePointerTouchProcessor;
@@ -216,8 +211,6 @@
@Mock
private AlternateBouncerInteractor mAlternateBouncerInteractor;
@Mock
- private SecureSettings mSecureSettings;
- @Mock
private UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate;
@Mock
private Provider<UdfpsKeyguardViewModels> mUdfpsKeyguardViewModels;
@@ -239,7 +232,6 @@
private ScreenLifecycle.Observer mScreenObserver;
private FingerprintSensorPropertiesInternal mOpticalProps;
private FingerprintSensorPropertiesInternal mUltrasonicProps;
- private UdfpsUtils mUdfpsUtils;
@Mock
private InputManager mInputManager;
@Mock
@@ -250,8 +242,6 @@
mContext.getOrCreateTestableResources()
.addOverride(com.android.internal.R.bool.config_ignoreUdfpsVote, false);
- mUdfpsUtils = new UdfpsUtils();
-
when(mLayoutInflater.inflate(R.layout.udfps_view, null, false))
.thenReturn(mUdfpsView);
when(mLayoutInflater.inflate(R.layout.udfps_keyguard_view_legacy, null))
@@ -292,24 +282,13 @@
// Create a fake background executor.
mBiometricExecutor = new FakeExecutor(new FakeSystemClock());
- initUdfpsController(true /* hasAlternateTouchProvider */);
+ initUdfpsController(mOpticalProps);
}
-
- private void initUdfpsController(boolean hasAlternateTouchProvider) {
- initUdfpsController(mOpticalProps, hasAlternateTouchProvider);
- }
-
- private void initUdfpsController(FingerprintSensorPropertiesInternal sensorProps,
- boolean hasAlternateTouchProvider) {
+ private void initUdfpsController(FingerprintSensorPropertiesInternal sensorProps) {
reset(mFingerprintManager);
reset(mScreenLifecycle);
- final Optional<Provider<AlternateUdfpsTouchProvider>> alternateTouchProvider =
- hasAlternateTouchProvider ? Optional.of(
- (Provider<AlternateUdfpsTouchProvider>) () -> mAlternateTouchProvider)
- : Optional.empty();
-
mUdfpsController = new UdfpsController(
mContext,
new FakeExecution(),
@@ -339,15 +318,12 @@
mSystemUIDialogManager,
mLatencyTracker,
mActivityLaunchAnimator,
- alternateTouchProvider,
mBiometricExecutor,
mPrimaryBouncerInteractor,
mSinglePointerTouchProcessor,
mSessionTracker,
mAlternateBouncerInteractor,
- mSecureSettings,
mInputManager,
- mUdfpsUtils,
mock(KeyguardFaceAuthInteractor.class),
mUdfpsKeyguardAccessibilityDelegate,
mUdfpsKeyguardViewModels
@@ -374,17 +350,15 @@
public void onActionDownTouch_whenCanDismissLockScreen_entersDevice() throws RemoteException {
// GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController
when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController);
- // GIVEN that the overlay is showing
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
+ final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
+ givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
// WHEN ACTION_DOWN is received
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
- MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ touchProcessorResult.first);
+ MotionEvent downEvent = obtainMotionEvent(ACTION_DOWN, 0, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
mBiometricExecutor.runAllReady();
downEvent.recycle();
@@ -408,16 +382,14 @@
throws RemoteException {
// GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController
when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController);
- // GIVEN that the overlay is showing
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
+ final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
+ givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
// WHEN ACTION_MOVE is received
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ touchProcessorResult.first);
MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
if (stale) {
mOverlayController.hideUdfpsOverlay(mOpticalProps.sensorId);
@@ -436,22 +408,22 @@
public void onMultipleTouch_whenCanDismissLockScreen_entersDeviceOnce() throws RemoteException {
// GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController
when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController);
- // GIVEN that the overlay is showing
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
+ final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
+ givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UNCHANGED, false);
- // WHEN multiple touches are received
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+ // GIVEN that the overlay is showing
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ touchProcessorResult.first);
MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
mBiometricExecutor.runAllReady();
downEvent.recycle();
+
MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ touchProcessorResult.second);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
mBiometricExecutor.runAllReady();
moveEvent.recycle();
@@ -593,22 +565,17 @@
private static class TestParams {
public final FingerprintSensorPropertiesInternal sensorProps;
- public final boolean hasAlternateTouchProvider;
- TestParams(FingerprintSensorPropertiesInternal sensorProps,
- boolean hasAlternateTouchProvider) {
+ TestParams(FingerprintSensorPropertiesInternal sensorProps) {
this.sensorProps = sensorProps;
- this.hasAlternateTouchProvider = hasAlternateTouchProvider;
}
}
private void runWithAllParams(ThrowingConsumer<TestParams> testParamsConsumer) {
for (FingerprintSensorPropertiesInternal sensorProps : List.of(mOpticalProps,
mUltrasonicProps)) {
- for (boolean hasAlternateTouchProvider : new boolean[]{false, true}) {
- initUdfpsController(sensorProps, hasAlternateTouchProvider);
- testParamsConsumer.accept(new TestParams(sensorProps, hasAlternateTouchProvider));
- }
+ initUdfpsController(sensorProps);
+ testParamsConsumer.accept(new TestParams(sensorProps));
}
}
@@ -621,23 +588,33 @@
private void onTouch_propagatesTouchInNativeOrientationAndResolutionParameterized(
TestParams testParams) throws RemoteException {
reset(mUdfpsView);
+ when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl);
final Rect sensorBounds = new Rect(1000, 1900, 1080, 1920); // Bottom right corner.
+ final int pointerId = 0;
final int displayWidth = 1080;
final int displayHeight = 1920;
- final float scaleFactor = 0.75f; // This means the native resolution is 1440x2560.
+ final float scaleFactor = 1f; // This means the native resolution is 1440x2560.
final float touchMinor = 10f;
final float touchMajor = 20f;
+ final float orientation = 30f;
// Expecting a touch at the very bottom right corner in native orientation and resolution.
- final int expectedX = (int) (displayWidth / scaleFactor);
- final int expectedY = (int) (displayHeight / scaleFactor);
+ final float expectedX = displayWidth / scaleFactor;
+ final float expectedY = displayHeight / scaleFactor;
final float expectedMinor = touchMinor / scaleFactor;
final float expectedMajor = touchMajor / scaleFactor;
// Configure UdfpsView to accept the ACTION_DOWN event
when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
+
+ // GIVEN a valid touch on sensor
+ NormalizedTouchData touchData = new NormalizedTouchData(pointerId, displayWidth,
+ displayHeight, touchMinor, touchMajor, orientation, 0L, 0L);
+ TouchProcessorResult processorDownResult = new TouchProcessorResult.ProcessedTouch(
+ InteractionEvent.DOWN, 1, touchData);
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ processorDownResult);
// Show the overlay.
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
@@ -654,21 +631,12 @@
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
mBiometricExecutor.runAllReady();
event.recycle();
- event = obtainMotionEvent(ACTION_MOVE, displayWidth, displayHeight, touchMinor, touchMajor);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
- mBiometricExecutor.runAllReady();
- event.recycle();
- if (testParams.hasAlternateTouchProvider) {
- verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
- eq(expectedY), eq(expectedMinor), eq(expectedMajor));
- } else {
- verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
- eq(testParams.sensorProps.sensorId), eq(expectedX), eq(expectedY),
- eq(expectedMinor), eq(expectedMajor));
- }
+ verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
+ eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY),
+ eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(),
+ anyBoolean());
// Test ROTATION_90
- reset(mAlternateTouchProvider);
reset(mFingerprintManager);
mUdfpsController.updateOverlayParams(testParams.sensorProps,
new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
@@ -677,21 +645,12 @@
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
mBiometricExecutor.runAllReady();
event.recycle();
- event = obtainMotionEvent(ACTION_MOVE, displayHeight, 0, touchMinor, touchMajor);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
- mBiometricExecutor.runAllReady();
- event.recycle();
- if (testParams.hasAlternateTouchProvider) {
- verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
- eq(expectedY), eq(expectedMinor), eq(expectedMajor));
- } else {
- verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
- eq(testParams.sensorProps.sensorId), eq(expectedX), eq(expectedY),
- eq(expectedMinor), eq(expectedMajor));
- }
+ verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
+ eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY),
+ eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(),
+ anyBoolean());
// Test ROTATION_270
- reset(mAlternateTouchProvider);
reset(mFingerprintManager);
mUdfpsController.updateOverlayParams(testParams.sensorProps,
new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
@@ -700,21 +659,12 @@
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
mBiometricExecutor.runAllReady();
event.recycle();
- event = obtainMotionEvent(ACTION_MOVE, 0, displayWidth, touchMinor, touchMajor);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
- mBiometricExecutor.runAllReady();
- event.recycle();
- if (testParams.hasAlternateTouchProvider) {
- verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
- eq(expectedY), eq(expectedMinor), eq(expectedMajor));
- } else {
- verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
- eq(testParams.sensorProps.sensorId), eq(expectedX), eq(expectedY),
- eq(expectedMinor), eq(expectedMajor));
- }
+ verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
+ eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY),
+ eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(),
+ anyBoolean());
// Test ROTATION_180
- reset(mAlternateTouchProvider);
reset(mFingerprintManager);
mUdfpsController.updateOverlayParams(testParams.sensorProps,
new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
@@ -724,18 +674,10 @@
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
mBiometricExecutor.runAllReady();
event.recycle();
- event = obtainMotionEvent(ACTION_MOVE, displayWidth, displayHeight, touchMinor, touchMajor);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
- mBiometricExecutor.runAllReady();
- event.recycle();
- if (testParams.hasAlternateTouchProvider) {
- verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
- eq(expectedY), eq(expectedMinor), eq(expectedMajor));
- } else {
- verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
- eq(testParams.sensorProps.sensorId), eq(expectedX), eq(expectedY),
- eq(expectedMinor), eq(expectedMajor));
- }
+ verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
+ eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY),
+ eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(),
+ anyBoolean());
}
@Test
@@ -744,46 +686,36 @@
}
private void fingerDownParameterized(TestParams testParams) throws RemoteException {
- reset(mUdfpsView, mAlternateTouchProvider, mFingerprintManager, mLatencyTracker,
+ reset(mUdfpsView, mFingerprintManager, mLatencyTracker,
mKeyguardUpdateMonitor);
+ when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl);
// Configure UdfpsView to accept the ACTION_DOWN event
when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
- // GIVEN that the overlay is showing
+ final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
+ 0L);
+ final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch(
+ InteractionEvent.DOWN, 1 /* pointerId */, touchData);
+
+ initUdfpsController(testParams.sensorProps);
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
// WHEN ACTION_DOWN is received
+ verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ processorResultDown);
MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
mBiometricExecutor.runAllReady();
downEvent.recycle();
- MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
- mBiometricExecutor.runAllReady();
- moveEvent.recycle();
-
- mFgExecutor.runAllReady();
-
// THEN the touch provider is notified about onPointerDown.
- if (testParams.hasAlternateTouchProvider) {
- verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(0), eq(0), eq(0f),
- eq(0f));
- verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(),
- anyInt(), anyFloat(), anyFloat());
- verify(mKeyguardUpdateMonitor).onUdfpsPointerDown(eq((int) TEST_REQUEST_ID));
- } else {
- verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
- eq(testParams.sensorProps.sensorId), eq(0), eq(0), eq(0f), eq(0f));
- verify(mAlternateTouchProvider, never()).onPointerDown(anyInt(), anyInt(), anyInt(),
- anyFloat(), anyFloat());
- }
+ verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(),
+ anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
// AND display configuration begins
if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
@@ -800,33 +732,20 @@
// AND onDisplayConfigured notifies FingerprintManager about onUiReady
mOnDisplayConfiguredCaptor.getValue().run();
mBiometricExecutor.runAllReady();
- if (testParams.hasAlternateTouchProvider) {
- InOrder inOrder = inOrder(mAlternateTouchProvider, mLatencyTracker);
- inOrder.verify(mAlternateTouchProvider).onUiReady();
- inOrder.verify(mLatencyTracker).onActionEnd(
- eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
- verify(mFingerprintManager, never()).onUdfpsUiEvent(
- eq(FingerprintManager.UDFPS_UI_READY), anyLong(), anyInt());
- } else {
- InOrder inOrder = inOrder(mFingerprintManager, mLatencyTracker);
- inOrder.verify(mFingerprintManager).onUdfpsUiEvent(
- eq(FingerprintManager.UDFPS_UI_READY), eq(TEST_REQUEST_ID),
- eq(testParams.sensorProps.sensorId));
- inOrder.verify(mLatencyTracker).onActionEnd(
- eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
- verify(mAlternateTouchProvider, never()).onUiReady();
- }
+ InOrder inOrder = inOrder(mFingerprintManager, mLatencyTracker);
+ inOrder.verify(mFingerprintManager).onUdfpsUiEvent(
+ eq(FingerprintManager.UDFPS_UI_READY), eq(TEST_REQUEST_ID),
+ eq(testParams.sensorProps.sensorId));
+ inOrder.verify(mLatencyTracker).onActionEnd(
+ eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
} else {
verify(mFingerprintManager, never()).onUdfpsUiEvent(
eq(FingerprintManager.UDFPS_UI_READY), anyLong(), anyInt());
- verify(mAlternateTouchProvider, never()).onUiReady();
verify(mLatencyTracker, never()).onActionEnd(
eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
}
}
-
-
@Test
public void aodInterrupt() {
runWithAllParams(this::aodInterruptParameterized);
@@ -834,8 +753,9 @@
private void aodInterruptParameterized(TestParams testParams) throws RemoteException {
mUdfpsController.cancelAodSendFingerUpAction();
- reset(mUdfpsView, mAlternateTouchProvider, mFingerprintManager, mKeyguardUpdateMonitor);
+ reset(mUdfpsView, mFingerprintManager, mKeyguardUpdateMonitor);
when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
+ when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl);
// GIVEN that the overlay is showing and screen is on and fp is running
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
@@ -855,19 +775,8 @@
}
mBiometricExecutor.runAllReady();
- if (testParams.hasAlternateTouchProvider) {
- verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(0), eq(0),
- eq(3f) /* minor */, eq(2f) /* major */);
- verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(),
- anyInt(), anyFloat(), anyFloat());
- verify(mKeyguardUpdateMonitor).onUdfpsPointerDown(eq((int) TEST_REQUEST_ID));
- } else {
- verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
- eq(testParams.sensorProps.sensorId), eq(0), eq(0), eq(3f) /* minor */,
- eq(2f) /* major */);
- verify(mAlternateTouchProvider, never()).onPointerDown(anyLong(), anyInt(), anyInt(),
- anyFloat(), anyFloat());
- }
+ verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(),
+ anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
}
@Test
@@ -907,10 +816,12 @@
private void onFingerUp_displayConfigurationParameterized(TestParams testParams)
throws RemoteException {
reset(mUdfpsView);
+ when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl);
+
+ final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
+ givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
// GIVEN AOD interrupt
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOn();
mFgExecutor.runAllReady();
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
@@ -918,7 +829,8 @@
when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
// WHEN up-action received
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ touchProcessorResult.second);
MotionEvent upEvent = MotionEvent.obtain(0, 0, ACTION_UP, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent);
mBiometricExecutor.runAllReady();
@@ -931,7 +843,8 @@
when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
// WHEN up-action received
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ touchProcessorResult.second);
MotionEvent upEvent = MotionEvent.obtain(0, 0, ACTION_UP, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent);
mBiometricExecutor.runAllReady();
@@ -1015,16 +928,19 @@
private void aodInterruptCancelTimeoutActionOnFingerUpParameterized(TestParams testParams)
throws RemoteException {
reset(mUdfpsView);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
+ when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl);
// GIVEN AOD interrupt
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOn();
mFgExecutor.runAllReady();
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
mFgExecutor.runAllReady();
+ final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
+ givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+
if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
// Configure UdfpsView to accept the ACTION_UP event
when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
@@ -1033,7 +949,8 @@
}
// WHEN ACTION_UP is received
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ touchProcessorResult.second);
MotionEvent upEvent = MotionEvent.obtain(0, 0, ACTION_UP, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent);
mBiometricExecutor.runAllReady();
@@ -1043,16 +960,13 @@
when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
// WHEN ACTION_DOWN is received
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ touchProcessorResult.first);
MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
mBiometricExecutor.runAllReady();
downEvent.recycle();
- // WHEN ACTION_MOVE is received
- MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
- mBiometricExecutor.runAllReady();
- moveEvent.recycle();
mFgExecutor.runAllReady();
if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
@@ -1122,24 +1036,16 @@
@Test
public void playHapticOnTouchUdfpsArea_a11yTouchExplorationEnabled() throws RemoteException {
- // Configure UdfpsView to accept the ACTION_DOWN event
- when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
-
- // GIVEN that the overlay is showing and a11y touch exploration enabled
- when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true);
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
+ final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
+ givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, true);
// WHEN ACTION_HOVER is received
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ touchProcessorResult.first);
verify(mUdfpsView).setOnHoverListener(mHoverListenerCaptor.capture());
MotionEvent enterEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0, 0, 0);
mHoverListenerCaptor.getValue().onHover(mUdfpsView, enterEvent);
enterEvent.recycle();
- MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_MOVE, 0, 0, 0);
- mHoverListenerCaptor.getValue().onHover(mUdfpsView, moveEvent);
- moveEvent.recycle();
// THEN tick haptic is played
verify(mVibrator).vibrate(
@@ -1159,24 +1065,16 @@
public void playHapticOnTouchUdfpsArea_a11yTouchExplorationEnabled_oneWayHapticsEnabled()
throws RemoteException {
when(mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)).thenReturn(true);
- // Configure UdfpsView to accept the ACTION_DOWN event
- when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
- // GIVEN that the overlay is showing and a11y touch exploration enabled
- when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true);
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
+ final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
+ givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, true);
// WHEN ACTION_HOVER is received
- verify(mUdfpsView).setOnHoverListener(mHoverListenerCaptor.capture());
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ touchProcessorResult.first);
MotionEvent enterEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0, 0, 0);
mHoverListenerCaptor.getValue().onHover(mUdfpsView, enterEvent);
enterEvent.recycle();
- MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_MOVE, 0, 0, 0);
- mHoverListenerCaptor.getValue().onHover(mUdfpsView, moveEvent);
- moveEvent.recycle();
// THEN context click haptic is played
verify(mVibrator).performHapticFeedback(
@@ -1187,26 +1085,16 @@
@Test
public void noHapticOnTouchUdfpsArea_a11yTouchExplorationDisabled() throws RemoteException {
- // Configure UdfpsView to accept the ACTION_DOWN event
- when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
-
- // GIVEN that the overlay is showing and a11y touch exploration NOT enabled
- when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
+ final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
+ givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
// WHEN ACTION_DOWN is received
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ touchProcessorResult.first);
MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
mBiometricExecutor.runAllReady();
downEvent.recycle();
- MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
- mBiometricExecutor.runAllReady();
- moveEvent.recycle();
// THEN NO haptic played
verify(mVibrator, never()).vibrate(
@@ -1221,80 +1109,26 @@
public void noHapticOnTouchUdfpsArea_a11yTouchExplorationDisabled__oneWayHapticsEnabled()
throws RemoteException {
when(mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)).thenReturn(true);
- // Configure UdfpsView to accept the ACTION_DOWN event
- when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
- // GIVEN that the overlay is showing and a11y touch exploration NOT enabled
- when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
+ final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
+ givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
// WHEN ACTION_DOWN is received
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ touchProcessorResult.first);
MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
mBiometricExecutor.runAllReady();
downEvent.recycle();
- MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
- mBiometricExecutor.runAllReady();
- moveEvent.recycle();
// THEN NO haptic played
verify(mVibrator, never()).performHapticFeedback(any(), anyInt());
}
@Test
- public void onTouch_withoutNewTouchDetection_shouldCallOldFingerprintManagerPath()
- throws RemoteException {
- // Disable new touch detection.
- when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(false);
-
- // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
- initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */);
-
- // Configure UdfpsView to accept the ACTION_DOWN event
- when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
-
- // GIVEN that the overlay is showing and a11y touch exploration NOT enabled
- when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
-
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
-
- // WHEN ACTION_DOWN is received
- MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
- mBiometricExecutor.runAllReady();
- downEvent.recycle();
-
- // AND ACTION_MOVE is received
- MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
- mBiometricExecutor.runAllReady();
- moveEvent.recycle();
-
- // AND ACTION_UP is received
- MotionEvent upEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent);
- mBiometricExecutor.runAllReady();
- upEvent.recycle();
-
- // THEN the old FingerprintManager path is invoked.
- verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyInt(),
- anyFloat(), anyFloat());
- verify(mFingerprintManager).onPointerUp(anyLong(), anyInt());
- }
-
- @Test
public void fingerDown_falsingManagerInformed() throws RemoteException {
final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
- givenAcceptFingerDownEvent();
+ givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
// WHEN ACTION_DOWN is received
when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
@@ -1308,85 +1142,46 @@
verify(mFalsingManager).isFalseTouch(UDFPS_AUTHENTICATION);
}
- @Test
- public void onTouch_withNewTouchDetection_shouldCallNewFingerprintManagerPath()
- throws RemoteException {
- final Pair<TouchProcessorResult, TouchProcessorResult> processorResultDownAndUp =
- givenAcceptFingerDownEvent();
-
- // WHEN ACTION_DOWN is received
- when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- processorResultDownAndUp.first);
- MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
- mBiometricExecutor.runAllReady();
- downEvent.recycle();
-
- // AND ACTION_UP is received
- when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- processorResultDownAndUp.second);
- MotionEvent upEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent);
- mBiometricExecutor.runAllReady();
- upEvent.recycle();
-
- // THEN the new FingerprintManager path is invoked.
- verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(),
- anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
- verify(mFingerprintManager).onPointerUp(anyLong(), anyInt(), anyInt(), anyFloat(),
- anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
- }
-
- private Pair<TouchProcessorResult, TouchProcessorResult> givenAcceptFingerDownEvent()
+ private Pair<TouchProcessorResult, TouchProcessorResult> givenFingerEvent(
+ InteractionEvent event1, InteractionEvent event2, boolean a11y)
throws RemoteException {
final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
0L);
final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch(
- InteractionEvent.DOWN, 1 /* pointerId */, touchData);
+ event1, 1 /* pointerId */, touchData);
final TouchProcessorResult processorResultUp = new TouchProcessorResult.ProcessedTouch(
- InteractionEvent.UP, 1 /* pointerId */, touchData);
-
- // Enable new touch detection.
- when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true);
+ event2, 1 /* pointerId */, touchData);
// Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
- initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */);
+ initUdfpsController(mOpticalProps);
// Configure UdfpsView to accept the ACTION_DOWN event
when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
// GIVEN that the overlay is showing and a11y touch exploration NOT enabled
- when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
+ when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(a11y);
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+ if (a11y) {
+ verify(mUdfpsView).setOnHoverListener(mHoverListenerCaptor.capture());
+ } else {
+ verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+ }
return new Pair<>(processorResultDown, processorResultUp);
}
@Test
- public void onTouch_WithNewTouchDetection_forwardToKeyguard() throws RemoteException {
+ public void onTouch_forwardToKeyguard() throws RemoteException {
final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
0L);
final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch(
InteractionEvent.UNCHANGED, -1 /* pointerOnSensorId */, touchData);
- // Enable new touch detection.
- when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true);
-
- // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
- initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */);
-
- // Configure UdfpsView to accept the ACTION_DOWN event
- when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(false);
-
// GIVEN that the overlay is showing and a11y touch exploration NOT enabled
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
- when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
@@ -1402,47 +1197,23 @@
// THEN the touch is forwarded to Keyguard
verify(mStatusBarKeyguardViewManager).onTouch(downEvent);
- downEvent.recycle();
}
@Test
- public void onTouch_withNewTouchDetection_pilferPointer() throws RemoteException {
- final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
- 0L);
- final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch(
- InteractionEvent.DOWN, 1 /* pointerId */, touchData);
-
- // Enable new touch detection.
- when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true);
-
- // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
- initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */);
-
- // Configure UdfpsView to accept the ACTION_DOWN event
- when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
-
- // GIVEN that the overlay is showing and a11y touch exploration NOT enabled
- when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
-
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+ public void onTouch_pilferPointer() throws RemoteException {
+ final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
+ givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UNCHANGED, false);
// WHEN ACTION_DOWN is received
when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- processorResultDown);
+ touchProcessorResult.first);
MotionEvent event = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
mBiometricExecutor.runAllReady();
// WHEN ACTION_MOVE is received after
- final TouchProcessorResult processorResultUnchanged =
- new TouchProcessorResult.ProcessedTouch(
- InteractionEvent.UNCHANGED, 1 /* pointerId */, touchData);
when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- processorResultUnchanged);
+ touchProcessorResult.second);
event.setAction(ACTION_MOVE);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
mBiometricExecutor.runAllReady();
@@ -1453,25 +1224,13 @@
}
@Test
- public void onTouch_withNewTouchDetection_doNotPilferPointer() throws RemoteException {
+ public void onTouch_doNotPilferPointer() throws RemoteException {
final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
0L);
final TouchProcessorResult processorResultUnchanged =
new TouchProcessorResult.ProcessedTouch(InteractionEvent.UNCHANGED,
- 1 /* pointerId */, touchData);
+ -1 /* pointerId */, touchData);
- // Enable new touch detection.
- when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true);
-
- // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
- initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */);
-
- // Configure UdfpsView to not accept the ACTION_DOWN event
- when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(false);
-
- // GIVEN that the overlay is showing and a11y touch exploration NOT enabled
- when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
@@ -1491,36 +1250,17 @@
}
@Test
- public void onTouch_withNewTouchDetection_pilferPointerWhenAltBouncerShowing()
+ public void onTouch_pilferPointerWhenAltBouncerShowing()
throws RemoteException {
- final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
- 0L);
- final TouchProcessorResult processorResultUnchanged =
- new TouchProcessorResult.ProcessedTouch(InteractionEvent.UNCHANGED,
- 1 /* pointerId */, touchData);
+ final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
+ givenFingerEvent(InteractionEvent.UNCHANGED, InteractionEvent.UP, false);
- // Enable new touch detection.
- when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true);
-
- // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
- initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */);
-
- // Configure UdfpsView to not accept the ACTION_DOWN event
- when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(false);
-
- // GIVEN that the alternate bouncer is showing and a11y touch exploration NOT enabled
- when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
+ // WHEN alternate bouncer is showing
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
-
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
// WHEN ACTION_DOWN is received and touch is not within sensor
when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- processorResultUnchanged);
+ touchProcessorResult.first);
MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
mBiometricExecutor.runAllReady();
@@ -1531,32 +1271,10 @@
}
@Test
- public void onTouch_withNewTouchDetection_doNotProcessTouchWhenPullingUpBouncer()
+ public void onTouch_doNotProcessTouchWhenPullingUpBouncer()
throws RemoteException {
- final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
- 0L);
- final TouchProcessorResult processorResultMove =
- new TouchProcessorResult.ProcessedTouch(InteractionEvent.DOWN,
- 1 /* pointerId */, touchData);
-
- // Enable new touch detection.
- when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true);
-
- // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
- initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */);
-
- // Configure UdfpsView to accept the ACTION_MOVE event
- when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
-
- // GIVEN that the alternate bouncer is not showing and a11y touch exploration NOT enabled
- when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
- when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false);
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
-
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+ final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
+ givenFingerEvent(InteractionEvent.UNCHANGED, InteractionEvent.UP, false);
// GIVEN a swipe up to bring up primary bouncer is in progress or swipe down for QS
when(mPrimaryBouncerInteractor.isInTransit()).thenReturn(true);
@@ -1564,7 +1282,7 @@
// WHEN ACTION_MOVE is received and touch is within sensor
when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- processorResultMove);
+ touchProcessorResult.first);
MotionEvent moveEvent = MotionEvent.obtain(0, 0, ACTION_MOVE, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
mBiometricExecutor.runAllReady();
@@ -1578,40 +1296,19 @@
@Test
- public void onTouch_withNewTouchDetection_qsDrag_processesTouchWhenAlternateBouncerVisible()
+ public void onTouch_qsDrag_processesTouchWhenAlternateBouncerVisible()
throws RemoteException {
- final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
- 0L);
- final TouchProcessorResult processorResultMove =
- new TouchProcessorResult.ProcessedTouch(InteractionEvent.DOWN,
- 1 /* pointerId */, touchData);
+ final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
+ givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
- // Enable new touch detection.
- when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true);
-
- // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
- initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */);
-
- // Configure UdfpsView to accept the ACTION_MOVE event
- when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
-
- // GIVEN that the alternate bouncer is showing and a11y touch exploration NOT enabled
- when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
-
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
-
// GIVEN swipe down for QS
when(mPrimaryBouncerInteractor.isInTransit()).thenReturn(false);
when(mLockscreenShadeTransitionController.getQSDragProgress()).thenReturn(1f);
// WHEN ACTION_MOVE is received and touch is within sensor
when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- processorResultMove);
+ touchProcessorResult.first);
MotionEvent moveEvent = MotionEvent.obtain(0, 0, ACTION_MOVE, 0, 0, 0);
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
mBiometricExecutor.runAllReady();
@@ -1689,43 +1386,4 @@
// THEN vibrate is used
verify(mVibrator).performHapticFeedback(any(), eq(UdfpsController.LONG_PRESS));
}
-
- @Test
- public void aodInterrupt_withNewTouchDetection() throws RemoteException {
- mUdfpsController.cancelAodSendFingerUpAction();
- final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
- 0L);
- final TouchProcessorResult processorResultDown =
- new TouchProcessorResult.ProcessedTouch(InteractionEvent.DOWN,
- 1 /* pointerId */, touchData);
-
- // Enable new touch detection.
- when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true);
-
- // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
- initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */);
-
- // GIVEN that the overlay is showing and screen is on and fp is running
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, 0,
- BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mScreenObserver.onScreenTurnedOn();
- mFgExecutor.runAllReady();
-
- // WHEN fingerprint is requested because of AOD interrupt
- mUdfpsController.onAodInterrupt(0, 0, 2f, 3f);
-
- // Check case where touch driver sends touch to UdfpsView as well
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
- when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
- when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- processorResultDown);
- MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
-
- mBiometricExecutor.runAllReady();
-
- // THEN only one onPointerDown is sent
- verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(),
- anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
index 3276e66..e512adc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
@@ -30,7 +30,6 @@
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
@@ -116,7 +115,7 @@
}
public UdfpsKeyguardViewControllerLegacy createUdfpsKeyguardViewController() {
- return createUdfpsKeyguardViewController(false, false);
+ return createUdfpsKeyguardViewController(false);
}
public void captureKeyGuardViewManagerCallback() {
@@ -126,8 +125,7 @@
}
protected UdfpsKeyguardViewControllerLegacy createUdfpsKeyguardViewController(
- boolean useModernBouncer, boolean useExpandedOverlay) {
- mFeatureFlags.set(Flags.UDFPS_NEW_TOUCH_DETECTION, useExpandedOverlay);
+ boolean useModernBouncer) {
UdfpsKeyguardViewControllerLegacy controller = new UdfpsKeyguardViewControllerLegacy(
mView,
mStatusBarStateController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java
index b018a3e..21928cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java
@@ -18,15 +18,12 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.testing.TestableLooper;
-import android.view.MotionEvent;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -44,8 +41,7 @@
UdfpsKeyguardViewLegacyControllerBaseTest {
@Override
public UdfpsKeyguardViewControllerLegacy createUdfpsKeyguardViewController() {
- return createUdfpsKeyguardViewController(/* useModernBouncer */ false,
- /* useExpandedOverlay */ false);
+ return createUdfpsKeyguardViewController(/* useModernBouncer */ false);
}
@Test
@@ -216,37 +212,4 @@
sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED);
assertTrue(mController.shouldPauseAuth());
}
-
- @Test
- // TODO(b/259264861): Tracking Bug
- public void testUdfpsExpandedOverlayOn() {
- // GIVEN view is attached and useExpandedOverlay is true
- mController = createUdfpsKeyguardViewController(false, true);
- mController.onViewAttached();
- captureKeyGuardViewManagerCallback();
-
- // WHEN a touch is received
- mKeyguardViewManagerCallback.onTouch(
- MotionEvent.obtain(0, 0, 0, 0, 0, 0));
-
- // THEN udfpsController onTouch is not called
- assertTrue(mView.mUseExpandedOverlay);
- verify(mUdfpsController, never()).onTouch(any());
- }
-
- @Test
- // TODO(b/259264861): Tracking Bug
- public void testUdfpsExpandedOverlayOff() {
- // GIVEN view is attached and useExpandedOverlay is false
- mController.onViewAttached();
- captureKeyGuardViewManagerCallback();
-
- // WHEN a touch is received
- mKeyguardViewManagerCallback.onTouch(
- MotionEvent.obtain(0, 0, 0, 0, 0, 0));
-
- // THEN udfpsController onTouch is called
- assertFalse(mView.mUseExpandedOverlay);
- verify(mUdfpsController).onTouch(any());
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
index da4548b..02ee53879 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
@@ -106,10 +106,7 @@
mock(SystemClock::class.java),
mKeyguardUpdateMonitor,
)
- return createUdfpsKeyguardViewController(
- /* useModernBouncer */ true, /* useExpandedOverlay */
- false
- )
+ return createUdfpsKeyguardViewController(/* useModernBouncer */ true)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
index 0c8e7a5..9fbe096 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
@@ -16,8 +16,6 @@
package com.android.systemui.biometrics
-import android.graphics.PointF
-import android.graphics.RectF
import android.hardware.biometrics.SensorLocationInternal
import android.testing.TestableLooper
import android.testing.ViewUtils
@@ -42,7 +40,6 @@
import org.mockito.Mockito.nullable
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
-import org.mockito.Mockito.`when` as whenever
private const val SENSOR_X = 50
private const val SENSOR_Y = 250
@@ -80,56 +77,7 @@
ViewUtils.detachView(view)
}
- @Test
- fun layoutSizeFitsSensor() {
- val params = withArgCaptor<RectF> {
- verify(animationViewController).onSensorRectUpdated(capture())
- }
- assertThat(params.width()).isAtLeast(2f * SENSOR_RADIUS)
- assertThat(params.height()).isAtLeast(2f * SENSOR_RADIUS)
- }
-
- @Test
- fun isWithinSensorAreaAndPaused() = isWithinSensorArea(paused = true)
-
- @Test
- fun isWithinSensorAreaAndNotPaused() = isWithinSensorArea(paused = false)
-
- private fun isWithinSensorArea(paused: Boolean) {
- whenever(animationViewController.shouldPauseAuth()).thenReturn(paused)
- whenever(animationViewController.touchTranslation).thenReturn(PointF(0f, 0f))
- val end = (SENSOR_RADIUS * 2) - 1
- for (x in 1 until end) {
- for (y in 1 until end) {
- assertThat(view.isWithinSensorArea(x.toFloat(), y.toFloat())).isEqualTo(!paused)
- }
- }
- }
-
- @Test
- fun isWithinSensorAreaWhenTranslated() {
- val offset = PointF(100f, 200f)
- whenever(animationViewController.touchTranslation).thenReturn(offset)
- val end = (SENSOR_RADIUS * 2) - 1
- for (x in 0 until offset.x.toInt() step 2) {
- for (y in 0 until offset.y.toInt() step 2) {
- assertThat(view.isWithinSensorArea(x.toFloat(), y.toFloat())).isFalse()
- }
- }
- for (x in offset.x.toInt() + 1 until offset.x.toInt() + end) {
- for (y in offset.y.toInt() + 1 until offset.y.toInt() + end) {
- assertThat(view.isWithinSensorArea(x.toFloat(), y.toFloat())).isTrue()
- }
- }
- }
-
- @Test
- fun isNotWithinSensorArea() {
- whenever(animationViewController.touchTranslation).thenReturn(PointF(0f, 0f))
- assertThat(view.isWithinSensorArea(SENSOR_RADIUS * 2.5f, SENSOR_RADIUS.toFloat()))
- .isFalse()
- assertThat(view.isWithinSensorArea(SENSOR_RADIUS.toFloat(), SENSOR_RADIUS * 2.5f)).isFalse()
- }
+ // TODO: Add test to verify view is size of screen
@Test
fun startAndStopIllumination() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalTutorialRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalTutorialRepositoryImplTest.kt
new file mode 100644
index 0000000..30a5497
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalTutorialRepositoryImplTest.kt
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.communal.data.repository
+
+import android.content.pm.UserInfo
+import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.FakeLogBuffer
+import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.util.settings.FakeSettings
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CommunalTutorialRepositoryImplTest : SysuiTestCase() {
+ private lateinit var secureSettings: FakeSettings
+ private lateinit var userRepository: FakeUserRepository
+ private lateinit var userTracker: FakeUserTracker
+ private lateinit var logBuffer: LogBuffer
+
+ private val testDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ logBuffer = FakeLogBuffer.Factory.create()
+ secureSettings = FakeSettings()
+ userRepository = FakeUserRepository()
+ val listOfUserInfo = listOf(MAIN_USER_INFO)
+ userRepository.setUserInfos(listOfUserInfo)
+
+ userTracker = FakeUserTracker()
+ userTracker.set(
+ userInfos = listOfUserInfo,
+ selectedUserIndex = 0,
+ )
+ }
+
+ @Test
+ fun tutorialSettingState_defaultToNotStarted() =
+ testScope.runTest {
+ val repository = initCommunalTutorialRepository()
+ val tutorialSettingState = collectLastValue(repository.tutorialSettingState)()
+ assertThat(tutorialSettingState)
+ .isEqualTo(Settings.Secure.HUB_MODE_TUTORIAL_NOT_STARTED)
+ }
+
+ @Test
+ fun tutorialSettingState_whenTutorialSettingsUpdatedToStarted() =
+ testScope.runTest {
+ val repository = initCommunalTutorialRepository()
+ setTutorialStateSetting(Settings.Secure.HUB_MODE_TUTORIAL_STARTED)
+ val tutorialSettingState = collectLastValue(repository.tutorialSettingState)()
+ assertThat(tutorialSettingState).isEqualTo(Settings.Secure.HUB_MODE_TUTORIAL_STARTED)
+ }
+
+ @Test
+ fun tutorialSettingState_whenTutorialSettingsUpdatedToCompleted() =
+ testScope.runTest {
+ val repository = initCommunalTutorialRepository()
+ setTutorialStateSetting(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)
+ val tutorialSettingState = collectLastValue(repository.tutorialSettingState)()
+ assertThat(tutorialSettingState).isEqualTo(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)
+ }
+
+ private fun initCommunalTutorialRepository(): CommunalTutorialRepositoryImpl {
+ return CommunalTutorialRepositoryImpl(
+ testScope.backgroundScope,
+ testDispatcher,
+ userRepository,
+ secureSettings,
+ userTracker,
+ logBuffer
+ )
+ }
+
+ private fun setTutorialStateSetting(
+ @Settings.Secure.HubModeTutorialState state: Int,
+ user: UserInfo = MAIN_USER_INFO
+ ) {
+ secureSettings.putIntForUser(Settings.Secure.HUB_MODE_TUTORIAL_STATE, state, user.id)
+ }
+
+ companion object {
+ private val MAIN_USER_INFO =
+ UserInfo(/* id= */ 0, /* name= */ "primary", /* flags= */ UserInfo.FLAG_MAIN)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
new file mode 100644
index 0000000..0a9a15e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.domain.interactor
+
+import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED
+import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_NOT_STARTED
+import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_STARTED
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(JUnit4::class)
+class CommunalTutorialInteractorTest : SysuiTestCase() {
+
+ @Mock private lateinit var userTracker: UserTracker
+
+ private val testDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
+
+ private lateinit var underTest: CommunalTutorialInteractor
+ private lateinit var keyguardRepository: FakeKeyguardRepository
+ private lateinit var keyguardInteractor: KeyguardInteractor
+ private lateinit var communalTutorialRepository: FakeCommunalTutorialRepository
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ val withDeps = KeyguardInteractorFactory.create()
+ keyguardInteractor = withDeps.keyguardInteractor
+ keyguardRepository = withDeps.repository
+ communalTutorialRepository = FakeCommunalTutorialRepository()
+
+ underTest =
+ CommunalTutorialInteractor(
+ keyguardInteractor = keyguardInteractor,
+ communalTutorialRepository = communalTutorialRepository,
+ )
+
+ whenever(userTracker.userHandle).thenReturn(mock())
+ }
+
+ @Test
+ fun tutorialUnavailable_whenKeyguardNotVisible() =
+ testScope.runTest {
+ val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
+ communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
+ keyguardRepository.setKeyguardShowing(false)
+ assertThat(isTutorialAvailable).isFalse()
+ }
+
+ @Test
+ fun tutorialUnavailable_whenTutorialIsCompleted() =
+ testScope.runTest {
+ val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
+ keyguardRepository.setKeyguardShowing(true)
+ keyguardRepository.setKeyguardOccluded(false)
+ communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+ assertThat(isTutorialAvailable).isFalse()
+ }
+
+ @Test
+ fun tutorialAvailable_whenTutorialNotStarted() =
+ testScope.runTest {
+ val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
+ keyguardRepository.setKeyguardShowing(true)
+ keyguardRepository.setKeyguardOccluded(false)
+ communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
+ assertThat(isTutorialAvailable).isTrue()
+ }
+
+ @Test
+ fun tutorialAvailable_whenTutorialIsStarted() =
+ testScope.runTest {
+ val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
+ keyguardRepository.setKeyguardShowing(true)
+ keyguardRepository.setKeyguardOccluded(false)
+ communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
+ assertThat(isTutorialAvailable).isTrue()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
index 41a8be9..33a6667 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
@@ -6,6 +6,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.ui.view.layout.sections.DefaultCommunalHubSection
import com.android.systemui.communal.ui.view.layout.sections.DefaultCommunalWidgetSection
import org.junit.Before
import org.junit.Test
@@ -18,6 +19,7 @@
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
class DefaultCommunalBlueprintTest : SysuiTestCase() {
+ @Mock private lateinit var hubSection: DefaultCommunalHubSection
@Mock private lateinit var widgetSection: DefaultCommunalWidgetSection
private lateinit var blueprint: DefaultCommunalBlueprint
@@ -25,13 +27,14 @@
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- blueprint = DefaultCommunalBlueprint(widgetSection)
+ blueprint = DefaultCommunalBlueprint(hubSection, widgetSection)
}
@Test
fun addView() {
val constraintLayout = ConstraintLayout(context, null)
blueprint.replaceViews(null, constraintLayout)
+ verify(hubSection).addViews(constraintLayout)
verify(widgetSection).addViews(constraintLayout)
}
@@ -39,6 +42,7 @@
fun applyConstraints() {
val cs = ConstraintSet()
blueprint.applyConstraints(cs)
+ verify(hubSection).applyConstraints(cs)
verify(widgetSection).applyConstraints(cs)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt
index 68ea7eb..6c2e136 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt
@@ -25,7 +25,7 @@
import com.android.systemui.demomode.DemoMode.ACTION_DEMO
import com.android.systemui.demomode.DemoMode.COMMAND_STATUS
import com.android.systemui.dump.DumpManager
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.FakeGlobalSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
@@ -48,7 +48,7 @@
@Mock private lateinit var dumpManager: DumpManager
- private val globalSettings = FakeSettings()
+ private val globalSettings = FakeGlobalSettings()
private val testDispatcher = UnconfinedTestDispatcher()
private val testScope = TestScope(testDispatcher)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt
index 14c5ec0..c12a581 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt
@@ -36,7 +36,6 @@
import org.junit.Assert
import org.junit.Before
import org.junit.Test
-import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.anyString
@@ -479,7 +478,7 @@
verify(flagManager, times(numReads))
.readFlagValue(eq(name), any<FlagSerializer<*>>())
verify(flagManager).nameToSettingsKey(eq(name))
- verify(globalSettings).putStringForUser(eq("key-$name"), eq(data), anyInt())
+ verify(globalSettings).putString(eq("key-$name"), eq(data))
verify(flagManager).dispatchListenersAndMaybeRestart(eq(name), any())
}
.verifyNoMoreInteractions()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index b1cf051..2d3f69d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -78,6 +78,8 @@
import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.util.RingerModeLiveData;
import com.android.systemui.util.RingerModeTracker;
+import com.android.systemui.util.settings.FakeGlobalSettings;
+import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.settings.GlobalSettings;
import com.android.systemui.util.settings.SecureSettings;
@@ -105,8 +107,8 @@
@Mock private LockPatternUtils mLockPatternUtils;
@Mock private BroadcastDispatcher mBroadcastDispatcher;
@Mock private TelephonyListenerManager mTelephonyListenerManager;
- @Mock private GlobalSettings mGlobalSettings;
- @Mock private SecureSettings mSecureSettings;
+ private GlobalSettings mGlobalSettings;
+ private SecureSettings mSecureSettings;
@Mock private Resources mResources;
@Mock private ConfigurationController mConfigurationController;
@Mock private UserTracker mUserTracker;
@@ -148,6 +150,9 @@
when(mResources.getConfiguration()).thenReturn(
getContext().getResources().getConfiguration());
+ mGlobalSettings = new FakeGlobalSettings();
+ mSecureSettings = new FakeSettings();
+
mGlobalActionsDialogLite = new GlobalActionsDialogLite(mContext,
mWindowManagerFuncs,
mAudioManager,
@@ -592,8 +597,8 @@
UserInfo currentUser = mockCurrentUser(FLAG_ADMIN);
when(mGlobalActionsDialogLite.getCurrentUser()).thenReturn(currentUser);
- when(mGlobalSettings.getIntForUser(Settings.Secure.BUGREPORT_IN_POWER_MENU,
- 0, currentUser.id)).thenReturn(1);
+ mSecureSettings.putIntForUser(Settings.Secure.BUGREPORT_IN_POWER_MENU, 1,
+ currentUser.id);
GlobalActionsDialogLite.BugReportAction bugReportAction =
mGlobalActionsDialogLite.makeBugReportActionForTesting();
@@ -605,8 +610,8 @@
UserInfo currentUser = mockCurrentUser(0);
when(mGlobalActionsDialogLite.getCurrentUser()).thenReturn(currentUser);
- doReturn(1).when(mGlobalSettings)
- .getIntForUser(Settings.Secure.BUGREPORT_IN_POWER_MENU, 0, currentUser.id);
+ mSecureSettings.putIntForUser(Settings.Secure.BUGREPORT_IN_POWER_MENU, 1,
+ currentUser.id);
GlobalActionsDialogLite.BugReportAction bugReportAction =
mGlobalActionsDialogLite.makeBugReportActionForTesting();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
index faf9751..977f1db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
@@ -25,13 +25,13 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.notification.EnableZenModeDialog
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
+import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.policy.ZenModeController
import com.android.systemui.util.mockito.argumentCaptor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
index 1e80fb6..26fcb23 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
@@ -22,8 +22,8 @@
import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import com.android.systemui.util.FakeSharedPreferences
@@ -70,8 +70,7 @@
val resources: Resources = mock()
whenever(resources.getStringArray(R.array.config_keyguardQuickAffordanceDefaults))
.thenReturn(emptyArray())
- whenever(resources.getBoolean(R.bool.custom_lockscreen_shortcuts_enabled))
- .thenReturn(true)
+ whenever(resources.getBoolean(R.bool.custom_lockscreen_shortcuts_enabled)).thenReturn(true)
whenever(context.resources).thenReturn(resources)
testDispatcher = UnconfinedTestDispatcher()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index 9ee22c8..b32905f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -204,8 +204,7 @@
fromScene = SceneKey.Gone,
toScene = SceneKey.Lockscreen,
progress = flowOf(0f),
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
runCurrent()
assertThat(isAnimate).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
index e10815d..a5d7457 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
@@ -35,7 +35,6 @@
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -47,7 +46,6 @@
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
-@ExperimentalCoroutinesApi
@SmallTest
@RunWith(AndroidJUnit4::class)
class KeyguardKeyEventInteractorTest : SysuiTestCase() {
@@ -130,73 +128,58 @@
}
@Test
- fun dispatchKeyEvent_menuActionUp_awakeKeyguard_showsPrimaryBouncer() {
+ fun dispatchKeyEvent_menuActionUp_interactiveKeyguard_collapsesShade() {
powerInteractor.setAwakeForTest()
whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true)
- verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_MENU)
+ val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU)
+ assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
+ verify(shadeController).animateCollapseShadeForced()
}
@Test
- fun dispatchKeyEvent_menuActionUp_awakeShadeLocked_collapsesShade() {
+ fun dispatchKeyEvent_menuActionUp_interactiveShadeLocked_collapsesShade() {
powerInteractor.setAwakeForTest()
whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true)
- verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_MENU)
+ // action down: does NOT collapse the shade
+ val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU)
+ assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
+ verify(shadeController, never()).animateCollapseShadeForced()
+
+ // action up: collapses the shade
+ val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU)
+ assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
+ verify(shadeController).animateCollapseShadeForced()
}
@Test
- fun dispatchKeyEvent_menuActionUp_asleepKeyguard_neverCollapsesShade() {
+ fun dispatchKeyEvent_menuActionUp_nonInteractiveKeyguard_neverCollapsesShade() {
powerInteractor.setAsleepForTest()
whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
whenever(statusBarKeyguardViewManager.shouldDismissOnMenuPressed()).thenReturn(true)
- verifyActionsDoNothing(KeyEvent.KEYCODE_MENU)
+ val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU)
+ assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isFalse()
+ verify(shadeController, never()).animateCollapseShadeForced()
}
@Test
- fun dispatchKeyEvent_spaceActionUp_awakeKeyguard_collapsesShade() {
+ fun dispatchKeyEvent_spaceActionUp_interactiveKeyguard_collapsesShade() {
powerInteractor.setAwakeForTest()
whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
- whenever(statusBarKeyguardViewManager.primaryBouncerIsShowing()).thenReturn(false)
- verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_SPACE)
- }
+ // action down: does NOT collapse the shade
+ val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_SPACE)
+ assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
+ verify(shadeController, never()).animateCollapseShadeForced()
- @Test
- fun dispatchKeyEvent_spaceActionUp_shadeLocked_collapsesShade() {
- powerInteractor.setAwakeForTest()
- whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
-
- verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_SPACE)
- }
-
- @Test
- fun dispatchKeyEvent_enterActionUp_awakeKeyguard_showsPrimaryBouncer() {
- powerInteractor.setAwakeForTest()
- whenever(statusBarKeyguardViewManager.primaryBouncerIsShowing()).thenReturn(false)
- whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
-
- verifyActionUpShowsPrimaryBouncer(KeyEvent.KEYCODE_ENTER)
- }
-
- @Test
- fun dispatchKeyEvent_enterActionUp_awakeKeyguard_primaryBouncerAlreadyShowing() {
- powerInteractor.setAwakeForTest()
- whenever(statusBarKeyguardViewManager.primaryBouncerIsShowing()).thenReturn(true)
- whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
-
- verifyActionsDoNothing(KeyEvent.KEYCODE_ENTER)
- }
-
- @Test
- fun dispatchKeyEvent_enterActionUp_shadeLocked_collapsesShade() {
- powerInteractor.setAwakeForTest()
- whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
-
- verifyActionUpCollapsesTheShade(KeyEvent.KEYCODE_ENTER)
+ // action up: collapses the shade
+ val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_SPACE)
+ assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
+ verify(shadeController).animateCollapseShadeForced()
}
@Test
@@ -266,42 +249,4 @@
.isFalse()
verify(statusBarKeyguardViewManager, never()).interceptMediaKey(any())
}
-
- private fun verifyActionUpCollapsesTheShade(keycode: Int) {
- // action down: does NOT collapse the shade
- val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode)
- assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
- verify(shadeController, never()).animateCollapseShadeForced()
-
- // action up: collapses the shade
- val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode)
- assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
- verify(shadeController).animateCollapseShadeForced()
- }
-
- private fun verifyActionUpShowsPrimaryBouncer(keycode: Int) {
- // action down: does NOT collapse the shade
- val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode)
- assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
- verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any())
-
- // action up: collapses the shade
- val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode)
- assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
- verify(statusBarKeyguardViewManager).showPrimaryBouncer(eq(true))
- }
-
- private fun verifyActionsDoNothing(keycode: Int) {
- // action down: does nothing
- val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode)
- assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
- verify(shadeController, never()).animateCollapseShadeForced()
- verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any())
-
- // action up: doesNothing
- val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode)
- assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isFalse()
- verify(shadeController, never()).animateCollapseShadeForced()
- verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any())
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index 2cf0e77..5d5ece0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -433,7 +433,9 @@
// GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
runTransitionAndSetWakefulness(
- KeyguardState.GONE, KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+ KeyguardState.GONE,
+ KeyguardState.DREAMING_LOCKSCREEN_HOSTED
+ )
// WHEN the lockscreen hosted dream stops
keyguardRepository.setIsActiveDreamLockscreenHosted(false)
@@ -457,7 +459,9 @@
testScope.runTest {
// GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
runTransitionAndSetWakefulness(
- KeyguardState.GONE, KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+ KeyguardState.GONE,
+ KeyguardState.DREAMING_LOCKSCREEN_HOSTED
+ )
// WHEN biometrics succeeds with wake and unlock from dream mode
keyguardRepository.setBiometricUnlockState(
@@ -487,7 +491,9 @@
// GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
runTransitionAndSetWakefulness(
- KeyguardState.GONE, KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+ KeyguardState.GONE,
+ KeyguardState.DREAMING_LOCKSCREEN_HOSTED
+ )
// WHEN the primary bouncer is set to show
bouncerRepository.setPrimaryShow(true)
@@ -515,7 +521,9 @@
// GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
runTransitionAndSetWakefulness(
- KeyguardState.GONE, KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+ KeyguardState.GONE,
+ KeyguardState.DREAMING_LOCKSCREEN_HOSTED
+ )
// WHEN the device begins to sleep
keyguardRepository.setIsActiveDreamLockscreenHosted(false)
@@ -547,7 +555,9 @@
// GIVEN a prior transition has run to DREAMING_LOCKSCREEN_HOSTED
runTransitionAndSetWakefulness(
- KeyguardState.GONE, KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
+ KeyguardState.GONE,
+ KeyguardState.DREAMING_LOCKSCREEN_HOSTED
+ )
// WHEN the keyguard is occluded and the lockscreen hosted dream stops
keyguardRepository.setIsActiveDreamLockscreenHosted(false)
@@ -783,7 +793,9 @@
testScope.runTest {
// GIVEN a prior transition has run to ALTERNATE_BOUNCER
runTransitionAndSetWakefulness(
- KeyguardState.LOCKSCREEN, KeyguardState.ALTERNATE_BOUNCER)
+ KeyguardState.LOCKSCREEN,
+ KeyguardState.ALTERNATE_BOUNCER
+ )
// WHEN the alternateBouncer stops showing and then the primary bouncer shows
bouncerRepository.setPrimaryShow(true)
@@ -808,7 +820,9 @@
// GIVEN a prior transition has run to ALTERNATE_BOUNCER
bouncerRepository.setAlternateVisible(true)
runTransitionAndSetWakefulness(
- KeyguardState.LOCKSCREEN, KeyguardState.ALTERNATE_BOUNCER)
+ KeyguardState.LOCKSCREEN,
+ KeyguardState.ALTERNATE_BOUNCER
+ )
// GIVEN the primary bouncer isn't showing, aod available and starting to sleep
bouncerRepository.setPrimaryShow(false)
@@ -838,7 +852,9 @@
// GIVEN a prior transition has run to ALTERNATE_BOUNCER
bouncerRepository.setAlternateVisible(true)
runTransitionAndSetWakefulness(
- KeyguardState.LOCKSCREEN, KeyguardState.ALTERNATE_BOUNCER)
+ KeyguardState.LOCKSCREEN,
+ KeyguardState.ALTERNATE_BOUNCER
+ )
// GIVEN the primary bouncer isn't showing, aod not available and starting to sleep
// to sleep
@@ -869,7 +885,9 @@
// GIVEN a prior transition has run to ALTERNATE_BOUNCER
bouncerRepository.setAlternateVisible(true)
runTransitionAndSetWakefulness(
- KeyguardState.LOCKSCREEN, KeyguardState.ALTERNATE_BOUNCER)
+ KeyguardState.LOCKSCREEN,
+ KeyguardState.ALTERNATE_BOUNCER
+ )
// GIVEN the primary bouncer isn't showing and device not sleeping
bouncerRepository.setPrimaryShow(false)
@@ -980,7 +998,9 @@
// GIVEN a prior transition has run to PRIMARY_BOUNCER
bouncerRepository.setPrimaryShow(true)
runTransitionAndSetWakefulness(
- KeyguardState.DREAMING_LOCKSCREEN_HOSTED, KeyguardState.PRIMARY_BOUNCER)
+ KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
+ KeyguardState.PRIMARY_BOUNCER
+ )
// WHEN the primary bouncer stops showing and lockscreen hosted dream still active
bouncerRepository.setPrimaryShow(false)
@@ -1161,6 +1181,57 @@
}
@Test
+ fun dreamingToOccluded() =
+ testScope.runTest {
+ // GIVEN a prior transition has run to DREAMING
+ keyguardRepository.setDreaming(true)
+ runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.DREAMING)
+ runCurrent()
+
+ // WHEN the keyguard is occluded and device wakes up and is no longer dreaming
+ keyguardRepository.setDreaming(false)
+ keyguardRepository.setKeyguardOccluded(true)
+ powerInteractor.setAwakeForTest()
+ runCurrent()
+
+ val info =
+ withArgCaptor<TransitionInfo> {
+ verify(transitionRepository).startTransition(capture(), anyBoolean())
+ }
+ // THEN a transition to OCCLUDED should occur
+ assertThat(info.ownerName).isEqualTo("FromDreamingTransitionInteractor")
+ assertThat(info.from).isEqualTo(KeyguardState.DREAMING)
+ assertThat(info.to).isEqualTo(KeyguardState.OCCLUDED)
+ assertThat(info.animator).isNotNull()
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
+ fun lockscreenToOccluded() =
+ testScope.runTest {
+ // GIVEN a prior transition has run to LOCKSCREEN
+ runTransitionAndSetWakefulness(KeyguardState.GONE, KeyguardState.LOCKSCREEN)
+ runCurrent()
+
+ // WHEN the keyguard is occluded
+ keyguardRepository.setKeyguardOccluded(true)
+ runCurrent()
+
+ val info =
+ withArgCaptor<TransitionInfo> {
+ verify(transitionRepository).startTransition(capture(), anyBoolean())
+ }
+ // THEN a transition to OCCLUDED should occur
+ assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
+ assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN)
+ assertThat(info.to).isEqualTo(KeyguardState.OCCLUDED)
+ assertThat(info.animator).isNotNull()
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
fun aodToOccluded() =
testScope.runTest {
// GIVEN a prior transition has run to AOD
@@ -1286,8 +1357,8 @@
}
private suspend fun TestScope.runTransitionAndSetWakefulness(
- from: KeyguardState,
- to: KeyguardState
+ from: KeyguardState,
+ to: KeyguardState
) {
transitionRepository.sendTransitionStep(
TransitionStep(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
index 7940b45..50ee026 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
@@ -23,6 +23,7 @@
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.ui.view.layout.sections.CommunalTutorialIndicatorSection
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.view.KeyguardRootView
@@ -65,6 +66,7 @@
@Mock private lateinit var splitShadeGuidelines: SplitShadeGuidelines
@Mock private lateinit var aodNotificationIconsSection: AodNotificationIconsSection
@Mock private lateinit var aodBurnInSection: AodBurnInSection
+ @Mock private lateinit var communalTutorialIndicatorSection: CommunalTutorialIndicatorSection
@Before
fun setup() {
@@ -83,6 +85,7 @@
splitShadeGuidelines,
aodNotificationIconsSection,
aodBurnInSection,
+ communalTutorialIndicatorSection,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
index 9364097..d7802aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
@@ -60,10 +60,7 @@
MockitoAnnotations.initMocks(this)
repository = FakeKeyguardTransitionRepository()
val featureFlags =
- FakeFeatureFlags().apply {
- set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false)
- set(Flags.UDFPS_NEW_TOUCH_DETECTION, true)
- }
+ FakeFeatureFlags().apply { set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false) }
val interactor =
KeyguardTransitionInteractorFactory.create(
scope = TestScope().backgroundScope,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
index fb0a4be..59d8104 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
@@ -48,7 +48,6 @@
import com.android.internal.logging.InstanceId
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.InstanceIdSequenceFake
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dump.DumpManager
@@ -63,6 +62,7 @@
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.res.R
import com.android.systemui.statusbar.SbnBuilder
import com.android.systemui.tuner.TunerService
import com.android.systemui.util.concurrency.FakeExecutor
@@ -2204,6 +2204,85 @@
}
@Test
+ fun testSessionPlayer_sessionDestroyed_noResume_fullyRemoved() {
+ whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
+ addPlaybackStateAction()
+
+ // When a media control with PlaybackState actions is added, times out,
+ // and then the session is destroyed
+ addNotificationAndLoad()
+ val data = mediaDataCaptor.value
+ assertThat(data.active).isTrue()
+ mediaDataManager.setTimedOut(KEY, timedOut = true)
+ sessionCallbackCaptor.value.invoke(KEY)
+
+ // It is fully removed.
+ verify(listener).onMediaDataRemoved(eq(KEY))
+ verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
+ verify(listener, never())
+ .onMediaDataLoaded(
+ eq(PACKAGE_NAME),
+ eq(KEY),
+ capture(mediaDataCaptor),
+ eq(true),
+ eq(0),
+ eq(false)
+ )
+ }
+
+ @Test
+ fun testSessionPlayer_destroyedWhileActive_noResume_fullyRemoved() {
+ whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
+ addPlaybackStateAction()
+
+ // When a media control using session actions is added, and then the session is destroyed
+ // without timing out first
+ addNotificationAndLoad()
+ val data = mediaDataCaptor.value
+ assertThat(data.active).isTrue()
+ sessionCallbackCaptor.value.invoke(KEY)
+
+ // It is fully removed
+ verify(listener).onMediaDataRemoved(eq(KEY))
+ verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
+ verify(listener, never())
+ .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
+ }
+
+ @Test
+ fun testSessionPlayer_canResume_destroyedWhileActive_setToResume() {
+ whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
+ addPlaybackStateAction()
+
+ // When a media control using session actions and that does allow resumption is added,
+ addNotificationAndLoad()
+ val dataResumable = mediaDataCaptor.value.copy(resumeAction = Runnable {})
+ mediaDataManager.onMediaDataLoaded(KEY, null, dataResumable)
+
+ // And then the session is destroyed without timing out first
+ sessionCallbackCaptor.value.invoke(KEY)
+
+ // It is converted to a resume player
+ verify(listener)
+ .onMediaDataLoaded(
+ eq(PACKAGE_NAME),
+ eq(KEY),
+ capture(mediaDataCaptor),
+ eq(true),
+ eq(0),
+ eq(false)
+ )
+ assertThat(mediaDataCaptor.value.resumption).isTrue()
+ assertThat(mediaDataCaptor.value.active).isFalse()
+ verify(logger)
+ .logActiveConvertedToResume(
+ anyInt(),
+ eq(PACKAGE_NAME),
+ eq(mediaDataCaptor.value.instanceId)
+ )
+ }
+
+ @Test
fun testSessionDestroyed_noNotificationKey_stillRemoved() {
whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt
index 85d3fba..deefab6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt
@@ -21,6 +21,7 @@
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
+import android.media.MediaRoute2Info
import android.media.MediaRouter2Manager
import android.media.RoutingSessionInfo
import android.media.session.MediaController
@@ -34,15 +35,18 @@
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
-import com.android.systemui.res.R
+import com.android.settingslib.media.PhoneMediaDevice
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.flags.Flags
import com.android.systemui.media.controls.MediaTestUtils
import com.android.systemui.media.controls.models.player.MediaData
import com.android.systemui.media.controls.models.player.MediaDeviceData
import com.android.systemui.media.controls.util.MediaControllerFactory
import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionManager
import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionManagerFactory
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.eq
@@ -95,6 +99,7 @@
@Mock private lateinit var device: MediaDevice
@Mock private lateinit var icon: Drawable
@Mock private lateinit var route: RoutingSessionInfo
+ @Mock private lateinit var selectedRoute: MediaRoute2Info
@Mock private lateinit var controller: MediaController
@Mock private lateinit var playbackInfo: PlaybackInfo
@Mock private lateinit var configurationController: ConfigurationController
@@ -107,6 +112,7 @@
private lateinit var session: MediaSession
private lateinit var mediaData: MediaData
@JvmField @Rule val mockito = MockitoJUnit.rule()
+ private val featureFlags = FakeFeatureFlagsClassic()
@Before
fun setUp() {
@@ -124,7 +130,8 @@
localBluetoothManager,
fakeFgExecutor,
fakeBgExecutor,
- dumpster
+ dumpster,
+ featureFlags,
)
manager.addListener(listener)
@@ -143,6 +150,7 @@
MediaTestUtils.emptyMediaData.copy(packageName = PACKAGE, token = session.sessionToken)
whenever(controllerFactory.create(session.sessionToken)).thenReturn(controller)
setupLeAudioConfiguration(false)
+ featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, false)
}
@After
@@ -454,9 +462,54 @@
}
@Test
- fun mr2ReturnsRouteWithNullName_useLocalDeviceName() {
+ fun mr2ReturnsSystemRouteWithNullName_isPhone_usePhoneName() {
+ featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
+ // When the routing session name is null, and is a system session for a PhoneMediaDevice
+ val phoneDevice = mock(PhoneMediaDevice::class.java)
+ whenever(phoneDevice.iconWithoutBackground).thenReturn(icon)
+ whenever(lmm.currentConnectedDevice).thenReturn(phoneDevice)
+ whenever(route.isSystemSession).thenReturn(true)
+
+ whenever(route.name).thenReturn(null)
+ whenever(mr2.getSelectedRoutes(any())).thenReturn(listOf(selectedRoute))
+ whenever(selectedRoute.name).thenReturn(REMOTE_DEVICE_NAME)
+ whenever(selectedRoute.type).thenReturn(MediaRoute2Info.TYPE_BUILTIN_SPEAKER)
+
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+
+ // Then the device name is the PhoneMediaDevice string
+ val data = captureDeviceData(KEY)
+ assertThat(data.name)
+ .isEqualTo(
+ context.getString(com.android.settingslib.R.string.media_transfer_this_device_name)
+ )
+ }
+
+ @Test
+ fun mr2ReturnsSystemRouteWithNullName_useSelectedRouteName() {
+ featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
+ // When the routing session does not have a name, and is a system session
+ whenever(route.name).thenReturn(null)
+ whenever(mr2.getSelectedRoutes(any())).thenReturn(listOf(selectedRoute))
+ whenever(selectedRoute.name).thenReturn(REMOTE_DEVICE_NAME)
+ whenever(route.isSystemSession).thenReturn(true)
+
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+
+ // Then the device name is the selected route name
+ val data = captureDeviceData(KEY)
+ assertThat(data.name).isEqualTo(REMOTE_DEVICE_NAME)
+ }
+
+ @Test
+ fun mr2ReturnsNonSystemRouteWithNullName_useLocalDeviceName() {
// GIVEN that MR2Manager returns a routing session that does not have a name
whenever(route.name).thenReturn(null)
+ whenever(route.isSystemSession).thenReturn(false)
// WHEN a notification is added
manager.onMediaDataLoaded(KEY, null, mediaData)
fakeBgExecutor.runAllReady()
@@ -672,13 +725,108 @@
assertThat(data.showBroadcastButton).isFalse()
}
- fun captureCallback(): LocalMediaManager.DeviceCallback {
+ // Duplicates of above tests with MEDIA_DEVICE_NAME_FIX enabled
+
+ @Test
+ fun loadMediaDataWithNullToken_withNameFix() {
+ featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
+ manager.onMediaDataLoaded(KEY, null, mediaData.copy(token = null))
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+ val data = captureDeviceData(KEY)
+ assertThat(data.enabled).isTrue()
+ assertThat(data.name).isEqualTo(DEVICE_NAME)
+ }
+
+ @Test
+ fun onAboutToConnectDeviceAdded_findsDeviceInfoFromAddress_withNameFix() {
+ featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ // Run and reset the executors and listeners so we only focus on new events.
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+ reset(listener)
+
+ // Ensure we'll get device info when using the address
+ val fullMediaDevice = mock(MediaDevice::class.java)
+ val address = "fakeAddress"
+ val nameFromDevice = "nameFromDevice"
+ val iconFromDevice = mock(Drawable::class.java)
+ whenever(lmm.getMediaDeviceById(eq(address))).thenReturn(fullMediaDevice)
+ whenever(fullMediaDevice.name).thenReturn(nameFromDevice)
+ whenever(fullMediaDevice.iconWithoutBackground).thenReturn(iconFromDevice)
+
+ // WHEN the about-to-connect device changes to non-null
+ val deviceCallback = captureCallback()
+ val nameFromParam = "nameFromParam"
+ val iconFromParam = mock(Drawable::class.java)
+ deviceCallback.onAboutToConnectDeviceAdded(address, nameFromParam, iconFromParam)
+ assertThat(fakeFgExecutor.runAllReady()).isEqualTo(1)
+
+ // THEN the about-to-connect device based on the address is returned
+ val data = captureDeviceData(KEY)
+ assertThat(data.enabled).isTrue()
+ assertThat(data.name).isEqualTo(nameFromDevice)
+ assertThat(data.name).isNotEqualTo(nameFromParam)
+ assertThat(data.icon).isEqualTo(iconFromDevice)
+ assertThat(data.icon).isNotEqualTo(iconFromParam)
+ }
+
+ @Test
+ fun deviceNameFromMR2RouteInfo_withNameFix() {
+ featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
+ // GIVEN that MR2Manager returns a valid routing session
+ whenever(route.name).thenReturn(REMOTE_DEVICE_NAME)
+ // WHEN a notification is added
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+ // THEN it uses the route name (instead of device name)
+ val data = captureDeviceData(KEY)
+ assertThat(data.enabled).isTrue()
+ assertThat(data.name).isEqualTo(REMOTE_DEVICE_NAME)
+ }
+
+ @Test
+ fun deviceDisabledWhenMR2ReturnsNullRouteInfo_withNameFix() {
+ featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
+ // GIVEN that MR2Manager returns null for routing session
+ whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
+ // WHEN a notification is added
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+ // THEN the device is disabled and name is set to null
+ val data = captureDeviceData(KEY)
+ assertThat(data.enabled).isFalse()
+ assertThat(data.name).isNull()
+ }
+
+ @Test
+ fun mr2ReturnsNonSystemRouteWithNullName_useLocalDeviceName_withNameFix() {
+ featureFlags.set(Flags.MEDIA_DEVICE_NAME_FIX, true)
+ // GIVEN that MR2Manager returns a routing session that does not have a name
+ whenever(route.name).thenReturn(null)
+ whenever(route.isSystemSession).thenReturn(false)
+ // WHEN a notification is added
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+ // THEN the device is enabled and uses the current connected device name
+ val data = captureDeviceData(KEY)
+ assertThat(data.name).isEqualTo(DEVICE_NAME)
+ assertThat(data.enabled).isTrue()
+ }
+
+ // End duplicate tests
+
+ private fun captureCallback(): LocalMediaManager.DeviceCallback {
val captor = ArgumentCaptor.forClass(LocalMediaManager.DeviceCallback::class.java)
verify(lmm).registerCallback(captor.capture())
return captor.getValue()
}
- fun setupBroadcastCallback(): BluetoothLeBroadcast.Callback {
+ private fun setupBroadcastCallback(): BluetoothLeBroadcast.Callback {
val callback: BluetoothLeBroadcast.Callback =
object : BluetoothLeBroadcast.Callback {
override fun onBroadcastStarted(reason: Int, broadcastId: Int) {}
@@ -699,7 +847,7 @@
return callback
}
- fun setupLeAudioConfiguration(isLeAudio: Boolean) {
+ private fun setupLeAudioConfiguration(isLeAudio: Boolean) {
whenever(localBluetoothManager.profileManager).thenReturn(localBluetoothProfileManager)
whenever(localBluetoothProfileManager.leAudioBroadcastProfile)
.thenReturn(localBluetoothLeBroadcast)
@@ -707,7 +855,7 @@
whenever(localBluetoothLeBroadcast.appSourceName).thenReturn(BROADCAST_APP_NAME)
}
- fun setupBroadcastPackage(currentName: String) {
+ private fun setupBroadcastPackage(currentName: String) {
whenever(lmm.packageName).thenReturn(PACKAGE)
whenever(packageManager.getApplicationInfo(eq(PACKAGE), anyInt()))
.thenReturn(applicationInfo)
@@ -715,7 +863,7 @@
context.setMockPackageManager(packageManager)
}
- fun captureDeviceData(key: String, oldKey: String? = null): MediaDeviceData {
+ private fun captureDeviceData(key: String, oldKey: String? = null): MediaDeviceData {
val captor = ArgumentCaptor.forClass(MediaDeviceData::class.java)
verify(listener).onMediaDeviceChanged(eq(key), eq(oldKey), captor.capture())
return captor.getValue()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
index de57b60..5296f1a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
@@ -24,7 +24,6 @@
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardViewController
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq
import com.android.systemui.dreams.DreamOverlayStateController
@@ -32,6 +31,7 @@
import com.android.systemui.media.controls.pipeline.MediaDataManager
import com.android.systemui.media.dream.MediaDreamComplication
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.res.R
import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
index f25cd24..34360d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
@@ -7,12 +7,16 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.mediaprojection.appselector.data.RecentTask
import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider
+import com.android.systemui.mediaprojection.appselector.data.RecentTaskThumbnailLoader
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver
+import com.android.systemui.shared.recents.model.ThumbnailData
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -34,6 +38,8 @@
private val view: MediaProjectionAppSelectorView = mock()
private val policyResolver: ScreenCaptureDevicePolicyResolver = mock()
+ private val thumbnailLoader = FakeThumbnailLoader()
+
private val controller =
MediaProjectionAppSelectorController(
taskListProvider,
@@ -42,7 +48,8 @@
personalUserHandle,
scope,
appSelectorComponentName,
- callerPackageName
+ callerPackageName,
+ thumbnailLoader,
)
@Before
@@ -69,6 +76,22 @@
}
@Test
+ fun init_refreshesThumbnailsOfForegroundTasks() = runTest {
+ val tasks =
+ listOf(
+ createRecentTask(taskId = 1, isForegroundTask = false),
+ createRecentTask(taskId = 2, isForegroundTask = true),
+ createRecentTask(taskId = 3, isForegroundTask = true),
+ createRecentTask(taskId = 4, isForegroundTask = false),
+ )
+ taskListProvider.tasks = tasks
+
+ controller.init()
+
+ assertThat(thumbnailLoader.capturedTaskIds).containsExactly(2, 3)
+ }
+
+ @Test
fun initMultipleRecentTasksWithoutAppSelectorTask_bindsListInTheSameOrder() {
val tasks =
listOf(
@@ -188,14 +211,16 @@
private fun createRecentTask(
taskId: Int,
topActivityComponent: ComponentName? = null,
- userId: Int = personalUserHandle.identifier
+ userId: Int = personalUserHandle.identifier,
+ isForegroundTask: Boolean = false
): RecentTask {
return RecentTask(
taskId = taskId,
topActivityComponent = topActivityComponent,
baseIntentComponent = ComponentName("com", "Test"),
userId = userId,
- colorBackground = 0
+ colorBackground = 0,
+ isForegroundTask = isForegroundTask,
)
}
@@ -205,4 +230,18 @@
override suspend fun loadRecentTasks(): List<RecentTask> = tasks
}
+
+ private class FakeThumbnailLoader : RecentTaskThumbnailLoader {
+
+ val capturedTaskIds = mutableListOf<Int>()
+
+ override suspend fun loadThumbnail(taskId: Int): ThumbnailData? {
+ return null
+ }
+
+ override suspend fun captureThumbnail(taskId: Int): ThumbnailData? {
+ capturedTaskIds += taskId
+ return null
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ActivityTaskManagerThumbnailLoaderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ActivityTaskManagerThumbnailLoaderTest.kt
new file mode 100644
index 0000000..db275ec
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ActivityTaskManagerThumbnailLoaderTest.kt
@@ -0,0 +1,109 @@
+package com.android.systemui.mediaprojection.appselector.data
+
+import android.app.WindowConfiguration
+import android.content.ComponentName
+import android.content.res.Configuration
+import android.graphics.ColorSpace
+import android.graphics.Point
+import android.graphics.Rect
+import android.hardware.HardwareBuffer
+import android.testing.AndroidTestingRunner
+import android.view.Surface
+import android.window.TaskSnapshot
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.shared.recents.model.ThumbnailData
+import com.android.systemui.shared.system.ActivityManagerWrapper
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+class ActivityTaskManagerThumbnailLoaderTest : SysuiTestCase() {
+
+ private val dispatcher = UnconfinedTestDispatcher()
+ private val testScope = TestScope(dispatcher)
+ private val activityManager = mock<ActivityManagerWrapper>()
+ private val loader = ActivityTaskManagerThumbnailLoader(dispatcher, activityManager)
+
+ @Test
+ fun loadThumbnail_emptyThumbnail_returnsNull() =
+ testScope.runTest {
+ val taskId = 123
+ val isLowResolution = false
+ val thumbnailData = ThumbnailData()
+ whenever(activityManager.getTaskThumbnail(taskId, isLowResolution))
+ .thenReturn(thumbnailData)
+
+ assertThat(loader.loadThumbnail(taskId)).isNull()
+ }
+
+ @Test
+ fun loadThumbnail_thumbnailAvailable_returnsThumbnailData() =
+ testScope.runTest {
+ val taskId = 123
+ val isLowResolution = false
+ val snapshot = createTaskSnapshot()
+ val thumbnailData = ThumbnailData(snapshot)
+ whenever(activityManager.getTaskThumbnail(taskId, isLowResolution))
+ .thenReturn(thumbnailData)
+
+ assertThat(loader.loadThumbnail(taskId)).isEqualTo(thumbnailData)
+ }
+
+ @Test
+ fun captureThumbnail_emptyThumbnail_returnsNull() =
+ testScope.runTest {
+ val taskId = 321
+ val emptyThumbnailData = ThumbnailData()
+
+ whenever(activityManager.takeTaskThumbnail(taskId)).thenReturn(emptyThumbnailData)
+
+ assertThat(loader.captureThumbnail(taskId)).isNull()
+ }
+
+ @Test
+ fun captureThumbnail_thumbnailAvailable_returnsThumbnailData() =
+ testScope.runTest {
+ val taskId = 321
+ val thumbnailData = ThumbnailData(createTaskSnapshot())
+
+ whenever(activityManager.takeTaskThumbnail(taskId)).thenReturn(thumbnailData)
+
+ assertThat(loader.captureThumbnail(taskId)).isEqualTo(thumbnailData)
+ }
+
+ private fun createTaskSnapshot() =
+ TaskSnapshot(
+ /* id= */ 123,
+ /* captureTime= */ 0,
+ /* topActivityComponent= */ ComponentName("package", "class"),
+ /* snapshot= */ HardwareBuffer.create(
+ /* width= */ 100,
+ /* height= */ 100,
+ HardwareBuffer.RGBA_8888,
+ /* layers= */ 1,
+ /* usage= */ HardwareBuffer.USAGE_CPU_READ_OFTEN
+ ),
+ ColorSpace.get(ColorSpace.Named.SRGB),
+ Configuration.ORIENTATION_PORTRAIT,
+ Surface.ROTATION_0,
+ /* taskSize= */ Point(100, 100),
+ /* contentInsets= */ Rect(),
+ /* letterboxInsets= */ Rect(),
+ /* isLowResolution= */ false,
+ /* isRealSnapshot= */ true,
+ WindowConfiguration.WINDOWING_MODE_FULLSCREEN,
+ /* appearance= */ 0,
+ /* isTranslucent= */ false,
+ /* hasImeSurface= */ false
+ )
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
index d35a212..2c7ee56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
@@ -11,7 +11,7 @@
import com.android.wm.shell.recents.RecentTasks
import com.android.wm.shell.util.GroupedRecentTaskInfo
import com.google.common.truth.Truth.assertThat
-import java.util.*
+import java.util.Optional
import java.util.function.Consumer
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
@@ -52,12 +52,7 @@
val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
- assertThat(result)
- .containsExactly(
- createRecentTask(taskId = 1),
- createRecentTask(taskId = 2),
- createRecentTask(taskId = 3),
- )
+ assertThat(result.map { it.taskId }).containsExactly(1, 2, 3).inOrder()
}
@Test
@@ -66,8 +61,7 @@
val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
- assertThat(result)
- .containsExactly(createRecentTask(taskId = 1), createRecentTask(taskId = 2))
+ assertThat(result.map { it.taskId }).containsExactly(1, 2).inOrder()
}
@Test
@@ -81,15 +75,46 @@
val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
- assertThat(result)
- .containsExactly(
- createRecentTask(taskId = 1),
- createRecentTask(taskId = 2),
- createRecentTask(taskId = 3),
- createRecentTask(taskId = 4),
- createRecentTask(taskId = 5),
- createRecentTask(taskId = 6),
- )
+ assertThat(result.map { it.taskId }).containsExactly(1, 2, 3, 4, 5, 6).inOrder()
+ }
+
+ @Test
+ fun loadRecentTasks_singleTask_returnsTaskAsNotForeground() {
+ givenRecentTasks(
+ createSingleTask(taskId = 1),
+ )
+
+ val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
+
+ assertThat(result[0].isForegroundTask).isFalse()
+ }
+
+ @Test
+ fun loadRecentTasks_multipleTasks_returnsSecondTaskAsForegroundTask() {
+ givenRecentTasks(
+ createSingleTask(taskId = 1),
+ createSingleTask(taskId = 2),
+ createSingleTask(taskId = 3),
+ )
+
+ val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
+
+ assertThat(result.map { it.isForegroundTask }).containsExactly(false, true, false).inOrder()
+ }
+
+ @Test
+ fun loadRecentTasks_secondTaskIsGrouped_marksBothGroupedTasksAsForeground() {
+ givenRecentTasks(
+ createSingleTask(taskId = 1),
+ createTaskPair(taskId1 = 2, taskId2 = 3),
+ createSingleTask(taskId = 4),
+ )
+
+ val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
+
+ assertThat(result.map { it.isForegroundTask })
+ .containsExactly(false, true, true, false)
+ .inOrder()
}
@Suppress("UNCHECKED_CAST")
@@ -106,7 +131,8 @@
userId = 0,
topActivityComponent = null,
baseIntentComponent = null,
- colorBackground = null
+ colorBackground = null,
+ isForegroundTask = false,
)
private fun createSingleTask(taskId: Int): GroupedRecentTaskInfo =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
index 8019fb5..6248bb1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
@@ -57,7 +57,7 @@
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.util.NotificationChannels;
-import com.android.systemui.util.settings.FakeSettings;
+import com.android.systemui.util.settings.FakeGlobalSettings;
import com.android.systemui.util.settings.GlobalSettings;
import org.junit.Before;
@@ -77,7 +77,7 @@
public static final String FORMATTED_45M = "0h 45m";
public static final String FORMATTED_HOUR = "1h 0m";
private final NotificationManager mMockNotificationManager = mock(NotificationManager.class);
- private final GlobalSettings mGlobalSettings = new FakeSettings();
+ private final GlobalSettings mGlobalSettings = new FakeGlobalSettings();
private PowerNotificationWarnings mPowerNotificationWarnings;
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/SettingObserverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/UserSettingObserverTest.kt
similarity index 88%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/SettingObserverTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/qs/UserSettingObserverTest.kt
index 4be6890..8f06fe2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/SettingObserverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/UserSettingObserverTest.kt
@@ -35,7 +35,7 @@
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
-class SettingObserverTest : SysuiTestCase() {
+class UserSettingObserverTest : SysuiTestCase() {
companion object {
private const val TEST_SETTING = "setting"
@@ -46,7 +46,7 @@
}
private lateinit var testableLooper: TestableLooper
- private lateinit var setting: SettingObserver
+ private lateinit var setting: UserSettingObserver
private lateinit var secureSettings: SecureSettings
private lateinit var callback: Callback
@@ -56,17 +56,19 @@
testableLooper = TestableLooper.get(this)
secureSettings = FakeSettings()
- setting = object : SettingObserver(
- secureSettings,
- Handler(testableLooper.looper),
- TEST_SETTING,
- USER,
- DEFAULT_VALUE
- ) {
- override fun handleValueChanged(value: Int, observedChange: Boolean) {
- callback(value, observedChange)
+ setting =
+ object :
+ UserSettingObserver(
+ secureSettings,
+ Handler(testableLooper.looper),
+ TEST_SETTING,
+ USER,
+ DEFAULT_VALUE
+ ) {
+ override fun handleValueChanged(value: Int, observedChange: Boolean) {
+ callback(value, observedChange)
+ }
}
- }
// Default empty callback
callback = { _, _ -> Unit }
@@ -162,4 +164,4 @@
setting.isListening = true
assertThat(setting.value).isEqualTo(DEFAULT_VALUE)
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
index 67587e3..6cc52d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
@@ -29,12 +29,14 @@
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.ActivityManager;
import android.app.compat.CompatChanges;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -73,16 +75,18 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class TileLifecycleManagerTest extends SysuiTestCase {
- private static final int TEST_FAIL_TIMEOUT = 5000;
private final PackageManagerAdapter mMockPackageManagerAdapter =
mock(PackageManagerAdapter.class);
private final BroadcastDispatcher mMockBroadcastDispatcher =
mock(BroadcastDispatcher.class);
private final IQSTileService.Stub mMockTileService = mock(IQSTileService.Stub.class);
+ private final ActivityManager mActivityManager = mock(ActivityManager.class);
+
private ComponentName mTileServiceComponentName;
private Intent mTileServiceIntent;
private UserHandle mUser;
+ private FakeSystemClock mClock;
private FakeExecutor mExecutor;
private HandlerThread mThread;
private Handler mHandler;
@@ -112,13 +116,15 @@
mThread = new HandlerThread("TestThread");
mThread.start();
mHandler = Handler.createAsync(mThread.getLooper());
- mExecutor = new FakeExecutor(new FakeSystemClock());
+ mClock = new FakeSystemClock();
+ mExecutor = new FakeExecutor(mClock);
mStateManager = new TileLifecycleManager(mHandler, mWrappedContext,
mock(IQSService.class),
mMockPackageManagerAdapter,
mMockBroadcastDispatcher,
mTileServiceIntent,
mUser,
+ mActivityManager,
mExecutor);
}
@@ -294,11 +300,32 @@
mStateManager.onStartListening();
mStateManager.executeSetBindService(true);
mExecutor.runAllReady();
- mStateManager.setBindRetryDelay(0);
+ mStateManager.onServiceDisconnected(mTileServiceComponentName);
+ mClock.advanceTime(5000);
+
+ // Two calls: one for the first bind, one for the restart.
+ verifyBind(2);
+ verify(mMockTileService, times(2)).onStartListening();
+ }
+
+ @Test
+ public void testKillProcessLowMemory() throws Exception {
+ doAnswer(invocation -> {
+ ActivityManager.MemoryInfo memoryInfo = invocation.getArgument(0);
+ memoryInfo.lowMemory = true;
+ return null;
+ }).when(mActivityManager).getMemoryInfo(any());
+ mStateManager.onStartListening();
+ mStateManager.executeSetBindService(true);
mExecutor.runAllReady();
mStateManager.onServiceDisconnected(mTileServiceComponentName);
- mExecutor.runAllReady();
+ // Longer delay than a regular one
+ mClock.advanceTime(5000);
+ verifyBind(1);
+ verify(mMockTileService, times(1)).onStartListening();
+
+ mClock.advanceTime(20000);
// Two calls: one for the first bind, one for the restart.
verifyBind(2);
verify(mMockTileService, times(2)).onStartListening();
@@ -319,6 +346,7 @@
mMockBroadcastDispatcher,
mTileServiceIntent,
mUser,
+ mActivityManager,
mExecutor);
manager.executeSetBindService(true);
@@ -340,6 +368,7 @@
mMockBroadcastDispatcher,
mTileServiceIntent,
mUser,
+ mActivityManager,
mExecutor);
manager.executeSetBindService(true);
@@ -361,6 +390,7 @@
mMockBroadcastDispatcher,
mTileServiceIntent,
mUser,
+ mActivityManager,
mExecutor);
manager.executeSetBindService(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
index 4bc16a5..d011821 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
@@ -304,7 +304,7 @@
CustomTileAddedRepository customTileAddedRepository, DelayableExecutor executor) {
super(host, handlerProvider, broadcastDispatcher, userTracker, keyguardStateController,
commandQueue, statusBarIconController, panelInteractor,
- customTileAddedRepository, executor);
+ mTileLifecycleManagerFactory, customTileAddedRepository, executor);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
index 4ada44c..9b1f830 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
@@ -25,7 +25,6 @@
import androidx.test.filters.SmallTest
import com.android.settingslib.Utils
import com.android.settingslib.drawable.UserIconDrawable
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.common.shared.model.ContentDescription
@@ -35,6 +34,7 @@
import com.android.systemui.qs.QSSecurityFooterUtils
import com.android.systemui.qs.footer.FooterActionsTestUtils
import com.android.systemui.qs.footer.domain.model.SecurityButtonConfig
+import com.android.systemui.res.R
import com.android.systemui.security.data.model.SecurityModel
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.statusbar.policy.FakeSecurityController
@@ -44,7 +44,7 @@
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.nullable
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.FakeGlobalSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.launch
@@ -128,7 +128,7 @@
fun userSwitcher() = runTest {
val picture: Drawable = mock()
val userInfoController = FakeUserInfoController(FakeInfo(picture = picture))
- val settings = FakeSettings()
+ val settings = FakeGlobalSettings()
val userId = 42
val userTracker = FakeUserTracker(userId)
val userSwitcherControllerWrapper =
@@ -167,14 +167,9 @@
// for the current user will be fired to notify us of that change.
isUserSwitcherEnabled = true
- // Update the setting for a random user: nothing should change, given that at this point we
- // weren't notified of the change yet.
- utils.setUserSwitcherEnabled(settings, true, 3)
- assertThat(currentUserSwitcher()).isNull()
-
// Update the setting for the observed user: now we will be notified and the button should
// be there.
- utils.setUserSwitcherEnabled(settings, true, userId)
+ utils.setUserSwitcherEnabled(settings, true)
val userSwitcher = currentUserSwitcher()
assertThat(userSwitcher).isNotNull()
assertThat(userSwitcher!!.icon)
@@ -372,9 +367,7 @@
),
)
- val job = launch {
- underTest.observeDeviceMonitoringDialogRequests(quickSettingsContext = mock())
- }
+ val job = launch { underTest.observeDeviceMonitoringDialogRequests(mock()) }
advanceUntilIdle()
assertThat(nDialogRequests).isEqualTo(3)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt
index a0ff2ab..dcda005 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt
@@ -32,6 +32,7 @@
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.test.TestCoroutineScheduler
@@ -95,8 +96,7 @@
testScope.backgroundScope,
dispatcher,
)
- `when`(deviceItemInteractor.deviceItemUpdate)
- .thenReturn(MutableStateFlow(null).asStateFlow())
+ `when`(deviceItemInteractor.deviceItemUpdate).thenReturn(MutableSharedFlow())
`when`(bluetoothStateInteractor.bluetoothStateUpdate)
.thenReturn(MutableStateFlow(null).asStateFlow())
`when`(deviceItemInteractor.deviceItemUpdateRequest)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt
index 3593075..428f79c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt
@@ -27,10 +27,11 @@
import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
@@ -78,7 +79,7 @@
@Before
fun setUp() {
- dispatcher = StandardTestDispatcher()
+ dispatcher = UnconfinedTestDispatcher()
testScope = TestScope(dispatcher)
interactor =
DeviceItemInteractor(
@@ -107,9 +108,10 @@
listOf(createFactory({ true }, deviceItem1))
)
+ val latest by collectLastValue(interactor.deviceItemUpdate)
interactor.updateDeviceItems(mContext)
- assertThat(interactor.deviceItemUpdate.value).isEmpty()
+ assertThat(latest).isEqualTo(emptyList<DeviceItem>())
}
}
@@ -121,9 +123,10 @@
listOf(createFactory({ false }, deviceItem1))
)
+ val latest by collectLastValue(interactor.deviceItemUpdate)
interactor.updateDeviceItems(mContext)
- assertThat(interactor.deviceItemUpdate.value).isEmpty()
+ assertThat(latest).isEqualTo(emptyList<DeviceItem>())
}
}
@@ -135,10 +138,10 @@
listOf(createFactory({ true }, deviceItem1))
)
+ val latest by collectLastValue(interactor.deviceItemUpdate)
interactor.updateDeviceItems(mContext)
- assertThat(interactor.deviceItemUpdate.value).hasSize(1)
- assertThat(interactor.deviceItemUpdate.value!![0]).isEqualTo(deviceItem1)
+ assertThat(latest).isEqualTo(listOf(deviceItem1))
}
}
@@ -150,11 +153,10 @@
listOf(createFactory({ false }, deviceItem1), createFactory({ true }, deviceItem2))
)
+ val latest by collectLastValue(interactor.deviceItemUpdate)
interactor.updateDeviceItems(mContext)
- assertThat(interactor.deviceItemUpdate.value).hasSize(2)
- assertThat(interactor.deviceItemUpdate.value!![0]).isEqualTo(deviceItem2)
- assertThat(interactor.deviceItemUpdate.value!![1]).isEqualTo(deviceItem2)
+ assertThat(latest).isEqualTo(listOf(deviceItem2, deviceItem2))
}
}
@@ -177,10 +179,10 @@
`when`(deviceItem1.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
`when`(deviceItem2.type).thenReturn(DeviceItemType.SAVED_BLUETOOTH_DEVICE)
+ val latest by collectLastValue(interactor.deviceItemUpdate)
interactor.updateDeviceItems(mContext)
- assertThat(interactor.deviceItemUpdate.value)
- .isEqualTo(listOf(deviceItem2, deviceItem1))
+ assertThat(latest).isEqualTo(listOf(deviceItem2, deviceItem1))
}
}
@@ -200,10 +202,10 @@
`when`(deviceItem1.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
`when`(deviceItem2.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
+ val latest by collectLastValue(interactor.deviceItemUpdate)
interactor.updateDeviceItems(mContext)
- assertThat(interactor.deviceItemUpdate.value)
- .isEqualTo(listOf(deviceItem2, deviceItem1))
+ assertThat(latest).isEqualTo(listOf(deviceItem2, deviceItem1))
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/retail/data/repository/RetailModeSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/retail/data/repository/RetailModeSettingsRepositoryTest.kt
index 421c355..fe80f70 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/retail/data/repository/RetailModeSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/retail/data/repository/RetailModeSettingsRepositoryTest.kt
@@ -21,7 +21,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.FakeGlobalSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
@@ -36,7 +36,7 @@
@RunWith(AndroidTestingRunner::class)
class RetailModeSettingsRepositoryTest : SysuiTestCase() {
- private val globalSettings = FakeSettings()
+ private val globalSettings = FakeGlobalSettings()
private val testDispatcher = StandardTestDispatcher()
private val testScope = TestScope(testDispatcher)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 2e16577..61dd69a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -52,7 +52,6 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
@@ -463,8 +462,7 @@
fromScene = getCurrentSceneInUi(),
toScene = to.key,
progress = progressFlow,
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
runCurrent()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
index 740c6d9..432bd0f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
@@ -29,7 +29,6 @@
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -120,8 +119,7 @@
fromScene = SceneKey.Lockscreen,
toScene = SceneKey.Shade,
progress = progress,
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
assertThat(reflectedTransitionState).isEqualTo(transitionState.value)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
index 31d26c0..8b23d18 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
@@ -83,8 +83,7 @@
fromScene = SceneKey.Lockscreen,
toScene = SceneKey.Shade,
progress = progress,
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
assertThat(reflectedTransitionState).isEqualTo(transitionState.value)
@@ -122,8 +121,7 @@
fromScene = underTest.desiredScene.value.key,
toScene = SceneKey.Shade,
progress = progress,
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
assertThat(transitionTo).isEqualTo(SceneKey.Shade)
@@ -160,8 +158,7 @@
fromScene = SceneKey.Gone,
toScene = SceneKey.Lockscreen,
progress = flowOf(0.5f),
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
)
val transitioning by
@@ -180,8 +177,7 @@
fromScene = SceneKey.Shade,
toScene = SceneKey.QuickSettings,
progress = flowOf(0.5f),
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
)
underTest.setTransitionState(transitionState)
@@ -198,8 +194,7 @@
fromScene = SceneKey.Shade,
toScene = SceneKey.Lockscreen,
progress = flowOf(0.5f),
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
)
val transitioning by
@@ -227,8 +222,7 @@
fromScene = SceneKey.Shade,
toScene = SceneKey.Lockscreen,
progress = flowOf(0.5f),
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
assertThat(transitioning).isTrue()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index 3b9621e..7b13de6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -109,8 +109,7 @@
fromScene = SceneKey.Gone,
toScene = SceneKey.Shade,
progress = flowOf(0.5f),
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
assertThat(isVisible).isTrue()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Shade), "reason")
@@ -123,8 +122,7 @@
fromScene = SceneKey.Shade,
toScene = SceneKey.Gone,
progress = flowOf(0.5f),
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
assertThat(isVisible).isTrue()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone), "reason")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
index bcb060d..81382a4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
@@ -60,7 +60,6 @@
import dagger.Component
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
@@ -595,8 +594,7 @@
fromScene = SceneKey.Lockscreen,
toScene = key,
progress = progress,
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
)
sceneInteractor.setTransitionState(transitionState)
@@ -633,8 +631,7 @@
fromScene = key,
toScene = SceneKey.Lockscreen,
progress = progress,
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
)
sceneInteractor.setTransitionState(transitionState)
@@ -670,8 +667,7 @@
fromScene = SceneKey.Lockscreen,
toScene = SceneKey.Shade,
progress = progress,
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
)
sceneInteractor.setTransitionState(transitionState)
@@ -947,8 +943,7 @@
fromScene = SceneKey.Lockscreen,
toScene = key,
progress = progress,
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
)
sceneInteractor.setTransitionState(transitionState)
@@ -985,8 +980,7 @@
fromScene = SceneKey.Lockscreen,
toScene = key,
progress = progress,
- isInitiatedByUserInput = true,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = true,
)
)
sceneInteractor.setTransitionState(transitionState)
@@ -1023,8 +1017,7 @@
fromScene = key,
toScene = SceneKey.Lockscreen,
progress = progress,
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
)
sceneInteractor.setTransitionState(transitionState)
@@ -1061,8 +1054,7 @@
fromScene = key,
toScene = SceneKey.Lockscreen,
progress = progress,
- isInitiatedByUserInput = true,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = true,
)
)
sceneInteractor.setTransitionState(transitionState)
@@ -1097,9 +1089,8 @@
ObservableTransitionState.Transition(
fromScene = SceneKey.Lockscreen,
toScene = SceneKey.QuickSettings,
- progress = MutableStateFlow(0f),
- isInitiatedByUserInput = true,
- isUserInputOngoing = flowOf(false),
+ progress = progress,
+ isUserInputDriven = true,
)
)
sceneInteractor.setTransitionState(transitionState)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
index bb20d94..607cdab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
@@ -17,7 +17,6 @@
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -85,8 +84,7 @@
fromScene = SceneKey.Shade,
toScene = SceneKey.QuickSettings,
progress = MutableStateFlow(0.5f),
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
)
)
@@ -104,8 +102,7 @@
fromScene = SceneKey.QuickSettings,
toScene = SceneKey.Shade,
progress = MutableStateFlow(0.5f),
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
)
)
@@ -123,8 +120,7 @@
fromScene = SceneKey.Gone,
toScene = SceneKey.Shade,
progress = MutableStateFlow(0.5f),
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
+ isUserInputDriven = false,
)
)
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
index 8f39ee6..83f2a5d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
@@ -62,11 +62,15 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.settings.FakeGlobalSettings;
import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.settings.GlobalSettings;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.utils.os.FakeHandler;
+import dagger.BindsInstance;
+import dagger.Component;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -77,9 +81,6 @@
import java.util.Map;
import java.util.function.Consumer;
-import dagger.BindsInstance;
-import dagger.Component;
-
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -93,7 +94,8 @@
@Mock private HighPriorityProvider mHighPriorityProvider;
@Mock private SysuiStatusBarStateController mStatusBarStateController;
@Mock private UserTracker mUserTracker;
- private final FakeSettings mFakeSettings = new FakeSettings();
+ private final FakeSettings mSecureSettings = new FakeSettings();
+ private final FakeGlobalSettings mGlobalSettings = new FakeGlobalSettings();
private KeyguardNotificationVisibilityProvider mKeyguardNotificationVisibilityProvider;
private NotificationEntry mEntry;
@@ -113,8 +115,8 @@
mHighPriorityProvider,
mStatusBarStateController,
mUserTracker,
- mFakeSettings,
- mFakeSettings);
+ mSecureSettings,
+ mGlobalSettings);
mKeyguardNotificationVisibilityProvider = component.getProvider();
for (CoreStartable startable : component.getCoreStartables().values()) {
startable.start();
@@ -223,7 +225,7 @@
Consumer<String> listener = mock(Consumer.class);
mKeyguardNotificationVisibilityProvider.addOnStateChangedListener(listener);
- mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true);
+ mSecureSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true);
verify(listener).accept(anyString());
}
@@ -234,7 +236,7 @@
Consumer<String> listener = mock(Consumer.class);
mKeyguardNotificationVisibilityProvider.addOnStateChangedListener(listener);
- mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, true);
+ mSecureSettings.putBool(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, true);
verify(listener).accept(anyString());
}
@@ -242,8 +244,8 @@
@Test
public void hideSilentNotificationsPerUserSettingWithHighPriorityParent() {
when(mStatusBarStateController.getCurrentOrUpcomingState()).thenReturn(KEYGUARD);
- mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true);
- mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, false);
+ mSecureSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true);
+ mSecureSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, false);
GroupEntry parent = new GroupEntryBuilder()
.setKey("parent")
.addChild(mEntry)
@@ -264,8 +266,8 @@
@Test
public void keyguardShowing_hideSilentNotifications_perUserSetting() {
when(mStatusBarStateController.getCurrentOrUpcomingState()).thenReturn(KEYGUARD);
- mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true);
- mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, false);
+ mSecureSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true);
+ mSecureSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, false);
mEntry = new NotificationEntryBuilder()
.setUser(new UserHandle(NOTIF_USER_ID))
.setImportance(IMPORTANCE_LOW)
@@ -277,8 +279,8 @@
@Test
public void keyguardShowing_hideSilentNotifications_perUserSetting_withHighPriorityParent() {
when(mKeyguardStateController.isShowing()).thenReturn(true);
- mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true);
- mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, false);
+ mSecureSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true);
+ mSecureSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, false);
GroupEntry parent = new GroupEntryBuilder()
.setKey("parent")
.addChild(mEntry)
@@ -300,10 +302,10 @@
public void hideSilentOnLockscreenSetting() {
// GIVEN an 'unfiltered-keyguard-showing' state and notifications shown on lockscreen
setupUnfilteredState(mEntry);
- mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true);
+ mSecureSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true);
// WHEN the show silent notifs on lockscreen setting is false
- mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, false);
+ mSecureSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, false);
// WHEN the notification is not high priority and not ambient
mEntry = new NotificationEntryBuilder()
@@ -319,10 +321,10 @@
public void showSilentOnLockscreenSetting() {
// GIVEN an 'unfiltered-keyguard-showing' state and notifications shown on lockscreen
setupUnfilteredState(mEntry);
- mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true);
+ mSecureSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true);
// WHEN the show silent notifs on lockscreen setting is true
- mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, true);
+ mSecureSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, true);
// WHEN the notification is not high priority and not ambient
mEntry = new NotificationEntryBuilder()
@@ -338,7 +340,7 @@
public void defaultSilentOnLockscreenSettingIsHide() {
// GIVEN an 'unfiltered-keyguard-showing' state and notifications shown on lockscreen
setupUnfilteredState(mEntry);
- mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true);
+ mSecureSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true);
// WHEN the notification is not high priority and not ambient
mEntry = new NotificationEntryBuilder()
@@ -348,7 +350,8 @@
when(mHighPriorityProvider.isExplicitlyHighPriority(any())).thenReturn(false);
// WhHEN the show silent notifs on lockscreen setting is unset
- assertNull(mFakeSettings.getString(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS));
+ assertNull(
+ mSecureSettings.getString(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS));
assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry));
}
@@ -359,7 +362,7 @@
Consumer<String> listener = mock(Consumer.class);
mKeyguardNotificationVisibilityProvider.addOnStateChangedListener(listener);
- mFakeSettings.putBool(Settings.Global.ZEN_MODE, true);
+ mGlobalSettings.putBool(Settings.Global.ZEN_MODE, true);
verify(listener).accept(anyString());
}
@@ -370,7 +373,7 @@
Consumer<String> listener = mock(Consumer.class);
mKeyguardNotificationVisibilityProvider.addOnStateChangedListener(listener);
- mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, true);
+ mSecureSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, true);
verify(listener).accept(anyString());
}
@@ -470,8 +473,8 @@
public void highPriorityCharacteristicsIgnored() {
// GIVEN an 'unfiltered-keyguard-showing' state with silent notifications hidden
setupUnfilteredState(mEntry);
- mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true);
- mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, false);
+ mSecureSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, true);
+ mSecureSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, false);
// WHEN the notification doesn't exceed the threshold to show on the lockscreen, but does
// have the "high priority characteristics" that would promote it to high priority
@@ -557,7 +560,7 @@
.build());
// WHEN its parent does exceed threshold tot show on the lockscreen
- mFakeSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, false);
+ mSecureSettings.putBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, false);
when(mHighPriorityProvider.isExplicitlyHighPriority(parent)).thenReturn(true);
// THEN filter out the entry regardless of parent
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/StackStateLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/StackStateLoggerTest.kt
deleted file mode 100644
index 47c5e5b..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/StackStateLoggerTest.kt
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.logging
-
-import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogcatEchoTracker
-import com.android.systemui.log.core.LogLevel
-import com.android.systemui.statusbar.notification.stack.StackStateLogger
-import com.google.common.truth.Truth
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidTestingRunner::class)
-@SmallTest
-class StackStateLoggerTest : SysuiTestCase() {
- private val logBufferCounter = LogBufferCounter()
- private lateinit var logger: StackStateLogger
-
- @Before
- fun setup() {
- logger = StackStateLogger(logBufferCounter.logBuffer, logBufferCounter.logBuffer)
- }
-
- @Test
- fun groupChildRemovalEvent() {
- logger.groupChildRemovalEventProcessed(KEY)
- verifyDidLog(1)
- logger.groupChildRemovalAnimationEnded(KEY)
- verifyDidLog(1)
- }
-
- class LogBufferCounter {
- val recentLogs = mutableListOf<Pair<String, LogLevel>>()
- val tracker =
- object : LogcatEchoTracker {
- override val logInBackgroundThread: Boolean = false
- override fun isBufferLoggable(bufferName: String, level: LogLevel): Boolean = false
- override fun isTagLoggable(tagName: String, level: LogLevel): Boolean {
- recentLogs.add(tagName to level)
- return true
- }
- }
- val logBuffer =
- LogBuffer(name = "test", maxSize = 1, logcatEchoTracker = tracker, systrace = false)
-
- fun verifyDidLog(times: Int) {
- Truth.assertThat(recentLogs).hasSize(times)
- recentLogs.clear()
- }
- }
-
- private fun verifyDidLog(times: Int) {
- logBufferCounter.verifyDidLog(times)
- }
-
- companion object {
- private val KEY = "PACKAGE_NAME"
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapperTest.kt
index 8c3bfd5..f7632aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapperTest.kt
@@ -30,9 +30,12 @@
import androidx.test.filters.SmallTest
import com.android.internal.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestUiOffloadThread
+import com.android.systemui.UiOffloadThread
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.NotificationTestHelper
import com.android.systemui.statusbar.notification.row.wrapper.NotificationTemplateViewWrapper.ActionPendingIntentCancellationHandler
+import com.android.systemui.util.leak.ReferenceTestUtils.waitForCondition
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -60,6 +63,12 @@
fun setUp() {
looper = TestableLooper.get(this)
allowTestableLooperAsMainThread()
+ // Use main thread instead of UI offload thread to fix flakes.
+ mDependency.injectTestDependency(
+ UiOffloadThread::class.java,
+ TestUiOffloadThread(looper.looper)
+ )
+
helper = NotificationTestHelper(mContext, mDependency, looper)
row = helper.createRow()
// Some code in the view iterates through parents so we need some extra containers around
@@ -88,12 +97,11 @@
val action2 = createActionWithPendingIntent()
val action3 = createActionWithPendingIntent()
wrapper.onContentUpdated(row)
- waitForUiOffloadThread() // Wait for cancellation registration to execute.
val pi3 = getPendingIntent(action3)
pi3.cancel()
- looper.processAllMessages() // Wait for listener callbacks to execute
+ waitForActionDisabled(action3)
assertThat(action1.isEnabled).isTrue()
assertThat(action2.isEnabled).isTrue()
assertThat(action3.isEnabled).isFalse()
@@ -109,12 +117,12 @@
val wrapper = NotificationTemplateViewWrapper(mContext, view, row)
val action = createActionWithPendingIntent()
wrapper.onContentUpdated(row)
- waitForUiOffloadThread() // Wait for cancellation registration to execute.
// Cancel the intent and check action is now false.
val pi = getPendingIntent(action)
pi.cancel()
- looper.processAllMessages() // Wait for listener callbacks to execute
+
+ waitForActionDisabled(action)
assertThat(action.isEnabled).isFalse()
// Create a NEW action and make sure that one will also be cancelled with same PI.
@@ -134,12 +142,13 @@
val action2 = createActionWithPendingIntent()
val action3 = createActionWithPendingIntent(getPendingIntent(action2))
wrapper.onContentUpdated(row)
- waitForUiOffloadThread() // Wait for cancellation registration to execute.
+ looper.processAllMessages()
val pi = getPendingIntent(action2)
pi.cancel()
- looper.processAllMessages() // Wait for listener callbacks to execute
+ waitForActionDisabled(action2)
+ waitForActionDisabled(action3)
assertThat(action1.isEnabled).isTrue()
assertThat(action2.isEnabled).isFalse()
assertThat(action3.isEnabled).isFalse()
@@ -152,10 +161,12 @@
val action = createActionWithPendingIntent()
wrapper.onContentUpdated(row)
getPendingIntent(action).cancel()
- ViewUtils.attachView(root)
- waitForUiOffloadThread()
looper.processAllMessages()
+ ViewUtils.attachView(root)
+ looper.processAllMessages()
+
+ waitForActionDisabled(action)
assertThat(action.isEnabled).isFalse()
}
@@ -173,7 +184,6 @@
val wrapper = NotificationTemplateViewWrapper(mContext, view, row)
wrapper.onContentUpdated(row)
ViewUtils.detachView(root)
- waitForUiOffloadThread()
looper.processAllMessages()
val captor = ArgumentCaptor.forClass(CancelListener::class.java)
@@ -194,7 +204,6 @@
val action = createActionWithPendingIntent(spy)
val wrapper = NotificationTemplateViewWrapper(mContext, view, row)
wrapper.onContentUpdated(row)
- waitForUiOffloadThread()
looper.processAllMessages()
// Grab set attach listener
@@ -213,7 +222,6 @@
)
action.setTagInternal(R.id.pending_intent_tag, newPi)
wrapper.onContentUpdated(row)
- waitForUiOffloadThread()
looper.processAllMessages()
// Listeners for original pending intent need to be cleaned up now.
@@ -251,4 +259,11 @@
assertThat(pendingIntent).isNotNull()
return pendingIntent
}
+
+ private fun waitForActionDisabled(action: View) {
+ waitForCondition {
+ looper.processAllMessages()
+ !action.isEnabled
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index a2be8b0..033c96a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -164,6 +164,7 @@
mFeatureFlags.setDefault(Flags.ANIMATED_NOTIFICATION_SHADE_INSETS);
mFeatureFlags.setDefault(Flags.NOTIFICATION_SHELF_REFACTOR);
mFeatureFlags.setDefault(Flags.NEW_AOD_TRANSITION);
+ mFeatureFlags.setDefault(Flags.UNCLEARED_TRANSIENT_HUN_FIX);
// Inject dependencies before initializing the layout
mDependency.injectTestDependency(FeatureFlags.class, mFeatureFlags);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index 416694b..1d8a346 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -50,15 +50,15 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dagger.NightDisplayListenerModule;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.AutoAddTracker;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.ReduceBrightColorsController;
-import com.android.systemui.qs.SettingObserver;
+import com.android.systemui.qs.UserSettingObserver;
import com.android.systemui.qs.external.CustomTile;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.CastDevice;
import com.android.systemui.statusbar.policy.DataSaverController;
@@ -288,7 +288,7 @@
inOrderSafety.verify(mSafetyController).removeCallback(any());
inOrderSafety.verify(mSafetyController).addCallback(any());
- SettingObserver setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING);
+ UserSettingObserver setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING);
assertEquals(USER + 1, setting.getCurrentUser());
assertTrue(setting.isListening());
}
@@ -342,7 +342,7 @@
inOrderSafety.verify(mSafetyController).removeCallback(any());
inOrderSafety.verify(mSafetyController).addCallback(any());
- SettingObserver setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING);
+ UserSettingObserver setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING);
assertEquals(USER + 1, setting.getCurrentUser());
assertFalse(setting.isListening());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index f18af61..c8cbe42 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -18,6 +18,8 @@
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
+import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
+import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
@@ -1110,6 +1112,16 @@
// THEN no NPE when fingerprintManager is null
}
+ @Test
+ public void bubbleBarVisibility() {
+ createCentralSurfaces();
+ mCentralSurfaces.onStatusBarWindowStateChanged(WINDOW_STATE_HIDDEN);
+ verify(mBubbles).onStatusBarVisibilityChanged(false);
+
+ mCentralSurfaces.onStatusBarWindowStateChanged(WINDOW_STATE_SHOWING);
+ verify(mBubbles).onStatusBarVisibilityChanged(true);
+ }
+
/**
* Configures the appropriate mocks and then calls {@link CentralSurfacesImpl#updateIsKeyguard}
* to reconfigure the keyguard to reflect the requested showing/occluded states.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index b36d09d..45e9224 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -19,8 +19,6 @@
import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN;
import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE;
-import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher;
-
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -37,6 +35,8 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher;
+
import android.service.trust.TrustAgentService;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -175,7 +175,6 @@
mFeatureFlags = new FakeFeatureFlags();
mFeatureFlags.set(Flags.WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM, true);
mFeatureFlags.set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false);
- mFeatureFlags.set(Flags.UDFPS_NEW_TOUCH_DETECTION, true);
mFeatureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false);
mFeatureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepositoryImplTest.kt
index d35ce76..8ecf6f8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/AirplaneModeRepositoryImplTest.kt
@@ -18,12 +18,11 @@
import android.os.Handler
import android.os.Looper
-import android.os.UserHandle
import android.provider.Settings.Global
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.table.TableLogBuffer
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.FakeGlobalSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -48,15 +47,14 @@
@Mock private lateinit var logger: TableLogBuffer
private lateinit var bgHandler: Handler
private lateinit var scope: CoroutineScope
- private lateinit var settings: FakeSettings
+ private lateinit var settings: FakeGlobalSettings
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
bgHandler = Handler(Looper.getMainLooper())
scope = CoroutineScope(IMMEDIATE)
- settings = FakeSettings()
- settings.userId = UserHandle.USER_ALL
+ settings = FakeGlobalSettings()
underTest =
AirplaneModeRepositoryImpl(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
index e761635..a1da1673 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
@@ -75,6 +75,8 @@
private BluetoothAdapter mMockAdapter;
private List<CachedBluetoothDevice> mDevices;
+ private FakeExecutor mBackgroundExecutor;
+
@Before
public void setup() throws Exception {
mTestableLooper = TestableLooper.get(this);
@@ -91,6 +93,7 @@
when(mMockBluetoothManager.getProfileManager())
.thenReturn(mock(LocalBluetoothProfileManager.class));
mMockDumpManager = mock(DumpManager.class);
+ mBackgroundExecutor = new FakeExecutor(new FakeSystemClock());
BluetoothRepository bluetoothRepository =
new FakeBluetoothRepository(mMockBluetoothManager);
@@ -101,6 +104,7 @@
mMockDumpManager,
mock(BluetoothLogger.class),
bluetoothRepository,
+ mBackgroundExecutor,
mTestableLooper.getLooper(),
mMockBluetoothManager,
mMockAdapter);
@@ -205,6 +209,7 @@
mBluetoothControllerImpl.onAclConnectionStateChanged(device,
BluetoothProfile.STATE_CONNECTED);
mBluetoothControllerImpl.onActiveDeviceChanged(device, BluetoothProfile.HEADSET);
+ mBackgroundExecutor.runAllReady();
assertTrue(mBluetoothControllerImpl.isBluetoothAudioActive());
assertTrue(mBluetoothControllerImpl.isBluetoothAudioProfileOnly());
@@ -290,6 +295,7 @@
BluetoothProfile.LE_AUDIO, /* isConnected= */ true, /* isActive= */ false);
mBluetoothControllerImpl.onDeviceAdded(device);
+ mBackgroundExecutor.runAllReady();
assertThat(mBluetoothControllerImpl.isBluetoothAudioProfileOnly()).isTrue();
}
@@ -300,6 +306,7 @@
BluetoothProfile.HEADSET, /* isConnected= */ true, /* isActive= */ false);
mBluetoothControllerImpl.onDeviceAdded(device);
+ mBackgroundExecutor.runAllReady();
assertThat(mBluetoothControllerImpl.isBluetoothAudioProfileOnly()).isTrue();
}
@@ -310,6 +317,7 @@
BluetoothProfile.A2DP, /* isConnected= */ true, /* isActive= */ false);
mBluetoothControllerImpl.onDeviceAdded(device);
+ mBackgroundExecutor.runAllReady();
assertThat(mBluetoothControllerImpl.isBluetoothAudioProfileOnly()).isTrue();
}
@@ -320,6 +328,7 @@
BluetoothProfile.HEARING_AID, /* isConnected= */ true, /* isActive= */ false);
mBluetoothControllerImpl.onDeviceAdded(device);
+ mBackgroundExecutor.runAllReady();
assertThat(mBluetoothControllerImpl.isBluetoothAudioProfileOnly()).isTrue();
}
@@ -337,6 +346,8 @@
mBluetoothControllerImpl.onDeviceAdded(device2);
mBluetoothControllerImpl.onDeviceAdded(device3);
+ mBackgroundExecutor.runAllReady();
+
assertThat(mBluetoothControllerImpl.isBluetoothAudioProfileOnly()).isTrue();
}
@@ -349,6 +360,7 @@
mBluetoothControllerImpl.onDeviceAdded(device1);
mBluetoothControllerImpl.onDeviceAdded(device2);
+ mBackgroundExecutor.runAllReady();
assertThat(mBluetoothControllerImpl.isBluetoothAudioProfileOnly()).isFalse();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt
index 6094135..361fa5b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt
@@ -28,6 +28,7 @@
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.settings.FakeGlobalSettings
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.util.time.FakeSystemClock
import com.android.systemui.util.wrapper.BuildInfo
@@ -67,19 +68,22 @@
private lateinit var mainExecutor: FakeExecutor
private lateinit var testableLooper: TestableLooper
- private lateinit var settings: FakeSettings
+ private lateinit var secureSettings: FakeSettings
+ private lateinit var globalSettings: FakeGlobalSettings
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
mainExecutor = FakeExecutor(FakeSystemClock())
- settings = FakeSettings()
+ secureSettings = FakeSettings()
+ globalSettings = FakeGlobalSettings()
`when`(userTracker.userId).thenReturn(START_USER)
whenever(buildInfo.isDebuggable).thenReturn(false)
controller = DeviceProvisionedControllerImpl(
- settings,
- settings,
+ secureSettings,
+ globalSettings,
userTracker,
dumpManager,
buildInfo,
@@ -108,7 +112,7 @@
@Test
fun testProvisionedWhenCreated() {
- settings.putInt(Settings.Global.DEVICE_PROVISIONED, 1)
+ globalSettings.putInt(Settings.Global.DEVICE_PROVISIONED, 1)
init()
assertThat(controller.isDeviceProvisioned).isTrue()
@@ -116,7 +120,7 @@
@Test
fun testFrpActiveWhenCreated() {
- settings.putInt(Settings.Secure.SECURE_FRP_MODE, 1)
+ globalSettings.putInt(Settings.Global.SECURE_FRP_MODE, 1)
init()
assertThat(controller.isFrpActive).isTrue()
@@ -124,7 +128,7 @@
@Test
fun testUserSetupWhenCreated() {
- settings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, START_USER)
+ secureSettings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, START_USER)
init()
assertThat(controller.isUserSetup(START_USER))
@@ -134,7 +138,7 @@
fun testDeviceProvisionedChange() {
init()
- settings.putInt(Settings.Global.DEVICE_PROVISIONED, 1)
+ globalSettings.putInt(Settings.Global.DEVICE_PROVISIONED, 1)
testableLooper.processAllMessages() // background observer
assertThat(controller.isDeviceProvisioned).isTrue()
@@ -144,7 +148,7 @@
fun testFrpActiveChange() {
init()
- settings.putInt(Settings.Secure.SECURE_FRP_MODE, 1)
+ globalSettings.putInt(Settings.Global.SECURE_FRP_MODE, 1)
testableLooper.processAllMessages() // background observer
assertThat(controller.isFrpActive).isTrue()
@@ -154,7 +158,7 @@
fun testUserSetupChange() {
init()
- settings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, START_USER)
+ secureSettings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, START_USER)
testableLooper.processAllMessages() // background observer
assertThat(controller.isUserSetup(START_USER)).isTrue()
@@ -165,7 +169,7 @@
init()
val otherUser = 10
- settings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, otherUser)
+ secureSettings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, otherUser)
testableLooper.processAllMessages() // background observer
assertThat(controller.isUserSetup(START_USER)).isFalse()
@@ -175,7 +179,7 @@
@Test
fun testCurrentUserSetup() {
val otherUser = 10
- settings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, otherUser)
+ secureSettings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, otherUser)
init()
assertThat(controller.isCurrentUserSetup).isFalse()
@@ -219,7 +223,7 @@
init()
controller.addCallback(listener)
- settings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, START_USER)
+ secureSettings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, START_USER)
testableLooper.processAllMessages()
mainExecutor.runAllReady()
@@ -234,7 +238,7 @@
init()
controller.addCallback(listener)
- settings.putInt(Settings.Global.DEVICE_PROVISIONED, 1)
+ globalSettings.putInt(Settings.Global.DEVICE_PROVISIONED, 1)
testableLooper.processAllMessages()
mainExecutor.runAllReady()
@@ -249,7 +253,7 @@
init()
controller.addCallback(listener)
- settings.putInt(Settings.Secure.SECURE_FRP_MODE, 1)
+ globalSettings.putInt(Settings.Global.SECURE_FRP_MODE, 1)
testableLooper.processAllMessages()
mainExecutor.runAllReady()
@@ -266,9 +270,9 @@
controller.removeCallback(listener)
switchUser(10)
- settings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, START_USER)
- settings.putInt(Settings.Global.DEVICE_PROVISIONED, 1)
- settings.putInt(Settings.Secure.SECURE_FRP_MODE, 1)
+ secureSettings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, START_USER)
+ globalSettings.putInt(Settings.Global.DEVICE_PROVISIONED, 1)
+ globalSettings.putInt(Settings.Global.SECURE_FRP_MODE, 1)
testableLooper.processAllMessages()
mainExecutor.runAllReady()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
index 66c5aaa..6825f65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
@@ -38,7 +38,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.ZenModeController.Callback;
-import com.android.systemui.util.settings.FakeSettings;
+import com.android.systemui.util.settings.FakeGlobalSettings;
import org.junit.Before;
import org.junit.Test;
@@ -67,7 +67,7 @@
UserTracker mUserTracker;
private ZenModeControllerImpl mController;
- private final FakeSettings mGlobalSettings = new FakeSettings();
+ private final FakeGlobalSettings mGlobalSettings = new FakeGlobalSettings();
@Before
public void setUp() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
index e249cec..0d78ae9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
@@ -29,7 +29,7 @@
import com.android.systemui.user.data.model.SelectedUserModel
import com.android.systemui.user.data.model.SelectionStatus
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.FakeGlobalSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -56,14 +56,14 @@
private lateinit var underTest: UserRepositoryImpl
- private lateinit var globalSettings: FakeSettings
+ private lateinit var globalSettings: FakeGlobalSettings
private lateinit var tracker: FakeUserTracker
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- globalSettings = FakeSettings()
+ globalSettings = FakeGlobalSettings()
tracker = FakeUserTracker()
}
@@ -282,20 +282,17 @@
com.android.internal.R.bool.config_expandLockScreenUserSwitcher,
true,
)
- globalSettings.putIntForUser(
+ globalSettings.putInt(
UserRepositoryImpl.SETTING_SIMPLE_USER_SWITCHER,
if (isSimpleUserSwitcher) 1 else 0,
- UserHandle.USER_SYSTEM,
)
- globalSettings.putIntForUser(
+ globalSettings.putInt(
Settings.Global.ADD_USERS_WHEN_LOCKED,
if (isAddUsersFromLockscreen) 1 else 0,
- UserHandle.USER_SYSTEM,
)
- globalSettings.putIntForUser(
+ globalSettings.putInt(
Settings.Global.USER_SWITCHER_ENABLED,
if (isUserSwitcherEnabled) 1 else 0,
- UserHandle.USER_SYSTEM,
)
}
diff --git a/packages/SystemUI/tests/utils/src/android/animation/AnimatorTestRule.java b/packages/SystemUI/tests/utils/src/android/animation/AnimatorTestRule.java
index 41dbc14..b820ca6 100644
--- a/packages/SystemUI/tests/utils/src/android/animation/AnimatorTestRule.java
+++ b/packages/SystemUI/tests/utils/src/android/animation/AnimatorTestRule.java
@@ -68,13 +68,26 @@
private final Object mLock = new Object();
private final TestHandler mTestHandler = new TestHandler();
+ private final long mStartTime;
+ private long mTotalTimeDelta = 0;
+
/**
- * initializing the start time with {@link SystemClock#uptimeMillis()} reduces the discrepancies
- * with various internals of classes like ValueAnimator which can sometimes read that clock via
+ * Construct an AnimatorTestRule with a custom start time.
+ * @see #AnimatorTestRule()
+ */
+ public AnimatorTestRule(long startTime) {
+ mStartTime = startTime;
+ }
+
+ /**
+ * Construct an AnimatorTestRule with a start time of {@link SystemClock#uptimeMillis()}.
+ * Initializing the start time with this clock reduces the discrepancies with various internals
+ * of classes like ValueAnimator which can sometimes read that clock via
* {@link android.view.animation.AnimationUtils#currentAnimationTimeMillis()}.
*/
- private final long mStartTime = SystemClock.uptimeMillis();
- private long mTotalTimeDelta = 0;
+ public AnimatorTestRule() {
+ this(SystemClock.uptimeMillis());
+ }
@NonNull
@Override
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/TestUiOffloadThread.java b/packages/SystemUI/tests/utils/src/com/android/systemui/TestUiOffloadThread.java
new file mode 100644
index 0000000..fdd26eb
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/TestUiOffloadThread.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Future;
+
+/**
+ * UiOffloadThread that can be used for testing as part of {@link TestableDependency}.
+ */
+public class TestUiOffloadThread extends UiOffloadThread {
+ private final Handler mTestHandler;
+
+ public TestUiOffloadThread(Looper looper) {
+ mTestHandler = new Handler(looper);
+ }
+
+ @Override
+ public Future<?> execute(Runnable runnable) {
+ Looper myLooper = Looper.myLooper();
+ if (myLooper != null && myLooper.isCurrentThread()) {
+ try {
+ runnable.run();
+ return CompletableFuture.completedFuture(null);
+ } catch (Exception e) {
+ return CompletableFuture.failedFuture(e);
+ }
+ }
+
+ final CompletableFuture<?> future = new CompletableFuture<>();
+ mTestHandler.post(() -> {
+ try {
+ runnable.run();
+ future.complete(null);
+ } catch (Exception e) {
+ future.completeExceptionally(e);
+ }
+ });
+
+ return future;
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/AnimatorTestRule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/AnimatorTestRule.kt
index 0ced19e..ba9c5ed 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/AnimatorTestRule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/AnimatorTestRule.kt
@@ -28,11 +28,21 @@
* advanced together.
*/
class AnimatorTestRule : TestRule {
+ // Create the androidx rule, which initializes start time to SystemClock.uptimeMillis(),
+ // then copy that time to the platform rule so that the two clocks are in sync.
private val androidxRule = androidx.core.animation.AnimatorTestRule()
- private val platformRule = android.animation.AnimatorTestRule()
+ private val platformRule = android.animation.AnimatorTestRule(androidxRule.startTime)
private val advanceAndroidXTimeBy =
Consumer<Long> { timeDelta -> androidxRule.advanceTimeBy(timeDelta) }
+ /** Access the mStartTime field; bypassing the restriction of being on a looper thread. */
+ private val androidx.core.animation.AnimatorTestRule.startTime: Long
+ get() =
+ javaClass.getDeclaredField("mStartTime").let { field ->
+ field.isAccessible = true
+ field.getLong(this)
+ }
+
/**
* Chain is for simplicity not to force a particular order; order should not matter, because
* each rule affects a different AnimationHandler classes, and no callbacks to code under test
@@ -55,4 +65,11 @@
// animation from one to start later than the other.
platformRule.advanceTimeBy(timeDelta, advanceAndroidXTimeBy)
}
+
+ /**
+ * Returns the current time in milliseconds tracked by the AnimationHandlers. Note that this is
+ * a different time than the time tracked by {@link SystemClock}.
+ */
+ val currentTime: Long
+ get() = androidxRule.currentTime
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalTutorialRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalTutorialRepository.kt
new file mode 100644
index 0000000..902e852
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalTutorialRepository.kt
@@ -0,0 +1,19 @@
+package com.android.systemui.communal.data.repository
+
+import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_NOT_STARTED
+import android.provider.Settings.Secure.HubModeTutorialState
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+
+/** Fake implementation of [CommunalTutorialRepository] */
+class FakeCommunalTutorialRepository() : CommunalTutorialRepository {
+ private val _tutorialSettingState = MutableStateFlow(HUB_MODE_TUTORIAL_NOT_STARTED)
+ override val tutorialSettingState: StateFlow<Int> = _tutorialSettingState
+ override suspend fun setTutorialState(@HubModeTutorialState state: Int) {
+ setTutorialSettingState(state)
+ }
+
+ fun setTutorialSettingState(@HubModeTutorialState state: Int) {
+ _tutorialSettingState.value = state
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt
index bf77b1a..911eafa 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt
@@ -53,7 +53,7 @@
import com.android.systemui.user.data.repository.UserSwitcherRepositoryImpl
import com.android.systemui.user.domain.interactor.UserInteractor
import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.FakeGlobalSettings
import com.android.systemui.util.settings.GlobalSettings
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.test.StandardTestDispatcher
@@ -69,8 +69,8 @@
private val scheduler: TestCoroutineScheduler,
) {
/** Enable or disable the user switcher in the settings. */
- fun setUserSwitcherEnabled(settings: GlobalSettings, enabled: Boolean, userId: Int) {
- settings.putBoolForUser(Settings.Global.USER_SWITCHER_ENABLED, enabled, userId)
+ fun setUserSwitcherEnabled(settings: GlobalSettings, enabled: Boolean) {
+ settings.putBool(Settings.Global.USER_SWITCHER_ENABLED, enabled)
// The settings listener is processing messages on the bgHandler (usually backed by a
// testableLooper in tests), so let's make sure we process the callback before continuing.
@@ -152,7 +152,7 @@
userTracker: UserTracker = FakeUserTracker(),
userSwitcherController: UserSwitcherController = mock(),
userInfoController: UserInfoController = FakeUserInfoController(),
- settings: GlobalSettings = FakeSettings(),
+ settings: GlobalSettings = FakeGlobalSettings(),
): UserSwitcherRepository {
return UserSwitcherRepositoryImpl(
context,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettings.java b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettings.java
new file mode 100644
index 0000000..db5eaff
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettings.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.net.Uri;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class FakeGlobalSettings implements GlobalSettings {
+ private final Map<String, String> mValues = new HashMap<>();
+ private final Map<String, List<ContentObserver>> mContentObserversAllUsers = new HashMap<>();
+
+ public static final Uri CONTENT_URI = Uri.parse("content://settings/fake_global");
+
+ public FakeGlobalSettings() {
+ }
+
+ @Override
+ public ContentResolver getContentResolver() {
+ return null;
+ }
+
+ @Override
+ public void registerContentObserver(Uri uri, boolean notifyDescendants,
+ ContentObserver settingsObserver) {
+ List<ContentObserver> observers;
+ mContentObserversAllUsers.putIfAbsent(uri.toString(), new ArrayList<>());
+ observers = mContentObserversAllUsers.get(uri.toString());
+ observers.add(settingsObserver);
+ }
+
+ @Override
+ public void unregisterContentObserver(ContentObserver settingsObserver) {
+ for (Map.Entry<String, List<ContentObserver>> entry :
+ mContentObserversAllUsers.entrySet()) {
+ entry.getValue().remove(settingsObserver);
+ }
+ }
+
+ @Override
+ public Uri getUriFor(String name) {
+ return Uri.withAppendedPath(CONTENT_URI, name);
+ }
+
+ @Override
+ public String getString(String name) {
+ return mValues.get(getUriFor(name).toString());
+ }
+
+ @Override
+ public boolean putString(String name, String value) {
+ return putString(name, value, null, false);
+ }
+
+ @Override
+ public boolean putString(@NonNull String name, @Nullable String value, @Nullable String tag,
+ boolean makeDefault) {
+ String key = getUriFor(name).toString();
+ mValues.put(key, value);
+
+ Uri uri = getUriFor(name);
+ for (ContentObserver observer :
+ mContentObserversAllUsers.getOrDefault(uri.toString(), new ArrayList<>())) {
+ observer.dispatchChange(false, List.of(uri), 0);
+ }
+ return true;
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java
index 4b97316..a491886 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java
@@ -23,6 +23,8 @@
import android.os.UserHandle;
import android.util.Pair;
+import androidx.annotation.NonNull;
+
import com.android.systemui.settings.UserTracker;
import java.util.ArrayList;
@@ -30,7 +32,7 @@
import java.util.List;
import java.util.Map;
-public class FakeSettings implements SecureSettings, GlobalSettings, SystemSettings {
+public class FakeSettings implements SecureSettings, SystemSettings {
private final Map<SettingsKey, String> mValues = new HashMap<>();
private final Map<SettingsKey, List<ContentObserver>> mContentObservers =
new HashMap<>();
@@ -64,7 +66,7 @@
}
@Override
- public void registerContentObserverForUser(Uri uri, boolean notifyDescendents,
+ public void registerContentObserverForUser(Uri uri, boolean notifyDescendants,
ContentObserver settingsObserver, int userHandle) {
List<ContentObserver> observers;
if (userHandle == UserHandle.USER_ALL) {
@@ -147,7 +149,7 @@
}
@Override
- public boolean putString(String name, String value, String tag, boolean makeDefault) {
+ public boolean putString(@NonNull String name, String value, String tag, boolean makeDefault) {
return putString(name, value);
}
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index ced97cc..7ac7859f 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -90,8 +90,14 @@
// Make sound through the speaker.
ALERT_BEEP = 2;
- // Flash a notificaiton light.
+ // Flash a notification light.
ALERT_BLINK = 4;
+
+ // Alert was attenuated by polite notif. feature.
+ ALERT_POLITE = 8;
+
+ // Alert was muted by polite notif. feature.
+ ALERT_MUTED = 16;
}
// Reasons that a notification might be dismissed.
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
new file mode 100644
index 0000000..91acc3d
--- /dev/null
+++ b/ravenwood/Android.bp
@@ -0,0 +1,33 @@
+package {
+ // 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"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+ name: "ravenwood-annotations",
+ srcs: [
+ "annotations-src/**/*.java",
+ ],
+ visibility: ["//visibility:public"],
+}
+
+// File that contains the standard command line arguments to hoststubgen.
+filegroup {
+ name: "ravenwood-standard-options",
+ srcs: [
+ "ravenwood-standard-options.txt",
+ ],
+ visibility: ["//visibility:public"],
+}
+
+java_library {
+ name: "ravenwood-annotations-lib",
+ srcs: [":ravenwood-annotations"],
+ sdk_version: "core_current",
+ host_supported: true,
+ visibility: ["//visibility:public"],
+}
diff --git a/ravenwood/OWNERS b/ravenwood/OWNERS
new file mode 100644
index 0000000..c06b3b9
--- /dev/null
+++ b/ravenwood/OWNERS
@@ -0,0 +1,3 @@
+jsharkey@google.com
+omakoto@google.com
+jaggies@google.com
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodClassLoadHook.java b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodClassLoadHook.java
new file mode 100644
index 0000000..be7b923
--- /dev/null
+++ b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodClassLoadHook.java
@@ -0,0 +1,38 @@
+/*
+ * 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 android.ravenwood.annotations;
+
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
+ * QUESTIONS ABOUT IT.
+ *
+ * Add this with a fully-specified method name (e.g. {@code "com.package.Class.methodName"})
+ * of a callback to get a callback at the class load time.
+ *
+ * The method must be {@code public static} with a single argument that takes
+ * {@link Class}.
+ */
+@Target({TYPE})
+@Retention(RetentionPolicy.CLASS)
+public @interface RavenwoodClassLoadHook {
+ String value();
+}
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodKeep.java b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodKeep.java
new file mode 100644
index 0000000..1644ffc
--- /dev/null
+++ b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodKeep.java
@@ -0,0 +1,37 @@
+/*
+ * 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 android.ravenwood.annotations;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
+ * QUESTIONS ABOUT IT.
+ *
+ * TODO: Javadoc
+ *
+ */
+@Target({TYPE, FIELD, METHOD, CONSTRUCTOR})
+@Retention(RetentionPolicy.CLASS)
+public @interface RavenwoodKeep {
+}
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodNativeSubstitutionClass.java b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodNativeSubstitutionClass.java
new file mode 100644
index 0000000..eb883e2
--- /dev/null
+++ b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodNativeSubstitutionClass.java
@@ -0,0 +1,34 @@
+/*
+ * 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 android.ravenwood.annotations;
+
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
+ * QUESTIONS ABOUT IT.
+ *
+ * TODO: Javadoc
+ */
+@Target({TYPE})
+@Retention(RetentionPolicy.CLASS)
+public @interface RavenwoodNativeSubstitutionClass {
+ String value();
+}
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodRemove.java b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodRemove.java
new file mode 100644
index 0000000..ffa1fa5
--- /dev/null
+++ b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodRemove.java
@@ -0,0 +1,36 @@
+/*
+ * 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 android.ravenwood.annotations;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
+ * QUESTIONS ABOUT IT.
+ *
+ * TODO: Javadoc
+ */
+@Target({TYPE, FIELD, METHOD, CONSTRUCTOR})
+@Retention(RetentionPolicy.CLASS)
+public @interface RavenwoodRemove {
+}
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodSubstitute.java b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodSubstitute.java
new file mode 100644
index 0000000..6d747da
--- /dev/null
+++ b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodSubstitute.java
@@ -0,0 +1,36 @@
+/*
+ * 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 android.ravenwood.annotations;
+
+import static java.lang.annotation.ElementType.METHOD;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
+ * QUESTIONS ABOUT IT.
+ *
+ * TODO: Javadoc
+ */
+@Target({METHOD})
+@Retention(RetentionPolicy.CLASS)
+public @interface RavenwoodSubstitute {
+ // TODO We should add "_host" as default. We're not doing it yet, because extractign the default
+ // value with ASM doesn't seem trivial. (? not sure.)
+ String suffix();
+}
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodThrow.java b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodThrow.java
new file mode 100644
index 0000000..a329d84
--- /dev/null
+++ b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodThrow.java
@@ -0,0 +1,35 @@
+/*
+ * 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 android.ravenwood.annotations;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.METHOD;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
+ * QUESTIONS ABOUT IT.
+ *
+ * TODO: Javadoc
+ * TODO: Create "whole-class-throw"?
+ */
+@Target({METHOD, CONSTRUCTOR})
+@Retention(RetentionPolicy.CLASS)
+public @interface RavenwoodThrow {
+}
diff --git a/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodWholeClassKeep.java b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodWholeClassKeep.java
new file mode 100644
index 0000000..ae6f42d
--- /dev/null
+++ b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodWholeClassKeep.java
@@ -0,0 +1,37 @@
+/*
+ * 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 android.ravenwood.annotations;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
+ * QUESTIONS ABOUT IT.
+ *
+ * TODO: Javadoc
+ * TODO: Create "whole-class-throw"?
+ */
+@Target({TYPE, FIELD, METHOD, CONSTRUCTOR})
+@Retention(RetentionPolicy.CLASS)
+public @interface RavenwoodWholeClassKeep {
+}
diff --git a/ravenwood/ravenwood-standard-options.txt b/ravenwood/ravenwood-standard-options.txt
new file mode 100644
index 0000000..6e1384f
--- /dev/null
+++ b/ravenwood/ravenwood-standard-options.txt
@@ -0,0 +1,37 @@
+# File containing standard options to HostStubGen for Ravenwood
+
+--debug
+
+# Keep all classes / methods / fields, but make the methods throw.
+--default-throw
+
+# Uncomment below lines to enable each feature.
+# --enable-non-stub-method-check
+
+#--default-method-call-hook
+# com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+#--default-class-load-hook
+# com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+
+# Standard annotations.
+# Note, each line is a single argument, so we need newlines after each `--xxx-annotation`.
+--keep-annotation
+ android.ravenwood.annotations.RavenwoodKeep
+
+--keep-class-annotation
+ android.ravenwood.annotations.RavenwoodWholeClassKeep
+
+--throw-annotation
+ android.ravenwood.annotations.RavenwoodThrow
+
+--remove-annotation
+ android.ravenwood.annotations.RavenwoodRemove
+
+--substitute-annotation
+ android.ravenwood.annotations.RavenwoodSubstitute
+
+--native-substitute-annotation
+ android.ravenwood.annotations.RavenwoodNativeSubstitutionClass
+
+--class-load-hook-annotation
+ android.ravenwood.annotations.RavenwoodClassLoadHook
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index 6fa9c08..10ac2eb 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -40,4 +40,11 @@
namespace: "accessibility"
description: "Whether to set min span of ScaleGestureDetector to zero."
bug: "295327792"
-}
\ No newline at end of file
+}
+
+flag {
+ name: "deprecate_package_list_observer"
+ namespace: "accessibility"
+ description: "Stops using the deprecated PackageListObserver."
+ bug: "304561459"
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 60d4ee6..aa6d800 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -841,32 +841,32 @@
// package changes
monitor.register(mContext, null, UserHandle.ALL, true);
- // Register an additional observer for new packages using PackageManagerInternal, which
- // generally notifies observers much sooner than the BroadcastReceiver-based PackageMonitor.
- final PackageManagerInternal pm = LocalServices.getService(
- PackageManagerInternal.class);
- if (pm != null) {
- pm.getPackageList(new PackageManagerInternal.PackageListObserver() {
- @Override
- public void onPackageAdded(String packageName, int uid) {
- final int userId = UserHandle.getUserId(uid);
- synchronized (mLock) {
- if (userId == mCurrentUserId) {
- onSomePackagesChangedLocked();
+ if (!Flags.deprecatePackageListObserver()) {
+ final PackageManagerInternal pm = LocalServices.getService(
+ PackageManagerInternal.class);
+ if (pm != null) {
+ pm.getPackageList(new PackageManagerInternal.PackageListObserver() {
+ @Override
+ public void onPackageAdded(String packageName, int uid) {
+ final int userId = UserHandle.getUserId(uid);
+ synchronized (mLock) {
+ if (userId == mCurrentUserId) {
+ onSomePackagesChangedLocked();
+ }
}
}
- }
- @Override
- public void onPackageRemoved(String packageName, int uid) {
- final int userId = UserHandle.getUserId(uid);
- synchronized (mLock) {
- if (userId == mCurrentUserId) {
- onPackageRemovedLocked(packageName);
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ final int userId = UserHandle.getUserId(uid);
+ synchronized (mLock) {
+ if (userId == mCurrentUserId) {
+ onPackageRemovedLocked(packageName);
+ }
}
}
- }
- });
+ });
+ }
}
// user change and unlock
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index 30b9d0b..01064ac 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -873,7 +873,7 @@
transitionToDelegatingStateAndClear();
- } else if (mDetectTripleTap
+ } else if (mDetectSingleFingerTripleTap
// If activated, delay an ACTION_DOWN for mMultiTapMaxDelay
// to ensure reachability of
// STATE_PANNING_SCALING(triggerable with ACTION_POINTER_DOWN)
@@ -989,7 +989,7 @@
// Shortcut acts as the 2 initial taps
if (mShortcutTriggered) return tapCount() + 2 >= numTaps;
- final boolean multitapTriggered = mDetectTripleTap
+ final boolean multitapTriggered = mDetectSingleFingerTripleTap
&& tapCount() >= numTaps
&& isMultiTap(mPreLastDown, mLastDown)
&& isMultiTap(mPreLastUp, mLastUp);
@@ -1205,7 +1205,7 @@
* @return true if tap is out of distance slop
*/
boolean isTapOutOfDistanceSlop() {
- if (!mDetectTripleTap) return false;
+ if (!mDetectSingleFingerTripleTap) return false;
if (mPreLastDown == null || mLastDown == null) {
return false;
}
@@ -1282,7 +1282,7 @@
+ ", mMagnifiedInteractionState=" + mPanningScalingState
+ ", mViewportDraggingState=" + mViewportDraggingState
+ ", mSinglePanningState=" + mSinglePanningState
- + ", mDetectTripleTap=" + mDetectTripleTap
+ + ", mDetectSingleFingerTripleTap=" + mDetectSingleFingerTripleTap
+ ", mDetectShortcutTrigger=" + mDetectShortcutTrigger
+ ", mCurrentState=" + State.nameOf(mCurrentState)
+ ", mPreviousState=" + State.nameOf(mPreviousState)
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
index 2894693..8476a5e 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
@@ -57,11 +57,11 @@
protected final boolean mDetectShortcutTrigger;
/**
- * {@code true} if this detector should detect and respond to triple-tap
+ * {@code true} if this detector should detect and respond to single-finger triple-tap
* gestures for engaging and disengaging magnification,
* {@code false} if it should ignore such gestures
*/
- protected final boolean mDetectTripleTap;
+ protected final boolean mDetectSingleFingerTripleTap;
/** Callback interface to report that magnification is interactive with a user. */
public interface Callback {
@@ -85,12 +85,12 @@
private final AccessibilityTraceManager mTrace;
protected final Callback mCallback;
- protected MagnificationGestureHandler(int displayId, boolean detectTripleTap,
+ protected MagnificationGestureHandler(int displayId, boolean detectSingleFingerTripleTap,
boolean detectShortcutTrigger,
AccessibilityTraceManager trace,
@NonNull Callback callback) {
mDisplayId = displayId;
- mDetectTripleTap = detectTripleTap;
+ mDetectSingleFingerTripleTap = detectSingleFingerTripleTap;
mDetectShortcutTrigger = detectShortcutTrigger;
mTrace = trace;
mCallback = callback;
@@ -128,7 +128,7 @@
}
private boolean shouldDispatchTransformedEvent(MotionEvent event) {
- if ((!mDetectTripleTap && !mDetectShortcutTrigger) || !event.isFromSource(
+ if ((!mDetectSingleFingerTripleTap && !mDetectShortcutTrigger) || !event.isFromSource(
SOURCE_TOUCHSCREEN)) {
return true;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
index c58e9a6..2d9dcb9 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
@@ -111,7 +111,7 @@
(event, rawEvent, policyFlags) -> dispatchTransformedEvent(event, rawEvent,
policyFlags));
mDelegatingState = new DelegatingState(mMotionEventDispatcherDelegate);
- mDetectingState = new DetectingState(context, mDetectTripleTap);
+ mDetectingState = new DetectingState(context);
mViewportDraggingState = new ViewportDraggingState();
mObservePanningScalingState = new PanningScalingGestureState(
new PanningScalingHandler(context, MAX_SCALE, MIN_SCALE, true,
@@ -448,22 +448,14 @@
private final MagnificationGesturesObserver mGesturesObserver;
- /**
- * {@code true} if this detector should detect and respond to triple-tap
- * gestures for engaging and disengaging magnification,
- * {@code false} if it should ignore such gestures
- */
- private final boolean mDetectTripleTap;
-
- DetectingState(@UiContext Context context, boolean detectTripleTap) {
- mDetectTripleTap = detectTripleTap;
- final MultiTap multiTap = new MultiTap(context, mDetectTripleTap ? 3 : 1,
- mDetectTripleTap
+ DetectingState(@UiContext Context context) {
+ final MultiTap multiTap = new MultiTap(context, mDetectSingleFingerTripleTap ? 3 : 1,
+ mDetectSingleFingerTripleTap
? MagnificationGestureMatcher.GESTURE_TRIPLE_TAP
: MagnificationGestureMatcher.GESTURE_SINGLE_TAP, null);
final MultiTapAndHold multiTapAndHold = new MultiTapAndHold(context,
- mDetectTripleTap ? 3 : 1,
- mDetectTripleTap
+ mDetectSingleFingerTripleTap ? 3 : 1,
+ mDetectSingleFingerTripleTap
? MagnificationGestureMatcher.GESTURE_TRIPLE_TAP_AND_HOLD
: MagnificationGestureMatcher.GESTURE_SINGLE_TAP_AND_HOLD, null);
mGesturesObserver = new MagnificationGesturesObserver(this,
@@ -488,7 +480,7 @@
@Override
public boolean shouldStopDetection(MotionEvent motionEvent) {
return !mWindowMagnificationMgr.isWindowMagnifierEnabled(mDisplayId)
- && !mDetectTripleTap;
+ && !mDetectSingleFingerTripleTap;
}
@Override
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 3e13499..72242d2 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -228,6 +228,9 @@
private int mMaxInputLengthForAutofill;
@GuardedBy("mFlagLock")
+ private boolean mAutofillCredmanIntegrationEnabled;
+
+ @GuardedBy("mFlagLock")
private boolean mIsFillFieldsFromCurrentSessionOnly;
// Default flag values for Autofill PCC
@@ -705,13 +708,16 @@
DeviceConfig.NAMESPACE_AUTOFILL,
AutofillFeatureFlags.DEVICE_CONFIG_MAX_INPUT_LENGTH_FOR_AUTOFILL,
AutofillFeatureFlags.DEFAULT_MAX_INPUT_LENGTH_FOR_AUTOFILL);
+ mAutofillCredmanIntegrationEnabled = Flags.autofillCredmanIntegration();
mIsFillFieldsFromCurrentSessionOnly = Flags.fillFieldsFromCurrentSessionOnly();
if (verbose) {
Slog.v(mTag, "setDeviceConfigProperties() for PCC: "
+ "mPccClassificationEnabled=" + mPccClassificationEnabled
+ ", mPccPreferProviderOverPcc=" + mPccPreferProviderOverPcc
+ ", mPccUseFallbackDetection=" + mPccUseFallbackDetection
- + ", mPccProviderHints=" + mPccProviderHints);
+ + ", mPccProviderHints=" + mPccProviderHints
+ + ", mAutofillCredmanIntegrationEnabled="
+ + mAutofillCredmanIntegrationEnabled);
}
}
}
@@ -970,6 +976,15 @@
}
/**
+ * Whether the Autofill-Credman integration feature flag is enabled.
+ */
+ public boolean isAutofillCredmanIntegrationEnabled() {
+ synchronized (mFlagLock) {
+ return mAutofillCredmanIntegrationEnabled;
+ }
+ }
+
+ /**
* Whether the Autofill Provider shouldbe preferred over PCC results for selecting datasets.
*/
public boolean preferProviderOverPcc() {
@@ -2110,6 +2125,9 @@
pw.print(";");
pw.print("mPccProviderHints=");
pw.println(mPccProviderHints);
+ pw.print(";");
+ pw.print("mAutofillCredmanIntegrationEnabled=");
+ pw.println(mAutofillCredmanIntegrationEnabled);
}
// Dump per-user services
dumpLocked("", pw);
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 5b8bdd5..518b81f 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -19,6 +19,7 @@
import static android.service.autofill.FillEventHistory.Event.NO_SAVE_UI_REASON_NONE;
import static android.service.autofill.FillEventHistory.Event.UI_TYPE_INLINE;
import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
+import static android.service.autofill.FillRequest.FLAG_SCREEN_HAS_CREDMAN_FIELD;
import static android.view.autofill.AutofillManager.ACTION_START_SESSION;
import static android.view.autofill.AutofillManager.FLAG_ADD_CLIENT_ENABLED;
import static android.view.autofill.AutofillManager.FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY;
@@ -103,6 +104,10 @@
extends AbstractPerUserSystemService<AutofillManagerServiceImpl, AutofillManagerService> {
private static final String TAG = "AutofillManagerServiceImpl";
+
+ private static final ComponentName CREDMAN_SERVICE_COMPONENT_NAME =
+ new ComponentName("com.android.credentialmanager",
+ "com.android.credentialmanager.autofill.CredentialAutofillService");
private static final int MAX_SESSION_ID_CREATE_TRIES = 2048;
/** Minimum interval to prune abandoned sessions */
@@ -532,9 +537,16 @@
assertCallerLocked(clientActivity, compatMode);
- // It's null when the session is just for augmented autofill
- final ComponentName serviceComponentName = mInfo == null ? null
+ ComponentName serviceComponentName = mInfo == null ? null
: mInfo.getServiceInfo().getComponentName();
+
+ if (isAutofillCredmanIntegrationEnabled()
+ && ((flags & FLAG_SCREEN_HAS_CREDMAN_FIELD) != 0)) {
+ // Hardcode to credential manager proxy service
+ Slog.i(TAG, "Routing to CredentialAutofillService");
+ serviceComponentName = CREDMAN_SERVICE_COMPONENT_NAME;
+ }
+
final Session newSession = new Session(this, mUi, getContext(), mHandler, mUserId, mLock,
sessionId, taskId, clientUid, clientActivityToken, clientCallback, hasCallback,
mUiLatencyHistory, mWtfHistory, serviceComponentName,
@@ -1747,6 +1759,10 @@
}
}
+ public boolean isAutofillCredmanIntegrationEnabled() {
+ return mMaster.isAutofillCredmanIntegrationEnabled();
+ }
+
/**
* Called when the {@link AutofillManagerService#mFieldClassificationResolver}
* changed (among other places).
diff --git a/services/autofill/java/com/android/server/autofill/ClientSuggestionsSession.java b/services/autofill/java/com/android/server/autofill/ClientSuggestionsSession.java
deleted file mode 100644
index 715697d..0000000
--- a/services/autofill/java/com/android/server/autofill/ClientSuggestionsSession.java
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.autofill;
-
-import static android.service.autofill.FillRequest.INVALID_REQUEST_ID;
-
-import static com.android.server.autofill.Helper.sVerbose;
-
-import android.annotation.Nullable;
-import android.annotation.UserIdInt;
-import android.app.AppGlobals;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.ICancellationSignal;
-import android.os.RemoteException;
-import android.service.autofill.Dataset;
-import android.service.autofill.FillResponse;
-import android.service.autofill.IFillCallback;
-import android.service.autofill.SaveInfo;
-import android.text.TextUtils;
-import android.text.format.DateUtils;
-import android.util.Slog;
-import android.view.autofill.AutofillId;
-import android.view.autofill.IAutoFillManagerClient;
-import android.view.inputmethod.InlineSuggestionsRequest;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.infra.AndroidFuture;
-
-import java.util.List;
-import java.util.concurrent.CancellationException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicReference;
-
-/**
- * Maintains a client suggestions session with the
- * {@link android.view.autofill.AutofillRequestCallback} through the {@link IAutoFillManagerClient}.
- *
- */
-final class ClientSuggestionsSession {
-
- private static final String TAG = "ClientSuggestionsSession";
- private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 15 * DateUtils.SECOND_IN_MILLIS;
-
- private final int mSessionId;
- private final IAutoFillManagerClient mClient;
- private final Handler mHandler;
- private final ComponentName mComponentName;
-
- private final RemoteFillService.FillServiceCallbacks mCallbacks;
-
- private final Object mLock = new Object();
- @GuardedBy("mLock")
- private AndroidFuture<FillResponse> mPendingFillRequest;
- @GuardedBy("mLock")
- private int mPendingFillRequestId = INVALID_REQUEST_ID;
-
- ClientSuggestionsSession(int sessionId, IAutoFillManagerClient client, Handler handler,
- ComponentName componentName, RemoteFillService.FillServiceCallbacks callbacks) {
- mSessionId = sessionId;
- mClient = client;
- mHandler = handler;
- mComponentName = componentName;
- mCallbacks = callbacks;
- }
-
- void onFillRequest(int requestId, InlineSuggestionsRequest inlineRequest, int flags) {
- final AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>();
- final AtomicReference<AndroidFuture<FillResponse>> futureRef = new AtomicReference<>();
- final AndroidFuture<FillResponse> fillRequest = new AndroidFuture<>();
-
- mHandler.post(() -> {
- if (sVerbose) {
- Slog.v(TAG, "calling onFillRequest() for id=" + requestId);
- }
-
- try {
- mClient.requestFillFromClient(requestId, inlineRequest,
- new FillCallbackImpl(fillRequest, futureRef, cancellationSink));
- } catch (RemoteException e) {
- fillRequest.completeExceptionally(e);
- }
- });
-
- fillRequest.orTimeout(TIMEOUT_REMOTE_REQUEST_MILLIS, TimeUnit.MILLISECONDS);
- futureRef.set(fillRequest);
-
- synchronized (mLock) {
- mPendingFillRequest = fillRequest;
- mPendingFillRequestId = requestId;
- }
-
- fillRequest.whenComplete((res, err) -> mHandler.post(() -> {
- synchronized (mLock) {
- mPendingFillRequest = null;
- mPendingFillRequestId = INVALID_REQUEST_ID;
- }
- if (err == null) {
- processAutofillId(res);
- mCallbacks.onFillRequestSuccess(requestId, res,
- mComponentName.getPackageName(), flags);
- } else {
- Slog.e(TAG, "Error calling on client fill request", err);
- if (err instanceof TimeoutException) {
- dispatchCancellationSignal(cancellationSink.get());
- mCallbacks.onFillRequestTimeout(requestId);
- } else if (err instanceof CancellationException) {
- dispatchCancellationSignal(cancellationSink.get());
- } else {
- mCallbacks.onFillRequestFailure(requestId, err.getMessage());
- }
- }
- }));
- }
-
- /**
- * Gets the application info for the component.
- */
- @Nullable
- static ApplicationInfo getAppInfo(ComponentName comp, @UserIdInt int userId) {
- try {
- ApplicationInfo si = AppGlobals.getPackageManager().getApplicationInfo(
- comp.getPackageName(),
- PackageManager.GET_META_DATA,
- userId);
- if (si != null) {
- return si;
- }
- } catch (RemoteException e) {
- }
- return null;
- }
-
- /**
- * Gets the user-visible name of the application.
- */
- @Nullable
- @GuardedBy("mLock")
- static CharSequence getAppLabelLocked(Context context, ApplicationInfo appInfo) {
- return appInfo == null ? null : appInfo.loadSafeLabel(
- context.getPackageManager(), 0 /* do not ellipsize */,
- TextUtils.SAFE_STRING_FLAG_FIRST_LINE | TextUtils.SAFE_STRING_FLAG_TRIM);
- }
-
- /**
- * Gets the user-visible icon of the application.
- */
- @Nullable
- @GuardedBy("mLock")
- static Drawable getAppIconLocked(Context context, ApplicationInfo appInfo) {
- return appInfo == null ? null : appInfo.loadIcon(context.getPackageManager());
- }
-
- int cancelCurrentRequest() {
- synchronized (mLock) {
- return mPendingFillRequest != null && mPendingFillRequest.cancel(false)
- ? mPendingFillRequestId
- : INVALID_REQUEST_ID;
- }
- }
-
- /**
- * The {@link AutofillId} which the client gets from its view is not contain the session id,
- * but Autofill framework is using the {@link AutofillId} with a session id. So before using
- * those ids in the Autofill framework, applies the current session id.
- *
- * @param res which response need to apply for a session id
- */
- private void processAutofillId(FillResponse res) {
- if (res == null) {
- return;
- }
-
- final List<Dataset> datasets = res.getDatasets();
- if (datasets != null && !datasets.isEmpty()) {
- for (int i = 0; i < datasets.size(); i++) {
- final Dataset dataset = datasets.get(i);
- if (dataset != null) {
- applySessionId(dataset.getFieldIds());
- }
- }
- }
-
- final SaveInfo saveInfo = res.getSaveInfo();
- if (saveInfo != null) {
- applySessionId(saveInfo.getOptionalIds());
- applySessionId(saveInfo.getRequiredIds());
- applySessionId(saveInfo.getSanitizerValues());
- applySessionId(saveInfo.getTriggerId());
- }
- }
-
- private void applySessionId(List<AutofillId> ids) {
- if (ids == null || ids.isEmpty()) {
- return;
- }
-
- for (int i = 0; i < ids.size(); i++) {
- applySessionId(ids.get(i));
- }
- }
-
- private void applySessionId(AutofillId[][] ids) {
- if (ids == null) {
- return;
- }
- for (int i = 0; i < ids.length; i++) {
- applySessionId(ids[i]);
- }
- }
-
- private void applySessionId(AutofillId[] ids) {
- if (ids == null) {
- return;
- }
- for (int i = 0; i < ids.length; i++) {
- applySessionId(ids[i]);
- }
- }
-
- private void applySessionId(AutofillId id) {
- if (id == null) {
- return;
- }
- id.setSessionId(mSessionId);
- }
-
- private void dispatchCancellationSignal(@Nullable ICancellationSignal signal) {
- if (signal == null) {
- return;
- }
- try {
- signal.cancel();
- } catch (RemoteException e) {
- Slog.e(TAG, "Error requesting a cancellation", e);
- }
- }
-
- private class FillCallbackImpl extends IFillCallback.Stub {
- final AndroidFuture<FillResponse> mFillRequest;
- final AtomicReference<AndroidFuture<FillResponse>> mFutureRef;
- final AtomicReference<ICancellationSignal> mCancellationSink;
-
- FillCallbackImpl(AndroidFuture<FillResponse> fillRequest,
- AtomicReference<AndroidFuture<FillResponse>> futureRef,
- AtomicReference<ICancellationSignal> cancellationSink) {
- mFillRequest = fillRequest;
- mFutureRef = futureRef;
- mCancellationSink = cancellationSink;
- }
-
- @Override
- public void onCancellable(ICancellationSignal cancellation) {
- AndroidFuture<FillResponse> future = mFutureRef.get();
- if (future != null && future.isCancelled()) {
- dispatchCancellationSignal(cancellation);
- } else {
- mCancellationSink.set(cancellation);
- }
- }
-
- @Override
- public void onSuccess(FillResponse response) {
- mFillRequest.complete(response);
- }
-
- @Override
- public void onFailure(int requestId, CharSequence message) {
- String errorMessage = message == null ? "" : String.valueOf(message);
- mFillRequest.completeExceptionally(
- new RuntimeException(errorMessage));
- }
- }
-}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index ae14877..07e9c50 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -16,7 +16,6 @@
package com.android.server.autofill;
-import static android.Manifest.permission.PROVIDE_OWN_AUTOFILL_SUGGESTIONS;
import static android.service.autofill.AutofillFieldClassificationService.EXTRA_SCORES;
import static android.service.autofill.AutofillService.EXTRA_FILL_RESPONSE;
import static android.service.autofill.Dataset.PICK_REASON_NO_PCC;
@@ -44,7 +43,6 @@
import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED;
import static android.view.autofill.AutofillManager.COMMIT_REASON_SESSION_DESTROYED;
import static android.view.autofill.AutofillManager.COMMIT_REASON_UNKNOWN;
-import static android.view.autofill.AutofillManager.FLAG_ENABLED_CLIENT_SUGGESTIONS;
import static android.view.autofill.AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM;
import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString;
@@ -110,8 +108,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.graphics.Bitmap;
import android.graphics.Rect;
@@ -481,9 +477,6 @@
*/
private final PccAssistDataReceiverImpl mPccAssistReceiver = new PccAssistDataReceiverImpl();
- @Nullable
- private ClientSuggestionsSession mClientSuggestionsSession;
-
private final ClassificationState mClassificationState = new ClassificationState();
// TODO(b/216576510): Share one BroadcastReceiver between all Sessions instead of creating a
@@ -625,9 +618,6 @@
/** Whether the current {@link FillResponse} is expired. */
private boolean mExpiredResponse;
- /** Whether the client is using {@link android.view.autofill.AutofillRequestCallback}. */
- private boolean mClientSuggestionsEnabled;
-
/** Whether the fill dialog UI is disabled. */
private boolean mFillDialogDisabled;
@@ -673,19 +663,13 @@
}
mWaitForInlineRequest = inlineSuggestionsRequest != null;
mPendingInlineSuggestionsRequest = inlineSuggestionsRequest;
- maybeRequestFillFromServiceLocked();
+ maybeRequestFillLocked();
viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
}
}
- void newAutofillRequestLocked(@Nullable InlineSuggestionsRequest inlineRequest) {
- mPendingFillRequest = null;
- mWaitForInlineRequest = inlineRequest != null;
- mPendingInlineSuggestionsRequest = inlineRequest;
- }
-
@GuardedBy("mLock")
- void maybeRequestFillFromServiceLocked() {
+ void maybeRequestFillLocked() {
if (mPendingFillRequest == null) {
return;
}
@@ -696,15 +680,13 @@
return;
}
- if (mPendingInlineSuggestionsRequest.isServiceSupported()) {
- mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(),
- mPendingFillRequest.getFillContexts(),
- mPendingFillRequest.getHints(),
- mPendingFillRequest.getClientState(),
- mPendingFillRequest.getFlags(),
- mPendingInlineSuggestionsRequest,
- mPendingFillRequest.getDelayedFillIntentSender());
- }
+ mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(),
+ mPendingFillRequest.getFillContexts(),
+ mPendingFillRequest.getHints(),
+ mPendingFillRequest.getClientState(),
+ mPendingFillRequest.getFlags(),
+ mPendingInlineSuggestionsRequest,
+ mPendingFillRequest.getDelayedFillIntentSender());
}
mLastFillRequest = mPendingFillRequest;
@@ -826,7 +808,7 @@
: mDelayedFillPendingIntent.getIntentSender());
mPendingFillRequest = request;
- maybeRequestFillFromServiceLocked();
+ maybeRequestFillLocked();
}
if (mActivityToken != null) {
@@ -1152,39 +1134,30 @@
}
/**
- * Cancels the last request sent to the {@link #mRemoteFillService} or the
- * {@link #mClientSuggestionsSession}.
+ * Cancels the last request sent to the {@link #mRemoteFillService}.
*/
@GuardedBy("mLock")
private void cancelCurrentRequestLocked() {
- if (mRemoteFillService == null && mClientSuggestionsSession == null) {
- wtf(null, "cancelCurrentRequestLocked() called without a remote service or a "
- + "client suggestions session. mForAugmentedAutofillOnly: %s",
- mSessionFlags.mAugmentedAutofillOnly);
+ if (mRemoteFillService == null) {
+ wtf(null, "cancelCurrentRequestLocked() called without a remote service. "
+ + "mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly);
return;
}
+ final int canceledRequest = mRemoteFillService.cancelCurrentRequest();
- if (mRemoteFillService != null) {
- final int canceledRequest = mRemoteFillService.cancelCurrentRequest();
+ // Remove the FillContext as there will never be a response for the service
+ if (canceledRequest != INVALID_REQUEST_ID && mContexts != null) {
+ final int numContexts = mContexts.size();
- // Remove the FillContext as there will never be a response for the service
- if (canceledRequest != INVALID_REQUEST_ID && mContexts != null) {
- final int numContexts = mContexts.size();
-
- // It is most likely the last context, hence search backwards
- for (int i = numContexts - 1; i >= 0; i--) {
- if (mContexts.get(i).getRequestId() == canceledRequest) {
- if (sDebug) Slog.d(TAG, "cancelCurrentRequest(): id = " + canceledRequest);
- mContexts.remove(i);
- break;
- }
+ // It is most likely the last context, hence search backwards
+ for (int i = numContexts - 1; i >= 0; i--) {
+ if (mContexts.get(i).getRequestId() == canceledRequest) {
+ if (sDebug) Slog.d(TAG, "cancelCurrentRequest(): id = " + canceledRequest);
+ mContexts.remove(i);
+ break;
}
}
}
-
- if (mClientSuggestionsSession != null) {
- mClientSuggestionsSession.cancelCurrentRequest();
- }
}
private boolean isViewFocusedLocked(int flags) {
@@ -1280,30 +1253,16 @@
requestAssistStructureForPccLocked(flags | FLAG_PCC_DETECTION);
}
- // Only ask IME to create inline suggestions request when
- // 1. Autofill provider supports it or client enabled client suggestions.
- // 2. The render service is available.
- // 3. The view is focused. (The view may not be focused if the autofill is triggered
- // manually.)
+ // Only ask IME to create inline suggestions request if Autofill provider supports it and
+ // the render service is available except the autofill is triggered manually and the view
+ // is also not focused.
final RemoteInlineSuggestionRenderService remoteRenderService =
mService.getRemoteInlineSuggestionRenderServiceLocked();
- if ((mSessionFlags.mInlineSupportedByService || mSessionFlags.mClientSuggestionsEnabled)
- && remoteRenderService != null
- && (isViewFocusedLocked(flags) || (isRequestSupportFillDialog(flags)))) {
- final Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer;
- if (mSessionFlags.mClientSuggestionsEnabled) {
- final int finalRequestId = requestId;
- inlineSuggestionsRequestConsumer = (inlineSuggestionsRequest) -> {
- // Using client suggestions
- synchronized (mLock) {
- onClientFillRequestLocked(finalRequestId, inlineSuggestionsRequest);
- }
- viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
- };
- } else {
- inlineSuggestionsRequestConsumer = mAssistReceiver.newAutofillRequestLocked(
- viewState, /* isInlineRequest= */ true);
- }
+ if (mSessionFlags.mInlineSupportedByService && remoteRenderService != null
+ && (isViewFocusedLocked(flags) || isRequestSupportFillDialog(flags))) {
+ Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer =
+ mAssistReceiver.newAutofillRequestLocked(viewState,
+ /* isInlineRequest= */ true);
if (inlineSuggestionsRequestConsumer != null) {
final int requestIdCopy = requestId;
final AutofillId focusedId = mCurrentViewId;
@@ -1323,18 +1282,10 @@
inlineSuggestionRendorInfoCallback);
viewState.setState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
}
- } else if (mSessionFlags.mClientSuggestionsEnabled) {
- // Request client suggestions for the dropdown mode
- onClientFillRequestLocked(requestId, null);
} else {
mAssistReceiver.newAutofillRequestLocked(viewState, /* isInlineRequest= */ false);
}
- if (mSessionFlags.mClientSuggestionsEnabled) {
- // Using client suggestions, unnecessary request AssistStructure
- return;
- }
-
// Now request the assist structure data.
requestAssistStructureLocked(requestId, flags);
}
@@ -1443,11 +1394,6 @@
mSessionFlags = new SessionFlags();
mSessionFlags.mAugmentedAutofillOnly = forAugmentedAutofillOnly;
mSessionFlags.mInlineSupportedByService = mService.isInlineSuggestionsEnabledLocked();
- if (mContext.checkCallingPermission(PROVIDE_OWN_AUTOFILL_SUGGESTIONS)
- == PackageManager.PERMISSION_GRANTED) {
- mSessionFlags.mClientSuggestionsEnabled =
- (mFlags & FLAG_ENABLED_CLIENT_SUGGESTIONS) != 0;
- }
setClientLocked(client);
}
@@ -1599,15 +1545,14 @@
if (requestLog != null) {
requestLog.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS, -1);
}
- processNullResponseOrFallbackLocked(requestId, requestFlags);
+ processNullResponseLocked(requestId, requestFlags);
return;
}
// TODO: Check if this is required. We can still present datasets to the user even if
// traditional field classification is disabled.
fieldClassificationIds = response.getFieldClassificationIds();
- if (!mSessionFlags.mClientSuggestionsEnabled && fieldClassificationIds != null
- && !mService.isFieldClassificationEnabledLocked()) {
+ if (fieldClassificationIds != null && !mService.isFieldClassificationEnabledLocked()) {
Slog.w(TAG, "Ignoring " + response + " because field detection is disabled");
processNullResponseLocked(requestId, requestFlags);
return;
@@ -1741,9 +1686,7 @@
|| (ArrayUtils.isEmpty(saveInfo.getOptionalIds())
&& ArrayUtils.isEmpty(saveInfo.getRequiredIds())
&& ((saveInfo.getFlags() & SaveInfo.FLAG_DELAY_SAVE) == 0)))
- && (ArrayUtils.isEmpty(response.getFieldClassificationIds())
- || (!mSessionFlags.mClientSuggestionsEnabled
- && !mService.isFieldClassificationEnabledLocked())));
+ && (ArrayUtils.isEmpty(response.getFieldClassificationIds())));
}
}
@@ -2190,40 +2133,6 @@
fieldFilters.add(dataset.getFilter(index));
}
- @GuardedBy("mLock")
- private void processNullResponseOrFallbackLocked(int requestId, int flags) {
- if (!mSessionFlags.mClientSuggestionsEnabled) {
- processNullResponseLocked(requestId, flags);
- return;
- }
-
- // fallback to the default platform password manager
- mSessionFlags.mClientSuggestionsEnabled = false;
- mLastFillDialogTriggerIds = null;
- // Log the existing FillResponse event.
- mFillResponseEventLogger.logAndEndEvent();
-
- final InlineSuggestionsRequest inlineRequest =
- (mLastInlineSuggestionsRequest != null
- && mLastInlineSuggestionsRequest.first == requestId)
- ? mLastInlineSuggestionsRequest.second : null;
-
- // Start a new FillRequest logger for client suggestion fallback.
- mFillRequestEventLogger.startLogForNewRequest();
- mRequestCount++;
- mFillRequestEventLogger.maybeSetAppPackageUid(uid);
- mFillRequestEventLogger.maybeSetFlags(
- flags & ~FLAG_ENABLED_CLIENT_SUGGESTIONS);
- mFillRequestEventLogger.maybeSetRequestTriggerReason(
- TRIGGER_REASON_NORMAL_TRIGGER);
- mFillRequestEventLogger.maybeSetIsClientSuggestionFallback(true);
-
- mAssistReceiver.newAutofillRequestLocked(inlineRequest);
- requestAssistStructureLocked(requestId,
- flags & ~FLAG_ENABLED_CLIENT_SUGGESTIONS);
- return;
- }
-
// FillServiceCallbacks
@Override
@SuppressWarnings("GuardedBy")
@@ -4520,22 +4429,13 @@
filterText = value.getTextValue().toString();
}
- final CharSequence targetLabel;
- final Drawable targetIcon;
- synchronized (mLock) {
- if (mSessionFlags.mClientSuggestionsEnabled) {
- final ApplicationInfo appInfo = ClientSuggestionsSession.getAppInfo(mComponentName,
- mService.getUserId());
- targetLabel = ClientSuggestionsSession.getAppLabelLocked(
- mService.getMaster().getContext(), appInfo);
- targetIcon = ClientSuggestionsSession.getAppIconLocked(
- mService.getMaster().getContext(), appInfo);
- } else {
- targetLabel = mService.getServiceLabelLocked();
- targetIcon = mService.getServiceIconLocked();
- }
+ final CharSequence serviceLabel;
+ final Drawable serviceIcon;
+ synchronized (this.mService.mLock) {
+ serviceLabel = mService.getServiceLabelLocked();
+ serviceIcon = mService.getServiceIconLocked();
}
- if (targetLabel == null || targetIcon == null) {
+ if (serviceLabel == null || serviceIcon == null) {
wtf(null, "onFillReady(): no service label or icon");
return;
}
@@ -4596,7 +4496,7 @@
getUiForShowing().showFillUi(filledId, response, filterText,
mService.getServicePackageName(), mComponentName,
- targetLabel, targetIcon, this, mContext, id, mCompatMode,
+ serviceLabel, serviceIcon, this, mContext, id, mCompatMode,
mService.getMaster().getMaxInputLengthForAutofill());
synchronized (mLock) {
@@ -4799,17 +4699,6 @@
return false;
}
- final InlineSuggestionsRequest request = inlineSuggestionsRequest.get();
- if (mSessionFlags.mClientSuggestionsEnabled && !request.isClientSupported()
- || !mSessionFlags.mClientSuggestionsEnabled && !request.isServiceSupported()) {
- if (sDebug) {
- Slog.d(TAG, "Inline suggestions not supported for "
- + (mSessionFlags.mClientSuggestionsEnabled ? "client" : "service")
- + ". Falling back to dropdown.");
- }
- return false;
- }
-
final RemoteInlineSuggestionRenderService remoteRenderService =
mService.getRemoteInlineSuggestionRenderServiceLocked();
if (remoteRenderService == null) {
@@ -4824,7 +4713,7 @@
}
final InlineFillUi.InlineFillUiInfo inlineFillUiInfo =
- new InlineFillUi.InlineFillUiInfo(request, focusedId,
+ new InlineFillUi.InlineFillUiInfo(inlineSuggestionsRequest.get(), focusedId,
filterText, remoteRenderService, userId, id);
InlineFillUi inlineFillUi = InlineFillUi.forAutofill(inlineFillUiInfo, response,
new InlineFillUi.InlineSuggestionUiCallback() {
@@ -5641,26 +5530,6 @@
}
}
- @GuardedBy("mLock")
- private void onClientFillRequestLocked(int requestId,
- InlineSuggestionsRequest inlineSuggestionsRequest) {
- if (mClientSuggestionsSession == null) {
- mClientSuggestionsSession = new ClientSuggestionsSession(id, mClient, mHandler,
- mComponentName, this);
- }
-
- if (mContexts == null) {
- mContexts = new ArrayList<>(1);
- }
- mContexts.add(new FillContext(requestId, new AssistStructure(), mCurrentViewId));
-
- if (inlineSuggestionsRequest != null && !inlineSuggestionsRequest.isClientSupported()) {
- inlineSuggestionsRequest = null;
- }
-
- mClientSuggestionsSession.onFillRequest(requestId, inlineSuggestionsRequest, mFlags);
- }
-
/**
* The result of checking whether to show the save dialog, when session can be saved.
*
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index f594170..1a8dd3a 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -20,6 +20,9 @@
import static android.content.Context.CONTENT_CAPTURE_MANAGER_SERVICE;
import static android.service.contentcapture.ContentCaptureService.setClientState;
import static android.view.contentcapture.ContentCaptureHelper.toList;
+import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_OPTIONAL_GROUPS_CONFIG;
+import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_OPTIONAL_GROUPS_THRESHOLD;
+import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_REQUIRED_GROUPS_CONFIG;
import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_FALSE;
import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_OK;
import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_SECURITY_EXCEPTION;
@@ -112,6 +115,7 @@
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
@@ -203,6 +207,17 @@
@GuardedBy("mLock")
int mDevCfgContentProtectionBufferSize;
+ @GuardedBy("mLock")
+ @NonNull
+ List<List<String>> mDevCfgContentProtectionRequiredGroups;
+
+ @GuardedBy("mLock")
+ @NonNull
+ List<List<String>> mDevCfgContentProtectionOptionalGroups;
+
+ @GuardedBy("mLock")
+ int mDevCfgContentProtectionOptionalGroupsThreshold;
+
private final Executor mDataShareExecutor = Executors.newCachedThreadPool();
private final Handler mHandler = new Handler(Looper.getMainLooper());
@@ -226,6 +241,11 @@
com.android.internal.R.string.config_defaultContentCaptureService),
UserManager.DISALLOW_CONTENT_CAPTURE,
/*packageUpdatePolicy=*/ PACKAGE_UPDATE_POLICY_NO_REFRESH);
+
+ mDevCfgContentProtectionRequiredGroups =
+ ContentCaptureManager.DEFAULT_CONTENT_PROTECTION_REQUIRED_GROUPS;
+ mDevCfgContentProtectionOptionalGroups =
+ ContentCaptureManager.DEFAULT_CONTENT_PROTECTION_OPTIONAL_GROUPS;
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
ActivityThread.currentApplication().getMainExecutor(),
(properties) -> onDeviceConfigChange(properties));
@@ -422,6 +442,9 @@
case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_BUFFER_SIZE:
case ContentCaptureManager
.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_APPS_BLOCKLIST_SIZE:
+ case DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_REQUIRED_GROUPS_CONFIG:
+ case DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_OPTIONAL_GROUPS_CONFIG:
+ case DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_OPTIONAL_GROUPS_THRESHOLD:
setFineTuneParamsFromDeviceConfig();
return;
default:
@@ -433,6 +456,8 @@
/** @hide */
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
protected void setFineTuneParamsFromDeviceConfig() {
+ String contentProtectionRequiredGroupsConfig;
+ String contentProtectionOptionalGroupsConfig;
synchronized (mLock) {
mDevCfgMaxBufferSize =
DeviceConfig.getInt(
@@ -486,6 +511,24 @@
ContentCaptureManager
.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_BUFFER_SIZE,
ContentCaptureManager.DEFAULT_CONTENT_PROTECTION_BUFFER_SIZE);
+ contentProtectionRequiredGroupsConfig =
+ DeviceConfig.getString(
+ DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
+ DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_REQUIRED_GROUPS_CONFIG,
+ ContentCaptureManager
+ .DEFAULT_CONTENT_PROTECTION_REQUIRED_GROUPS_CONFIG);
+ contentProtectionOptionalGroupsConfig =
+ DeviceConfig.getString(
+ DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
+ DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_OPTIONAL_GROUPS_CONFIG,
+ ContentCaptureManager
+ .DEFAULT_CONTENT_PROTECTION_OPTIONAL_GROUPS_CONFIG);
+ mDevCfgContentProtectionOptionalGroupsThreshold =
+ DeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
+ DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_OPTIONAL_GROUPS_THRESHOLD,
+ ContentCaptureManager
+ .DEFAULT_CONTENT_PROTECTION_OPTIONAL_GROUPS_THRESHOLD);
if (verbose) {
Slog.v(
TAG,
@@ -507,9 +550,24 @@
+ ", contentProtectionAppsBlocklistSize="
+ mDevCfgContentProtectionAppsBlocklistSize
+ ", contentProtectionBufferSize="
- + mDevCfgContentProtectionBufferSize);
+ + mDevCfgContentProtectionBufferSize
+ + ", contentProtectionRequiredGroupsConfig="
+ + contentProtectionRequiredGroupsConfig
+ + ", contentProtectionOptionalGroupsConfig="
+ + contentProtectionOptionalGroupsConfig
+ + ", contentProtectionOptionalGroupsThreshold="
+ + mDevCfgContentProtectionOptionalGroupsThreshold);
}
}
+
+ List<List<String>> contentProtectionRequiredGroups =
+ parseContentProtectionGroupsConfig(contentProtectionRequiredGroupsConfig);
+ List<List<String>> contentProtectionOptionalGroups =
+ parseContentProtectionGroupsConfig(contentProtectionOptionalGroupsConfig);
+ synchronized (mLock) {
+ mDevCfgContentProtectionRequiredGroups = contentProtectionRequiredGroups;
+ mDevCfgContentProtectionOptionalGroups = contentProtectionOptionalGroups;
+ }
}
private void setLoggingLevelFromDeviceConfig() {
@@ -786,6 +844,15 @@
pw.print(prefix2);
pw.print("contentProtectionBufferSize: ");
pw.println(mDevCfgContentProtectionBufferSize);
+ pw.print(prefix2);
+ pw.print("contentProtectionRequiredGroupsSize: ");
+ pw.println(mDevCfgContentProtectionRequiredGroups.size());
+ pw.print(prefix2);
+ pw.print("contentProtectionOptionalGroupsSize: ");
+ pw.println(mDevCfgContentProtectionOptionalGroups.size());
+ pw.print(prefix2);
+ pw.print("contentProtectionOptionalGroupsThreshold: ");
+ pw.println(mDevCfgContentProtectionOptionalGroupsThreshold);
pw.print(prefix);
pw.println("Global Options:");
mGlobalContentCaptureOptions.dump(prefix2, pw);
@@ -890,6 +957,16 @@
return mContentCaptureManagerServiceStub;
}
+ /** @hide */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ @NonNull
+ protected List<List<String>> parseContentProtectionGroupsConfig(@Nullable String config) {
+ if (verbose) {
+ Slog.v(TAG, "parseContentProtectionGroupsConfig: " + config);
+ }
+ return Collections.emptyList();
+ }
+
final class ContentCaptureManagerServiceStub extends IContentCaptureManager.Stub {
@Override
@@ -1277,7 +1354,10 @@
isContentCaptureReceiverEnabled || whitelistedComponents != null,
new ContentCaptureOptions.ContentProtectionOptions(
isContentProtectionReceiverEnabled,
- mDevCfgContentProtectionBufferSize),
+ mDevCfgContentProtectionBufferSize,
+ mDevCfgContentProtectionRequiredGroups,
+ mDevCfgContentProtectionOptionalGroups,
+ mDevCfgContentProtectionOptionalGroupsThreshold),
whitelistedComponents);
if (verbose) Slog.v(TAG, "getOptionsForPackage(" + packageName + "): " + options);
return options;
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 6521fab..e5225f6 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -183,6 +183,7 @@
"cbor-java",
"display_flags_lib",
"icu4j_calendar_astronomer",
+ "android.security.aaid_aidl-java",
"netd-client",
"overlayable_policy_aidl-java",
"SurfaceFlingerProperties",
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 556eba6..e9d4d76 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -16,6 +16,7 @@
package com.android.server;
+import static android.os.Flags.stateOfHealthPublic;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import static com.android.server.health.Utils.copyV1Battery;
@@ -27,7 +28,6 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.database.ContentObserver;
import android.hardware.health.HealthInfo;
import android.hardware.health.V2_1.BatteryCapacityLevel;
@@ -1333,10 +1333,14 @@
@Override
public int getProperty(int id, final BatteryProperty prop) throws RemoteException {
switch (id) {
+ case BatteryManager.BATTERY_PROPERTY_STATE_OF_HEALTH:
+ if (stateOfHealthPublic()) {
+ break;
+ }
+
case BatteryManager.BATTERY_PROPERTY_MANUFACTURING_DATE:
case BatteryManager.BATTERY_PROPERTY_FIRST_USAGE_DATE:
case BatteryManager.BATTERY_PROPERTY_CHARGING_POLICY:
- case BatteryManager.BATTERY_PROPERTY_STATE_OF_HEALTH:
mContext.enforceCallingPermission(
android.Manifest.permission.BATTERY_STATS, null);
break;
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index d47a399..db89cac 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -87,7 +87,7 @@
# replaces 27510 with a row per notification
27531 notification_visibility (key|3),(visibile|1),(lifespan|1),(freshness|1),(exposure|1),(rank|1)
# a notification emited noise, vibration, or light
-27532 notification_alert (key|3),(buzz|1),(beep|1),(blink|1)
+27532 notification_alert (key|3),(buzz|1),(beep|1),(blink|1),(politeness|1)
# a notification was added to a autogroup
27533 notification_autogrouped (key|3)
# notification was removed from an autogroup
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 19879db..b43b986 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -377,6 +377,7 @@
import android.util.IndentingPrintWriter;
import android.util.IntArray;
import android.util.Log;
+import android.util.MathUtils;
import android.util.Pair;
import android.util.PrintWriterPrinter;
import android.util.Slog;
@@ -562,7 +563,7 @@
static final int PROC_START_TIMEOUT = 10 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
// How long we wait for a launched process to complete its app startup before we ANR.
- static final int BIND_APPLICATION_TIMEOUT = 10 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
+ static final int BIND_APPLICATION_TIMEOUT = 15 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
// How long we wait to kill an application zygote, after the last process using
// it has gone away.
@@ -1531,6 +1532,11 @@
*/
int mBootPhase;
+ /**
+ * The time stamp that all apps have received BOOT_COMPLETED.
+ */
+ volatile long mBootCompletedTimestamp;
+
@GuardedBy("this")
boolean mDeterministicUidIdle = false;
@@ -1630,7 +1636,8 @@
static final int UPDATE_CACHED_APP_HIGH_WATERMARK = 79;
static final int ADD_UID_TO_OBSERVER_MSG = 80;
static final int REMOVE_UID_FROM_OBSERVER_MSG = 81;
- static final int BIND_APPLICATION_TIMEOUT_MSG = 82;
+ static final int BIND_APPLICATION_TIMEOUT_SOFT_MSG = 82;
+ static final int BIND_APPLICATION_TIMEOUT_HARD_MSG = 83;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -1983,15 +1990,11 @@
case UPDATE_CACHED_APP_HIGH_WATERMARK: {
mAppProfiler.mCachedAppsWatermarkData.updateCachedAppsSnapshot((long) msg.obj);
} break;
- case BIND_APPLICATION_TIMEOUT_MSG: {
- ProcessRecord app = (ProcessRecord) msg.obj;
-
- final String anrMessage;
- synchronized (app) {
- anrMessage = "Process " + app + " failed to complete startup";
- }
-
- mAnrHelper.appNotResponding(app, TimeoutRecord.forAppStart(anrMessage));
+ case BIND_APPLICATION_TIMEOUT_SOFT_MSG: {
+ handleBindApplicationTimeoutSoft((ProcessRecord) msg.obj, msg.arg1);
+ } break;
+ case BIND_APPLICATION_TIMEOUT_HARD_MSG: {
+ handleBindApplicationTimeoutHard((ProcessRecord) msg.obj);
} break;
}
}
@@ -4757,6 +4760,7 @@
mPlatformCompat.resetReporting(app.info);
}
final ProviderInfoList providerList = ProviderInfoList.fromList(providers);
+ app.mProfile.mLastCpuDelayTime.set(app.getCpuDelayTime());
if (app.getIsolatedEntryPoint() != null) {
// This is an isolated process which should just call an entry point instead of
// being bound to an application.
@@ -4794,9 +4798,10 @@
app.getStartElapsedTime(), app.getStartUptime());
}
- Message msg = mHandler.obtainMessage(BIND_APPLICATION_TIMEOUT_MSG);
+ Message msg = mHandler.obtainMessage(BIND_APPLICATION_TIMEOUT_SOFT_MSG);
msg.obj = app;
- mHandler.sendMessageDelayed(msg, BIND_APPLICATION_TIMEOUT);
+ msg.arg1 = BIND_APPLICATION_TIMEOUT;
+ mHandler.sendMessageDelayed(msg, msg.arg1 /* BIND_APPLICATION_TIMEOUT */);
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
if (profilerInfo != null) {
@@ -4873,7 +4878,8 @@
}
if (app != null && app.getStartUid() == uid && app.getStartSeq() == startSeq) {
- mHandler.removeMessages(BIND_APPLICATION_TIMEOUT_MSG, app);
+ mHandler.removeMessages(BIND_APPLICATION_TIMEOUT_SOFT_MSG, app);
+ mHandler.removeMessages(BIND_APPLICATION_TIMEOUT_HARD_MSG, app);
} else {
Slog.wtf(TAG, "Mismatched or missing ProcessRecord: " + app + ". Pid: " + pid
+ ". Uid: " + uid);
@@ -5010,6 +5016,35 @@
}
}
+ private void handleBindApplicationTimeoutSoft(ProcessRecord app, int softTimeoutMillis) {
+ // Similar logic as the broadcast delivery timeout:
+ // instead of immediately triggering an ANR, extend the timeout by
+ // the amount of time the process was runnable-but-waiting; we're
+ // only willing to do this once before triggering an hard ANR.
+ final long cpuDelayTime = app.getCpuDelayTime() - app.mProfile.mLastCpuDelayTime.get();
+ final long hardTimeoutMillis = MathUtils.constrain(cpuDelayTime, 0, softTimeoutMillis);
+
+ if (hardTimeoutMillis == 0) {
+ handleBindApplicationTimeoutHard(app);
+ return;
+ }
+
+ Slog.i(TAG, "Extending process start timeout by " + hardTimeoutMillis + "ms for " + app);
+ Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplicationTimeSoft "
+ + app.processName + "(" + app.getPid() + ")");
+ final Message msg = mHandler.obtainMessage(BIND_APPLICATION_TIMEOUT_HARD_MSG, app);
+ mHandler.sendMessageDelayed(msg, hardTimeoutMillis);
+ }
+
+ private void handleBindApplicationTimeoutHard(ProcessRecord app) {
+ final String anrMessage;
+ synchronized (app) {
+ anrMessage = "Process " + app + " failed to complete startup";
+ }
+
+ mAnrHelper.appNotResponding(app, TimeoutRecord.forAppStart(anrMessage));
+ }
+
/**
* @return The last part of the string of an intent's action.
*/
@@ -5134,10 +5169,14 @@
public void performReceive(Intent intent, int resultCode,
String data, Bundle extras, boolean ordered,
boolean sticky, int sendingUser) {
- synchronized (mProcLock) {
- mAppProfiler.requestPssAllProcsLPr(
- SystemClock.uptimeMillis(), true, false);
- }
+ mBootCompletedTimestamp = SystemClock.uptimeMillis();
+ // Defer the full Pss collection as the system is really busy now.
+ mHandler.postDelayed(() -> {
+ synchronized (mProcLock) {
+ mAppProfiler.requestPssAllProcsLPr(
+ SystemClock.uptimeMillis(), true, false);
+ }
+ }, mConstants.FULL_PSS_MIN_INTERVAL);
}
});
maybeLogUserspaceRebootEvent();
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index 4b622f5..903cb7b 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -175,13 +175,15 @@
TextFlags.ENABLE_NEW_CONTEXT_MENU_DEFAULT));
// Register all text aconfig flags.
- for (String flag : TextFlags.TEXT_ACONFIGS_FLAGS) {
+ for (int i = 0; i < TextFlags.TEXT_ACONFIGS_FLAGS.length; i++) {
+ final String flag = TextFlags.TEXT_ACONFIGS_FLAGS[i];
+ final boolean defaultValue = TextFlags.TEXT_ACONFIG_DEFAULT_VALUE[i];
sDeviceConfigEntries.add(new DeviceConfigEntry<Boolean>(
TextFlags.NAMESPACE,
flag,
TextFlags.getKeyForFlag(flag),
boolean.class,
- false)); // All aconfig flags are false by default.
+ defaultValue));
}
// add other device configs here...
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 4572766..e0e6cad 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1439,7 +1439,7 @@
}
public static long computeNextPssTime(int procState, ProcStateMemTracker tracker, boolean test,
- boolean sleeping, long now) {
+ boolean sleeping, long now, long earliest) {
boolean first;
float scalingFactor;
final int memState = sProcStateToProcMem[procState];
@@ -1470,7 +1470,7 @@
if (delay > PSS_MAX_INTERVAL) {
delay = PSS_MAX_INTERVAL;
}
- return now + delay;
+ return Math.max(now + delay, earliest);
}
long getMemLevel(int adjustment) {
diff --git a/services/core/java/com/android/server/am/ProcessProfileRecord.java b/services/core/java/com/android/server/am/ProcessProfileRecord.java
index db74f1a..940c58b 100644
--- a/services/core/java/com/android/server/am/ProcessProfileRecord.java
+++ b/services/core/java/com/android/server/am/ProcessProfileRecord.java
@@ -142,6 +142,11 @@
final AtomicLong mCurCpuTime = new AtomicLong(0);
/**
+ * How long the process has spent on waiting in the runqueue since fork.
+ */
+ final AtomicLong mLastCpuDelayTime = new AtomicLong(0);
+
+ /**
* Last selected memory trimming level.
*/
@CompositeRWLock({"mService", "mProcLock"})
@@ -570,7 +575,11 @@
@GuardedBy("mProfilerLock")
long computeNextPssTime(int procState, boolean test, boolean sleeping, long now) {
- return ProcessList.computeNextPssTime(procState, mProcStateMemTracker, test, sleeping, now);
+ return ProcessList.computeNextPssTime(procState, mProcStateMemTracker, test, sleeping, now,
+ // Cap the Pss time to make sure no Pss is collected during the very few
+ // minutes after the system is boot, given the system is already busy.
+ Math.max(mService.mBootCompletedTimestamp, mService.mLastIdleTime)
+ + mService.mConstants.FULL_PSS_MIN_INTERVAL);
}
private static void commitNextPssTime(ProcStateMemTracker tracker) {
diff --git a/services/core/java/com/android/server/audio/AudioManagerShellCommand.java b/services/core/java/com/android/server/audio/AudioManagerShellCommand.java
index 241abaf..85acf70 100644
--- a/services/core/java/com/android/server/audio/AudioManagerShellCommand.java
+++ b/services/core/java/com/android/server/audio/AudioManagerShellCommand.java
@@ -47,6 +47,12 @@
return setEncodedSurroundMode();
case "get-encoded-surround-mode":
return getEncodedSurroundMode();
+ case "set-sound-dose-value":
+ return setSoundDoseValue();
+ case "get-sound-dose-value":
+ return getSoundDoseValue();
+ case "reset-sound-dose-timeout":
+ return resetSoundDoseTimeout();
}
return 0;
}
@@ -66,6 +72,12 @@
pw.println(" Sets the encoded surround sound mode to SURROUND_SOUND_MODE");
pw.println(" get-encoded-surround-mode");
pw.println(" Returns the encoded surround sound mode");
+ pw.println(" set-sound-dose-value");
+ pw.println(" Sets the current sound dose value");
+ pw.println(" get-sound-dose-value");
+ pw.println(" Returns the current sound dose value");
+ pw.println(" reset-sound-dose-timeout");
+ pw.println(" Resets the sound dose timeout used for momentary exposure");
}
private int setSurroundFormatEnabled() {
@@ -162,4 +174,46 @@
getOutPrintWriter().println("Encoded surround mode: " + am.getEncodedSurroundMode());
return 0;
}
+
+ private int setSoundDoseValue() {
+ String soundDoseValueText = getNextArg();
+
+ if (soundDoseValueText == null) {
+ getErrPrintWriter().println("Error: no sound dose value specified");
+ return 1;
+ }
+
+ float soundDoseValue = 0.f;
+ try {
+ soundDoseValue = Float.parseFloat(soundDoseValueText);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: wrong format specified for sound dose");
+ return 1;
+ }
+
+ if (soundDoseValue < 0) {
+ getErrPrintWriter().println("Error: invalid value of sound dose");
+ return 1;
+ }
+
+ final Context context = mService.mContext;
+ final AudioManager am = context.getSystemService(AudioManager.class);
+ am.setCsd(soundDoseValue);
+ return 0;
+ }
+
+ private int getSoundDoseValue() {
+ final Context context = mService.mContext;
+ final AudioManager am = context.getSystemService(AudioManager.class);
+ getOutPrintWriter().println("Sound dose value: " + am.getCsd());
+ return 0;
+ }
+
+ private int resetSoundDoseTimeout() {
+ final Context context = mService.mContext;
+ final AudioManager am = context.getSystemService(AudioManager.class);
+ am.setCsd(-1.f);
+ getOutPrintWriter().println("Reset sound dose momentary exposure timeout");
+ return 0;
+ }
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 0aa9cc1..3243385 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -36,7 +36,6 @@
import static android.provider.Settings.Secure.VOLUME_HUSH_MUTE;
import static android.provider.Settings.Secure.VOLUME_HUSH_OFF;
import static android.provider.Settings.Secure.VOLUME_HUSH_VIBRATE;
-
import static com.android.server.audio.SoundDoseHelper.ACTION_CHECK_MUSIC_ACTIVE;
import static com.android.server.utils.EventLogger.Event.ALOGE;
import static com.android.server.utils.EventLogger.Event.ALOGI;
@@ -73,7 +72,6 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -267,6 +265,8 @@
private final SettingsAdapter mSettings;
private final AudioPolicyFacade mAudioPolicy;
+ private final MusicFxHelper mMusicFxHelper;
+
/** Debug audio mode */
protected static final boolean DEBUG_MODE = false;
@@ -407,9 +407,15 @@
private static final int MSG_CONFIGURATION_CHANGED = 54;
private static final int MSG_BROADCAST_MASTER_MUTE = 55;
- /** Messages handled by the {@link SoundDoseHelper}. */
+ /**
+ * Messages handled by the {@link SoundDoseHelper}, do not exceed
+ * {@link MUSICFX_HELPER_MSG_START}.
+ */
/*package*/ static final int SAFE_MEDIA_VOLUME_MSG_START = 1000;
+ /** Messages handled by the {@link MusicFxHelper}. */
+ /*package*/ static final int MUSICFX_HELPER_MSG_START = 1100;
+
// start of messages handled under wakelock
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
// and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -1304,6 +1310,8 @@
0 /* arg1 */, 0 /* arg2 */, null /* obj */, 0 /* delay */);
mDisplayManager = context.getSystemService(DisplayManager.class);
+
+ mMusicFxHelper = new MusicFxHelper(mContext, mAudioHandler);
}
private void initVolumeStreamStates() {
@@ -9456,11 +9464,21 @@
onConfigurationChanged();
break;
+ case MusicFxHelper.MSG_EFFECT_CLIENT_GONE:
+ mMusicFxHelper.handleMessage(msg);
+ break;
+
+ case SoundDoseHelper.MSG_CONFIGURE_SAFE_MEDIA:
+ case SoundDoseHelper.MSG_CONFIGURE_SAFE_MEDIA_FORCED:
+ case SoundDoseHelper.MSG_PERSIST_SAFE_VOLUME_STATE:
+ case SoundDoseHelper.MSG_PERSIST_MUSIC_ACTIVE_MS:
+ case SoundDoseHelper.MSG_PERSIST_CSD_VALUES:
+ case SoundDoseHelper.MSG_CSD_UPDATE_ATTENUATION:
+ mSoundDoseHelper.handleMessage(msg);
+ break;
+
default:
- if (msg.what >= SAFE_MEDIA_VOLUME_MSG_START) {
- // msg could be for the SoundDoseHelper
- mSoundDoseHelper.handleMessage(msg);
- }
+ Log.e(TAG, "Unsupported msgId " + msg.what);
}
}
}
@@ -9695,7 +9713,7 @@
}
} else if (action.equals(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION) ||
action.equals(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION)) {
- handleAudioEffectBroadcast(context, intent);
+ mMusicFxHelper.handleAudioEffectBroadcast(context, intent);
} else if (action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
final int[] suspendedUids = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
final String[] suspendedPackages =
@@ -9750,27 +9768,6 @@
}
} // end class AudioServiceUserRestrictionsListener
- private void handleAudioEffectBroadcast(Context context, Intent intent) {
- String target = intent.getPackage();
- if (target != null) {
- Log.w(TAG, "effect broadcast already targeted to " + target);
- return;
- }
- intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
- // TODO this should target a user-selected panel
- List<ResolveInfo> ril = context.getPackageManager().queryBroadcastReceivers(
- intent, 0 /* flags */);
- if (ril != null && ril.size() != 0) {
- ResolveInfo ri = ril.get(0);
- if (ri != null && ri.activityInfo != null && ri.activityInfo.packageName != null) {
- intent.setPackage(ri.activityInfo.packageName);
- context.sendBroadcastAsUser(intent, UserHandle.ALL);
- return;
- }
- }
- Log.w(TAG, "couldn't find receiver package for effect intent");
- }
-
private void killBackgroundUserProcessesWithRecordAudioPermission(UserInfo oldUser) {
PackageManager pm = mContext.getPackageManager();
// Find the home activity of the user. It should not be killed to avoid expensive restart,
diff --git a/services/core/java/com/android/server/audio/MusicFxHelper.java b/services/core/java/com/android/server/audio/MusicFxHelper.java
new file mode 100644
index 0000000..6c0fef5
--- /dev/null
+++ b/services/core/java/com/android/server/audio/MusicFxHelper.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.audio;
+
+import static android.content.pm.PackageManager.MATCH_ANY_USER;
+import static com.android.server.audio.AudioService.MUSICFX_HELPER_MSG_START;
+
+import android.annotation.NonNull;
+import android.app.ActivityManager;
+import android.app.IUidObserver;
+import android.app.UidObserver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
+import android.media.AudioManager;
+import android.media.audiofx.AudioEffect;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.audio.AudioService.AudioHandler;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * MusicFx management.
+ * .
+ */
+public class MusicFxHelper {
+ private static final String TAG = "AS.MusicFxHelper";
+
+ @NonNull private final Context mContext;
+
+ @NonNull private final AudioHandler mAudioHandler;
+
+ // Synchronization UidSessionMap access between UidObserver and AudioServiceBroadcastReceiver.
+ private final Object mClientUidMapLock = new Object();
+
+ // The binder token identifying the UidObserver registration.
+ private IBinder mUidObserverToken = null;
+
+ // Hashmap of UID and list of open sessions for this UID.
+ @GuardedBy("mClientUidMapLock")
+ private SparseArray<List<Integer>> mClientUidSessionMap = new SparseArray<>();
+
+ /*package*/ static final int MSG_EFFECT_CLIENT_GONE = MUSICFX_HELPER_MSG_START + 1;
+
+ // UID observer for effect MusicFx clients
+ private final IUidObserver mEffectUidObserver = new UidObserver() {
+ @Override public void onUidGone(int uid, boolean disabled) {
+ Log.w(TAG, " send MSG_EFFECT_CLIENT_GONE");
+ mAudioHandler.sendMessageAtTime(
+ mAudioHandler.obtainMessage(MSG_EFFECT_CLIENT_GONE,
+ uid /* arg1 */, 0 /* arg2 */,
+ null /* obj */), 0 /* delay */);
+ }
+ };
+
+ // BindService connection implementation, we don't need any implementation now
+ private ServiceConnection mMusicFxBindConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ Log.d(TAG, " service connected to " + name);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Log.d(TAG, " service disconnected from " + name);
+ }
+ };
+
+ MusicFxHelper(@NonNull Context context, @NonNull AudioHandler audioHandler) {
+ mContext = context;
+ mAudioHandler = audioHandler;
+ }
+
+ /**
+ * Handle the broadcast {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} and
+ * {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION} intents.
+ *
+ * If the intent is {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION}:
+ * - If the MusicFx process is not running, call bindService with AUTO_CREATE to create.
+ * - If this is the first audio session in MusicFx, call set foreground service delegate.
+ * - If this is the first audio session for a given UID, add the UID into observer.
+ *
+ * If the intent is {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION}:
+ * - MusicFx will not be foreground delegated anymore.
+ * - The KeepAliveService of MusicFx will be unbound.
+ * - The UidObserver will be removed.
+ */
+ public void handleAudioEffectBroadcast(Context context, Intent intent) {
+ String target = intent.getPackage();
+ if (target != null) {
+ Log.w(TAG, "effect broadcast already targeted to " + target);
+ return;
+ }
+ intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
+ final PackageManager pm = context.getPackageManager();
+ // TODO this should target a user-selected panel
+ List<ResolveInfo> ril = pm.queryBroadcastReceivers(intent, 0 /* flags */);
+ if (ril != null && ril.size() != 0) {
+ ResolveInfo ri = ril.get(0);
+ final String senderPackageName = intent.getStringExtra(AudioEffect.EXTRA_PACKAGE_NAME);
+ try {
+ final int senderUid = pm.getPackageUidAsUser(senderPackageName,
+ PackageManager.PackageInfoFlags.of(MATCH_ANY_USER), getCurrentUserId());
+ if (ri != null && ri.activityInfo != null && ri.activityInfo.packageName != null) {
+ intent.setPackage(ri.activityInfo.packageName);
+ synchronized (mClientUidMapLock) {
+ setMusicFxServiceWithObserver(context, intent, senderUid);
+ }
+ context.sendBroadcastAsUser(intent, UserHandle.ALL);
+ return;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Not able to find UID from package: " + senderPackageName + " error: "
+ + e);
+ }
+ }
+ Log.w(TAG, "couldn't find receiver package for effect intent");
+ }
+
+ /**
+ * Handle the UidObserver onUidGone callback of MusicFx clients.
+ * All open audio sessions of this UID will be closed.
+ * If this is the last UID for MusicFx:
+ * - MusicFx will not be foreground delegated anymore.
+ * - The KeepAliveService of MusicFx will be unbound.
+ * - The UidObserver will be removed.
+ */
+ public void handleEffectClientUidGone(int uid) {
+ synchronized (mClientUidMapLock) {
+ Log.w(TAG, " inside handle MSG_EFFECT_CLIENT_GONE");
+ // Once the uid is no longer running, close all remain audio session(s) for this UID
+ if (mClientUidSessionMap.get(Integer.valueOf(uid)) != null) {
+ final List<Integer> sessions = mClientUidSessionMap.get(Integer.valueOf(uid));
+ Log.i(TAG, "UID " + uid + " gone, closing " + sessions.size() + " sessions");
+ for (Integer session : sessions) {
+ Intent intent = new Intent(
+ AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION);
+ intent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, session);
+ setMusicFxServiceWithObserver(mContext, intent, uid);
+ Log.i(TAG, "Close session " + session + " of UID " + uid);
+ }
+ mClientUidSessionMap.remove(Integer.valueOf(uid));
+ }
+ }
+ }
+
+ @GuardedBy("mClientUidMapLock")
+ private void setMusicFxServiceWithObserver(Context context, Intent intent, int senderUid) {
+ PackageManager pm = context.getPackageManager();
+ try {
+ final int audioSession = intent.getIntExtra(AudioEffect.EXTRA_AUDIO_SESSION,
+ AudioManager.AUDIO_SESSION_ID_GENERATE);
+ if (AudioManager.AUDIO_SESSION_ID_GENERATE == audioSession) {
+ Log.e(TAG, "Intent missing audio session: " + audioSession);
+ return;
+ }
+
+ // only apply to com.android.musicfx and KeepAliveService for now
+ final String musicFxPackageName = "com.android.musicfx";
+ final String musicFxKeepAliveService = "com.android.musicfx.KeepAliveService";
+ final int musicFxUid = pm.getPackageUidAsUser(musicFxPackageName,
+ PackageManager.PackageInfoFlags.of(MATCH_ANY_USER), getCurrentUserId());
+
+ if (intent.getAction().equals(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION)) {
+ List<Integer> sessions = new ArrayList<>();
+ Log.d(TAG, "UID " + senderUid + ", open MusicFx session " + audioSession);
+ // start foreground service delegate and register UID observer with the first
+ // session of first UID open
+ if (0 == mClientUidSessionMap.size()) {
+ final int procState = ActivityManager.getService().getPackageProcessState(
+ musicFxPackageName, this.getClass().getPackage().getName());
+ // if musicfx process not in binding state, call bindService with AUTO_CREATE
+ if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
+ Intent bindIntent = new Intent().setClassName(musicFxPackageName,
+ musicFxKeepAliveService);
+ context.bindServiceAsUser(
+ bindIntent, mMusicFxBindConnection, Context.BIND_AUTO_CREATE,
+ UserHandle.of(getCurrentUserId()));
+ Log.i(TAG, "bindService to " + musicFxPackageName);
+ }
+
+ Log.i(TAG, "Package " + musicFxPackageName + " uid " + musicFxUid
+ + " procState " + procState);
+ } else if (mClientUidSessionMap.get(Integer.valueOf(senderUid)) != null) {
+ sessions = mClientUidSessionMap.get(Integer.valueOf(senderUid));
+ if (sessions.contains(audioSession)) {
+ Log.e(TAG, "Audio session " + audioSession + " already exist for UID "
+ + senderUid + ", abort");
+ return;
+ }
+ }
+ // first session of this UID
+ if (sessions.size() == 0) {
+ // call registerUidObserverForUids with the first UID and first session
+ if (mClientUidSessionMap.size() == 0 || mUidObserverToken == null) {
+ mUidObserverToken = ActivityManager.getService().registerUidObserverForUids(
+ mEffectUidObserver, ActivityManager.UID_OBSERVER_GONE,
+ ActivityManager.PROCESS_STATE_UNKNOWN, null, new int[]{senderUid});
+ Log.i(TAG, "UID " + senderUid + " registered to observer");
+ } else {
+ // add UID to observer for each new UID
+ ActivityManager.getService().addUidToObserver(mUidObserverToken, TAG,
+ senderUid);
+ Log.i(TAG, "UID " + senderUid + " addeded to observer");
+ }
+ }
+
+ sessions.add(Integer.valueOf(audioSession));
+ mClientUidSessionMap.put(Integer.valueOf(senderUid), sessions);
+ } else {
+ if (mClientUidSessionMap.get(senderUid) != null) {
+ Log.d(TAG, "UID " + senderUid + ", close MusicFx session " + audioSession);
+ List<Integer> sessions = mClientUidSessionMap.get(Integer.valueOf(senderUid));
+ sessions.remove(Integer.valueOf(audioSession));
+ if (0 == sessions.size()) {
+ mClientUidSessionMap.remove(Integer.valueOf(senderUid));
+ } else {
+ mClientUidSessionMap.put(Integer.valueOf(senderUid), sessions);
+ }
+
+ // stop foreground service delegate and unregister UID observer with the
+ // last session of last UID close
+ if (0 == mClientUidSessionMap.size()) {
+ ActivityManager.getService().unregisterUidObserver(mEffectUidObserver);
+ mClientUidSessionMap.clear();
+ context.unbindService(mMusicFxBindConnection);
+ Log.i(TAG, " remove all sessions, unregister UID observer, and unbind "
+ + musicFxPackageName);
+ }
+ } else {
+ // if the audio session already closed, print an error
+ Log.e(TAG, "UID " + senderUid + " close audio session " + audioSession
+ + " which does not exist");
+ }
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Not able to find UID from package: " + e);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException " + e + " with handling intent");
+ }
+ }
+
+ private int getCurrentUserId() {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ UserInfo currentUser = ActivityManager.getService().getCurrentUser();
+ return currentUser.id;
+ } catch (RemoteException e) {
+ // Activity manager not running, nothing we can do assume user 0.
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ return UserHandle.USER_SYSTEM;
+ }
+
+ /*package*/ void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_EFFECT_CLIENT_GONE:
+ Log.w(TAG, " handle MSG_EFFECT_CLIENT_GONE");
+ handleEffectClientUidGone(msg.arg1 /* uid */);
+ break;
+ default:
+ Log.e(TAG, "Unexpected msg to handle in MusicFxHelper: " + msg.what);
+ break;
+ }
+ }
+
+}
diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java
index 81365bf..6c5f3e7 100644
--- a/services/core/java/com/android/server/audio/SoundDoseHelper.java
+++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java
@@ -108,11 +108,11 @@
private static final int SAFE_MEDIA_VOLUME_INACTIVE = 2; // confirmed
private static final int SAFE_MEDIA_VOLUME_ACTIVE = 3; // unconfirmed
- private static final int MSG_CONFIGURE_SAFE_MEDIA = SAFE_MEDIA_VOLUME_MSG_START + 1;
- private static final int MSG_CONFIGURE_SAFE_MEDIA_FORCED = SAFE_MEDIA_VOLUME_MSG_START + 2;
- private static final int MSG_PERSIST_SAFE_VOLUME_STATE = SAFE_MEDIA_VOLUME_MSG_START + 3;
- private static final int MSG_PERSIST_MUSIC_ACTIVE_MS = SAFE_MEDIA_VOLUME_MSG_START + 4;
- private static final int MSG_PERSIST_CSD_VALUES = SAFE_MEDIA_VOLUME_MSG_START + 5;
+ /*package*/ static final int MSG_CONFIGURE_SAFE_MEDIA = SAFE_MEDIA_VOLUME_MSG_START + 1;
+ /*package*/ static final int MSG_CONFIGURE_SAFE_MEDIA_FORCED = SAFE_MEDIA_VOLUME_MSG_START + 2;
+ /*package*/ static final int MSG_PERSIST_SAFE_VOLUME_STATE = SAFE_MEDIA_VOLUME_MSG_START + 3;
+ /*package*/ static final int MSG_PERSIST_MUSIC_ACTIVE_MS = SAFE_MEDIA_VOLUME_MSG_START + 4;
+ /*package*/ static final int MSG_PERSIST_CSD_VALUES = SAFE_MEDIA_VOLUME_MSG_START + 5;
/*package*/ static final int MSG_CSD_UPDATE_ATTENUATION = SAFE_MEDIA_VOLUME_MSG_START + 6;
private static final int UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX = (20 * 3600 * 1000); // 20 hours
diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java
index d8831fa..03b0cfc 100644
--- a/services/core/java/com/android/server/display/color/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java
@@ -78,6 +78,7 @@
import com.android.server.DisplayThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.twilight.TwilightListener;
import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightState;
@@ -148,10 +149,12 @@
1f, 1f, 1f, 1f
};
+ private final DisplayManagerFlags mDisplayManagerFlags = new DisplayManagerFlags();
+
@VisibleForTesting
final DisplayWhiteBalanceTintController mDisplayWhiteBalanceTintController =
new DisplayWhiteBalanceTintController(
- LocalServices.getService(DisplayManagerInternal.class));
+ LocalServices.getService(DisplayManagerInternal.class), mDisplayManagerFlags);
private final NightDisplayTintController mNightDisplayTintController =
new NightDisplayTintController();
private final TintController mGlobalSaturationTintController =
@@ -716,15 +719,16 @@
tintController.computeMatrixForCct(to));
tintController.setAppliedCct(to);
} else {
+ final long duration = tintController.getTransitionDurationMilliseconds(to > from);
Slog.d(TAG, tintController.getClass().getSimpleName() + " animation started: toCct="
- + to + " fromCct=" + from);
+ + to + " fromCct=" + from + " with duration=" + duration);
ValueAnimator valueAnimator = ValueAnimator.ofInt(from, to);
tintController.setAnimator(valueAnimator);
final CctEvaluator evaluator = tintController.getEvaluator();
if (evaluator != null) {
valueAnimator.setEvaluator(evaluator);
}
- valueAnimator.setDuration(tintController.getTransitionDurationMilliseconds());
+ valueAnimator.setDuration(duration);
valueAnimator.setInterpolator(AnimationUtils.loadInterpolator(
getContext(), android.R.interpolator.linear));
valueAnimator.addUpdateListener((ValueAnimator animator) -> {
diff --git a/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java b/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java
index bf0139f..b9fd1d0 100644
--- a/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java
+++ b/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java
@@ -34,6 +34,7 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.feature.DisplayManagerFlags;
import java.io.PrintWriter;
@@ -65,6 +66,8 @@
boolean mSetUp = false;
private final float[] mMatrixDisplayWhiteBalance = new float[16];
private long mTransitionDuration;
+ private long mTransitionDurationIncrease;
+ private long mTransitionDurationDecrease;
private Boolean mIsAvailable;
// This feature becomes disallowed if the device is in an unsupported strong/light state.
private boolean mIsAllowed = true;
@@ -74,8 +77,12 @@
private final DisplayManagerInternal mDisplayManagerInternal;
- DisplayWhiteBalanceTintController(DisplayManagerInternal dm) {
+ private final DisplayManagerFlags mDisplayManagerFlags;
+
+ DisplayWhiteBalanceTintController(DisplayManagerInternal dm,
+ DisplayManagerFlags displayManagerFlags) {
mDisplayManagerInternal = dm;
+ mDisplayManagerFlags = displayManagerFlags;
}
@Override
@@ -139,6 +146,16 @@
mTransitionDuration = res.getInteger(
R.integer.config_displayWhiteBalanceTransitionTime);
+ if (!mDisplayManagerFlags.isAdaptiveTone2Enabled()) {
+ mTransitionDurationDecrease = mTransitionDuration;
+ mTransitionDurationIncrease = mTransitionDuration;
+ } else {
+ mTransitionDurationIncrease = res.getInteger(
+ R.integer.config_displayWhiteBalanceTransitionTimeIncrease);
+ mTransitionDurationDecrease = res.getInteger(
+ R.integer.config_displayWhiteBalanceTransitionTimeDecrease);
+ }
+
int[] cctRangeMinimums = res.getIntArray(
R.array.config_displayWhiteBalanceDisplayRangeMinimums);
int[] steps = res.getIntArray(R.array.config_displayWhiteBalanceDisplaySteps);
@@ -323,6 +340,11 @@
}
@Override
+ public long getTransitionDurationMilliseconds(boolean isIncreasing) {
+ return isIncreasing ? mTransitionDurationIncrease : mTransitionDurationDecrease;
+ }
+
+ @Override
public void dump(PrintWriter pw) {
synchronized (mLock) {
pw.println(" mSetUp = " + mSetUp);
@@ -348,6 +370,9 @@
pw.println(" mMatrixDisplayWhiteBalance = "
+ matrixToString(mMatrixDisplayWhiteBalance, 4));
pw.println(" mIsAllowed = " + mIsAllowed);
+ pw.println(" mTransitionDuration = " + mTransitionDuration);
+ pw.println(" mTransitionDurationIncrease = " + mTransitionDurationIncrease);
+ pw.println(" mTransitionDurationDecrease = " + mTransitionDurationDecrease);
}
}
diff --git a/services/core/java/com/android/server/display/color/TintController.java b/services/core/java/com/android/server/display/color/TintController.java
index 384333a..716661d 100644
--- a/services/core/java/com/android/server/display/color/TintController.java
+++ b/services/core/java/com/android/server/display/color/TintController.java
@@ -75,6 +75,10 @@
return TRANSITION_DURATION;
}
+ public long getTransitionDurationMilliseconds(boolean direction) {
+ return TRANSITION_DURATION;
+ }
+
/**
* Dump debug information.
*/
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index 738368b..7050c5a 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -47,6 +47,10 @@
Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1,
Flags::enableAdaptiveToneImprovements1);
+ private final FlagState mAdaptiveToneImprovements2 = new FlagState(
+ Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_2,
+ Flags::enableAdaptiveToneImprovements2);
+
private final FlagState mDisplayOffloadFlagState = new FlagState(
Flags.FLAG_ENABLE_DISPLAY_OFFLOAD,
Flags::enableDisplayOffload);
@@ -84,6 +88,13 @@
return mAdaptiveToneImprovements1.isEnabled();
}
+ /**
+ * Returns whether adaptive tone improvements are enabled
+ */
+ public boolean isAdaptiveTone2Enabled() {
+ return mAdaptiveToneImprovements2.isEnabled();
+ }
+
/** Returns whether resolution range voting feature is enabled or not. */
public boolean isDisplayResolutionRangeVotingEnabled() {
return isExternalDisplayLimitModeEnabled();
@@ -138,7 +149,6 @@
mFlagFunction = flagFunction;
}
- // TODO(b/297159910): Simplify using READ-ONLY flags when available.
private boolean isEnabled() {
if (mEnabledSet) {
if (DEBUG) {
@@ -155,14 +165,7 @@
}
private boolean flagOrSystemProperty(Supplier<Boolean> flagFunction, String flagName) {
- boolean flagValue = false;
- try {
- flagValue = flagFunction.get();
- } catch (Throwable ex) {
- if (DEBUG) {
- Slog.i(TAG, "Flags not ready yet. Return false for " + flagName, ex);
- }
- }
+ boolean flagValue = flagFunction.get();
// TODO(b/299462337) Remove when the infrastructure is ready.
if (Build.IS_ENG || Build.IS_USERDEBUG) {
return SystemProperties.getBoolean("persist.sys." + flagName + "-override",
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index a309913..a85e10d 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -35,6 +35,14 @@
}
flag {
+ name: "enable_adaptive_tone_improvements_2"
+ namespace: "display_manager"
+ description: "Feature flag for Further Adaptive Tone Improvements"
+ bug: "294762632"
+ is_fixed_read_only: true
+}
+
+flag {
name: "enable_display_resolution_range_voting"
namespace: "display_manager"
description: "Feature flag to enable voting for ranges of resolutions"
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
index c6a50ed..7b844a0 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
@@ -48,6 +48,7 @@
import com.android.server.LocalServices;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
+import com.android.text.flags.Flags;
import java.io.File;
import java.io.FileDescriptor;
@@ -240,21 +241,35 @@
mContext = context;
mIsSafeMode = safeMode;
- SystemServerInitThreadPool.submit(() -> {
- initialize();
+ if (Flags.useOptimizedBoottimeFontLoading()) {
+ Slog.i(TAG, "Using optimized boot-time font loading.");
+ SystemServerInitThreadPool.submit(() -> {
+ initialize();
- // Set system font map only if there is updatable font directory.
- // If there is no updatable font directory, `initialize` will have already loaded the
- // system font map, so there's no need to set the system font map again here.
- if (mUpdatableFontDir != null) {
- try {
- Typeface.setSystemFontMap(getCurrentFontMap());
- } catch (IOException | ErrnoException e) {
- Slog.w(TAG, "Failed to set system font map of system_server");
+ // Set system font map only if there is updatable font directory.
+ // If there is no updatable font directory, `initialize` will have already loaded
+ // the system font map, so there's no need to set the system font map again here.
+ synchronized (mUpdatableFontDirLock) {
+ if (mUpdatableFontDir != null) {
+ setSystemFontMap();
+ }
}
- }
+ serviceStarted.complete(null);
+ }, "FontManagerService_create");
+ } else {
+ Slog.i(TAG, "Not using optimized boot-time font loading.");
+ initialize();
+ setSystemFontMap();
serviceStarted.complete(null);
- }, "FontManagerService_create");
+ }
+ }
+
+ private void setSystemFontMap() {
+ try {
+ Typeface.setSystemFontMap(getCurrentFontMap());
+ } catch (IOException | ErrnoException e) {
+ Slog.w(TAG, "Failed to set system font map of system_server");
+ }
}
@Nullable
@@ -291,9 +306,11 @@
synchronized (mUpdatableFontDirLock) {
mUpdatableFontDir = createUpdatableFontDir();
if (mUpdatableFontDir == null) {
- // If fs-verity is not supported, load preinstalled system font map and use it for
- // all apps.
- Typeface.loadPreinstalledSystemFontMap();
+ if (Flags.useOptimizedBoottimeFontLoading()) {
+ // If fs-verity is not supported, load preinstalled system font map and use it
+ // for all apps.
+ Typeface.loadPreinstalledSystemFontMap();
+ }
setSerializedFontMap(serializeSystemServerFontMap());
return;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 429db5e..c28a68b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -4951,6 +4951,11 @@
AudioDeviceAttributes attributes = new AudioDeviceAttributes(
AudioDeviceAttributes.ROLE_OUTPUT, AudioDeviceInfo.TYPE_HDMI_EARC, "", "",
new ArrayList<AudioProfile>(), audioDescriptors);
+ // Set SAM to ON whenever CEC is disabled. Failure to do so may result in the absence
+ // of sound when CEC is disabled and eARC is enabled due to SAM being in the off state.
+ if (!isCecControlEnabled()) {
+ setSystemAudioActivated(true);
+ }
getAudioManager().setWiredDeviceConnectionState(attributes, enabled ? 1 : 0);
}
diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
index bad6bf0..8580b96 100644
--- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java
+++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
@@ -1246,11 +1246,17 @@
isFirstConfiguration);
for (int i = 0; i < imeInfoList.size(); i++) {
KeyboardLayoutInfo layoutInfo = layoutInfoList.get(i);
- boolean noLayoutFound = layoutInfo == null || layoutInfo.mDescriptor == null;
- configurationEventBuilder.addLayoutSelection(imeInfoList.get(i).mImeSubtype,
- noLayoutFound ? null : getKeyboardLayout(layoutInfo.mDescriptor),
- noLayoutFound ? LAYOUT_SELECTION_CRITERIA_DEFAULT
- : layoutInfo.mSelectionCriteria);
+ String layoutName = null;
+ int layoutSelectionCriteria = LAYOUT_SELECTION_CRITERIA_DEFAULT;
+ if (layoutInfo != null && layoutInfo.mDescriptor != null) {
+ layoutSelectionCriteria = layoutInfo.mSelectionCriteria;
+ KeyboardLayoutDescriptor d = KeyboardLayoutDescriptor.parse(layoutInfo.mDescriptor);
+ if (d != null) {
+ layoutName = d.keyboardLayoutName;
+ }
+ }
+ configurationEventBuilder.addLayoutSelection(imeInfoList.get(i).mImeSubtype, layoutName,
+ layoutSelectionCriteria);
}
KeyboardMetricsCollector.logKeyboardConfiguredAtom(configurationEventBuilder.build());
}
diff --git a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
index 08e5977..2dd2a16 100644
--- a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
+++ b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
@@ -491,7 +491,7 @@
private final InputDevice mInputDevice;
private boolean mIsFirstConfiguration;
private final List<InputMethodSubtype> mImeSubtypeList = new ArrayList<>();
- private final List<KeyboardLayout> mSelectedLayoutList = new ArrayList<>();
+ private final List<String> mSelectedLayoutList = new ArrayList<>();
private final List<Integer> mLayoutSelectionCriteriaList = new ArrayList<>();
public Builder(@NonNull InputDevice inputDevice) {
@@ -511,7 +511,7 @@
* Adds keyboard layout configuration info for a particular IME subtype language
*/
public Builder addLayoutSelection(@NonNull InputMethodSubtype imeSubtype,
- @Nullable KeyboardLayout selectedLayout,
+ @Nullable String selectedLayout,
@LayoutSelectionCriteria int layoutSelectionCriteria) {
Objects.requireNonNull(imeSubtype, "IME subtype provided should not be null");
if (!isValidSelectionCriteria(layoutSelectionCriteria)) {
@@ -533,7 +533,6 @@
}
List<LayoutConfiguration> configurationList = new ArrayList<>();
for (int i = 0; i < size; i++) {
- KeyboardLayout selectedLayout = mSelectedLayoutList.get(i);
@LayoutSelectionCriteria int layoutSelectionCriteria =
mLayoutSelectionCriteriaList.get(i);
InputMethodSubtype imeSubtype = mImeSubtypeList.get(i);
@@ -552,9 +551,9 @@
imeSubtype.getPhysicalKeyboardHintLayoutType());
// Sanitize null values
- String keyboardLayoutName =
- selectedLayout == null ? DEFAULT_LAYOUT_NAME
- : selectedLayout.getLabel();
+ String keyboardLayoutName = mSelectedLayoutList.get(i) == null
+ ? DEFAULT_LAYOUT_NAME
+ : mSelectedLayoutList.get(i);
configurationList.add(
new LayoutConfiguration(keyboardLayoutType, keyboardLanguageTag,
diff --git a/services/core/java/com/android/server/locales/LocaleManagerService.java b/services/core/java/com/android/server/locales/LocaleManagerService.java
index d4578dc..4851a81 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerService.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerService.java
@@ -486,9 +486,12 @@
Settings.Secure.DEFAULT_INPUT_METHOD,
userId);
if (!TextUtils.isEmpty(currentInputMethod)) {
- String inputMethodPkgName = ComponentName
- .unflattenFromString(currentInputMethod)
- .getPackageName();
+ ComponentName componentName = ComponentName.unflattenFromString(currentInputMethod);
+ if (componentName == null) {
+ Slog.d(TAG, "inValid input method");
+ return false;
+ }
+ String inputMethodPkgName = componentName.getPackageName();
int inputMethodUid = getPackageUid(inputMethodPkgName, userId);
return inputMethodUid >= 0 && UserHandle.isSameApp(Binder.getCallingUid(),
inputMethodUid);
diff --git a/services/core/java/com/android/server/media/AudioAttributesUtils.java b/services/core/java/com/android/server/media/AudioAttributesUtils.java
index b9c9bae..5d5d59b 100644
--- a/services/core/java/com/android/server/media/AudioAttributesUtils.java
+++ b/services/core/java/com/android/server/media/AudioAttributesUtils.java
@@ -48,6 +48,8 @@
case AudioDeviceInfo.TYPE_DOCK_ANALOG:
return MediaRoute2Info.TYPE_DOCK;
case AudioDeviceInfo.TYPE_HDMI:
+ case AudioDeviceInfo.TYPE_HDMI_ARC:
+ case AudioDeviceInfo.TYPE_HDMI_EARC:
return MediaRoute2Info.TYPE_HDMI;
case AudioDeviceInfo.TYPE_USB_DEVICE:
return MediaRoute2Info.TYPE_USB_DEVICE;
@@ -81,6 +83,8 @@
case AudioDeviceInfo.TYPE_DOCK:
case AudioDeviceInfo.TYPE_DOCK_ANALOG:
case AudioDeviceInfo.TYPE_HDMI:
+ case AudioDeviceInfo.TYPE_HDMI_ARC:
+ case AudioDeviceInfo.TYPE_HDMI_EARC:
case AudioDeviceInfo.TYPE_USB_DEVICE:
return true;
default:
diff --git a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
index 75a0cf5..6b7db2d 100644
--- a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
+++ b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
@@ -38,6 +38,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.media.AudioAttributes;
@@ -48,6 +49,7 @@
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.os.UserManager;
import android.os.VibrationEffect;
import android.provider.Settings;
import android.telephony.PhoneStateListener;
@@ -61,18 +63,21 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
+import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.server.EventLogTags;
import com.android.server.lights.LightsManager;
import com.android.server.lights.LogicalLight;
+import com.android.server.notification.Flags;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -87,15 +92,24 @@
static final boolean DEBUG_INTERRUPTIVENESS = SystemProperties.getBoolean(
"debug.notification.interruptiveness", false);
+ private static final float DEFAULT_VOLUME = 1.0f;
+ // TODO (b/291899544): remove for release
+ private static final String POLITE_STRATEGY1 = "rule1";
+ private static final String POLITE_STRATEGY2 = "rule2";
+ private static final int DEFAULT_NOTIFICATION_COOLDOWN_ENABLED = 1;
+ private static final int DEFAULT_NOTIFICATION_COOLDOWN_ENABLED_FOR_WORK = 0;
+ private static final int DEFAULT_NOTIFICATION_COOLDOWN_ALL = 1;
+ private static final int DEFAULT_NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED = 0;
+
private final Context mContext;
private final PackageManager mPackageManager;
private final TelephonyManager mTelephonyManager;
+ private final UserManager mUm;
private final NotificationManagerPrivate mNMP;
private final SystemUiSystemPropertiesFlags.FlagResolver mFlagResolver;
private AccessibilityManager mAccessibilityManager;
private KeyguardManager mKeyguardManager;
private AudioManager mAudioManager;
- private final LightsManager mLightsManager;
private final NotificationUsageStats mUsageStats;
private final ZenModeHelper mZenModeHelper;
@@ -126,17 +140,26 @@
private final float mInCallNotificationVolume;
private Binder mCallNotificationToken = null;
+ // Settings flags
+ private boolean mNotificationCooldownEnabled;
+ private boolean mNotificationCooldownForWorkEnabled;
+ private boolean mNotificationCooldownApplyToAll;
+ private boolean mNotificationCooldownVibrateUnlocked;
+
+ private boolean mEnablePoliteNotificationsFeature;
+ private final PolitenessStrategy mStrategy;
+ private int mCurrentWorkProfileId = UserHandle.USER_NULL;
public NotificationAttentionHelper(Context context, LightsManager lightsManager,
AccessibilityManager accessibilityManager, PackageManager packageManager,
- NotificationUsageStats usageStats,
+ UserManager userManager, NotificationUsageStats usageStats,
NotificationManagerPrivate notificationManagerPrivate,
ZenModeHelper zenModeHelper, SystemUiSystemPropertiesFlags.FlagResolver flagResolver) {
mContext = context;
mPackageManager = packageManager;
mTelephonyManager = context.getSystemService(TelephonyManager.class);
mAccessibilityManager = accessibilityManager;
- mLightsManager = lightsManager;
+ mUm = userManager;
mNMP = notificationManagerPrivate;
mUsageStats = usageStats;
mZenModeHelper = zenModeHelper;
@@ -144,8 +167,8 @@
mVibratorHelper = new VibratorHelper(context);
- mNotificationLight = mLightsManager.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
- mAttentionLight = mLightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION);
+ mNotificationLight = lightsManager.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
+ mAttentionLight = lightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION);
Resources resources = context.getResources();
mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
@@ -169,7 +192,39 @@
.build();
mInCallNotificationVolume = resources.getFloat(R.dimen.config_inCallNotificationVolume);
+ mEnablePoliteNotificationsFeature = Flags.politeNotifications();
+
+ if (mEnablePoliteNotificationsFeature) {
+ mStrategy = getPolitenessStrategy();
+ } else {
+ mStrategy = null;
+ }
+
mSettingsObserver = new SettingsObserver();
+ loadUserSettings();
+ }
+
+ private PolitenessStrategy getPolitenessStrategy() {
+ final String politenessStrategy = mFlagResolver.getStringValue(
+ NotificationFlags.NOTIF_COOLDOWN_RULE);
+
+ if (POLITE_STRATEGY2.equals(politenessStrategy)) {
+ return new Strategy2(mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T1),
+ mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T2),
+ mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME1),
+ mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME2));
+ } else {
+ if (!POLITE_STRATEGY1.equals(politenessStrategy)) {
+ Log.w(TAG, "Invalid cooldown strategy: " + politenessStrategy + ". Defaulting to "
+ + POLITE_STRATEGY1);
+ }
+
+ return new Strategy1(mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T1),
+ mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T2),
+ mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME1),
+ mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME2),
+ mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_COUNTER_RESET));
+ }
}
public void onSystemReady() {
@@ -202,11 +257,59 @@
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
filter.addAction(Intent.ACTION_USER_PRESENT);
+ filter.addAction(Intent.ACTION_USER_ADDED);
+ filter.addAction(Intent.ACTION_USER_REMOVED);
+ filter.addAction(Intent.ACTION_USER_SWITCHED);
+ filter.addAction(Intent.ACTION_USER_UNLOCKED);
mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, filter, null, null);
mContext.getContentResolver().registerContentObserver(
SettingsObserver.NOTIFICATION_LIGHT_PULSE_URI, false, mSettingsObserver,
UserHandle.USER_ALL);
+ if (mEnablePoliteNotificationsFeature) {
+ mContext.getContentResolver().registerContentObserver(
+ SettingsObserver.NOTIFICATION_COOLDOWN_ENABLED_URI, false, mSettingsObserver,
+ UserHandle.USER_ALL);
+ mContext.getContentResolver().registerContentObserver(
+ SettingsObserver.NOTIFICATION_COOLDOWN_ALL_URI, false, mSettingsObserver,
+ UserHandle.USER_ALL);
+ mContext.getContentResolver().registerContentObserver(
+ SettingsObserver.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED_URI, false,
+ mSettingsObserver, UserHandle.USER_ALL);
+ }
+ }
+
+ private void loadUserSettings() {
+ if (mEnablePoliteNotificationsFeature) {
+ try {
+ mCurrentWorkProfileId = getManagedProfileId(ActivityManager.getCurrentUser());
+
+ mNotificationCooldownEnabled =
+ Settings.System.getIntForUser(mContext.getContentResolver(),
+ Settings.System.NOTIFICATION_COOLDOWN_ENABLED,
+ DEFAULT_NOTIFICATION_COOLDOWN_ENABLED, UserHandle.USER_CURRENT) != 0;
+ if (mCurrentWorkProfileId != UserHandle.USER_NULL) {
+ mNotificationCooldownForWorkEnabled = Settings.System.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.System.NOTIFICATION_COOLDOWN_ENABLED,
+ DEFAULT_NOTIFICATION_COOLDOWN_ENABLED_FOR_WORK, mCurrentWorkProfileId)
+ != 0;
+ } else {
+ mNotificationCooldownForWorkEnabled = false;
+ }
+ mNotificationCooldownApplyToAll = Settings.System.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.System.NOTIFICATION_COOLDOWN_ALL, DEFAULT_NOTIFICATION_COOLDOWN_ALL,
+ UserHandle.USER_CURRENT) != 0;
+ mNotificationCooldownVibrateUnlocked = Settings.System.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED,
+ DEFAULT_NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED,
+ UserHandle.USER_CURRENT) != 0;
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to read Settings: " + e);
+ }
+ }
}
@VisibleForTesting
@@ -229,6 +332,10 @@
Log.d(TAG, "buzzBeepBlinkLocked " + record);
}
+ if (isPoliteNotificationFeatureEnabled(record)) {
+ mStrategy.onNotificationPosted(record);
+ }
+
// Should this notification make noise, vibe, or use the LED?
final boolean aboveThreshold =
mIsAutomotive
@@ -269,6 +376,9 @@
vibration = mVibratorHelper.createFallbackVibration(insistent);
}
hasValidVibrate = vibration != null;
+ // Vibration-only if unlocked and Settings flag set
+ boolean vibrateOnly =
+ hasValidVibrate && mNotificationCooldownVibrateUnlocked && mUserPresent;
boolean hasAudibleAlert = hasValidSound || hasValidVibrate;
if (hasAudibleAlert && !shouldMuteNotificationLocked(record, signals)) {
if (!sentAccessibilityEvent) {
@@ -277,7 +387,7 @@
}
if (DEBUG) Slog.v(TAG, "Interrupting!");
boolean isInsistentUpdate = isInsistentUpdate(record);
- if (hasValidSound) {
+ if (hasValidSound && !vibrateOnly) {
if (isInsistentUpdate) {
// don't reset insistent sound, it's jarring
beep = true;
@@ -301,7 +411,7 @@
if (isInsistentUpdate) {
buzz = true;
} else {
- buzz = playVibration(record, vibration, hasValidSound);
+ buzz = playVibration(record, vibration, hasValidSound && !vibrateOnly);
if (buzz) {
mVibrateNotificationKey = key;
}
@@ -341,9 +451,7 @@
} else if (wasShowLights) {
updateLightsLocked();
}
- final int buzzBeepBlink =
- (buzz ? 1 : 0) | (beep ? 2 : 0) | (blink ? 4 : 0);
- if (buzzBeepBlink > 0) {
+ if (buzz || beep || blink) {
// Ignore summary updates because we don't display most of the information.
if (record.getSbn().isGroup() && record.getSbn().getNotification().isGroupSummary()) {
if (DEBUG_INTERRUPTIVENESS) {
@@ -362,15 +470,43 @@
+ record.getKey() + " is interruptive: alerted");
}
}
+ }
+ final int buzzBeepBlinkLoggingCode =
+ (buzz ? 1 : 0) | (beep ? 2 : 0) | (blink ? 4 : 0) | getPoliteBit(record);
+ if (buzzBeepBlinkLoggingCode > 0) {
MetricsLogger.action(record.getLogMaker()
.setCategory(MetricsEvent.NOTIFICATION_ALERT)
.setType(MetricsEvent.TYPE_OPEN)
- .setSubtype(buzzBeepBlink));
- EventLogTags.writeNotificationAlert(key, buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
+ .setSubtype(buzzBeepBlinkLoggingCode));
+ EventLogTags.writeNotificationAlert(key, buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0,
+ getPolitenessState(record));
}
record.setAudiblyAlerted(buzz || beep);
+ if (mEnablePoliteNotificationsFeature) {
+ // Update last alert time
+ if (buzz || beep) {
+ record.getChannel().setLastNotificationUpdateTimeMs(System.currentTimeMillis());
+ }
+ }
+ return buzzBeepBlinkLoggingCode;
+ }
- return buzzBeepBlink;
+ private int getPoliteBit(final NotificationRecord record) {
+ switch (getPolitenessState(record)) {
+ case PolitenessStrategy.POLITE_STATE_POLITE:
+ return MetricsProto.MetricsEvent.ALERT_POLITE;
+ case PolitenessStrategy.POLITE_STATE_MUTED:
+ return MetricsProto.MetricsEvent.ALERT_MUTED;
+ default:
+ return 0;
+ }
+ }
+
+ private int getPolitenessState(final NotificationRecord record) {
+ if (!isPoliteNotificationFeatureEnabled(record)) {
+ return PolitenessStrategy.POLITE_STATE_DEFAULT;
+ }
+ return mStrategy.getPolitenessState(record);
}
boolean isInsistentUpdate(final NotificationRecord record) {
@@ -468,7 +604,7 @@
+ record.getAudioAttributes());
}
player.playAsync(soundUri, record.getSbn().getUser(), looping,
- record.getAudioAttributes());
+ record.getAudioAttributes(), getSoundVolume(record));
return true;
}
} catch (RemoteException e) {
@@ -480,12 +616,56 @@
return false;
}
+ private boolean isPoliteNotificationFeatureEnabled(final NotificationRecord record) {
+ // Check feature flag
+ if (!mEnablePoliteNotificationsFeature) {
+ return false;
+ }
+
+ // The user can enable/disable notifications cooldown from the Settings app
+ if (!mNotificationCooldownEnabled) {
+ return false;
+ }
+
+ // The user can enable/disable notifications cooldown for work profile from the Settings app
+ if (isNotificationForWorkProfile(record) && !mNotificationCooldownForWorkEnabled) {
+ return false;
+ }
+
+ // The user can choose to apply cooldown for all apps/conversations only from the
+ // Settings app
+ if (!mNotificationCooldownApplyToAll && record.getChannel().getConversationId() == null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private float getSoundVolume(final NotificationRecord record) {
+ if (!isPoliteNotificationFeatureEnabled(record)) {
+ return DEFAULT_VOLUME;
+ }
+
+ return mStrategy.getSoundVolume(record);
+ }
+
+ private float getVibrationIntensity(final NotificationRecord record) {
+ if (!isPoliteNotificationFeatureEnabled(record)) {
+ return DEFAULT_VOLUME;
+ }
+
+ return mStrategy.getVibrationIntensity(record);
+ }
+
private boolean playVibration(final NotificationRecord record, final VibrationEffect effect,
boolean delayVibForSound) {
// Escalate privileges so we can use the vibrator even if the
// notifying app does not have the VIBRATE permission.
final long identity = Binder.clearCallingIdentity();
try {
+ final float scale = getVibrationIntensity(record);
+ final VibrationEffect scaledEffect = Float.compare(scale, DEFAULT_VOLUME) != 0
+ ? mVibratorHelper.scale(effect, scale) : effect;
if (delayVibForSound) {
new Thread(() -> {
// delay the vibration by the same amount as the notification sound
@@ -503,7 +683,7 @@
// so need to check that the notification is still valid for vibrate.
if (mNMP.getNotificationByKey(record.getKey()) != null) {
if (record.getKey().equals(mVibrateNotificationKey)) {
- vibrate(record, effect, true);
+ vibrate(record, scaledEffect, true);
} else {
if (DEBUG) {
Slog.v(TAG, "No vibration for notification "
@@ -517,7 +697,7 @@
}
}).start();
} else {
- vibrate(record, effect, false);
+ vibrate(record, scaledEffect, false);
}
return true;
} finally {
@@ -535,7 +715,7 @@
}
void playInCallNotification() {
- // TODO: Should we apply politeness to mInCallNotificationVolume ?
+ // TODO b/270456865: Should we apply politeness to mInCallNotificationVolume ?
final ContentResolver cr = mContext.getContentResolver();
if (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_NORMAL
&& Settings.Secure.getIntForUser(cr,
@@ -760,6 +940,22 @@
|| signals.isCurrentProfile);
}
+ private boolean isNotificationForWorkProfile(final NotificationRecord record) {
+ return (record.getUser().getIdentifier() == mCurrentWorkProfileId
+ && mCurrentWorkProfileId != UserHandle.USER_NULL);
+ }
+
+ private int getManagedProfileId(int parentUserId) {
+ final List<UserInfo> profiles = mUm.getProfiles(parentUserId);
+ for (UserInfo profile : profiles) {
+ if (profile.isManagedProfile()
+ && profile.getUserHandle().getIdentifier() != parentUserId) {
+ return profile.getUserHandle().getIdentifier();
+ }
+ }
+ return UserHandle.USER_NULL;
+ }
+
void sendAccessibilityEvent(NotificationRecord record) {
if (!mAccessibilityManager.isEnabled()) {
return;
@@ -791,6 +987,16 @@
mAccessibilityManager.sendAccessibilityEvent(event);
}
+ /**
+ * Notify the attention helper of a user interaction with a notification
+ * @param record that was interacted with
+ */
+ public void onUserInteraction(final NotificationRecord record) {
+ if (isPoliteNotificationFeatureEnabled(record)) {
+ mStrategy.onUserInteraction(record);
+ }
+ }
+
public void dump(PrintWriter pw, String prefix, NotificationManagerService.DumpFilter filter) {
pw.println("\n Notification attention state:");
pw.print(prefix);
@@ -834,6 +1040,243 @@
}
}
+ abstract private static class PolitenessStrategy {
+ static final int POLITE_STATE_DEFAULT = 0;
+ static final int POLITE_STATE_POLITE = 1;
+ static final int POLITE_STATE_MUTED = 2;
+
+ @IntDef(prefix = { "POLITE_STATE_" }, value = {
+ POLITE_STATE_DEFAULT,
+ POLITE_STATE_POLITE,
+ POLITE_STATE_MUTED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface PolitenessState {}
+
+ protected final Map<String, Integer> mVolumeStates;
+
+ // Cooldown timer for transitioning into polite state
+ protected final int mTimeoutPolite;
+ // Cooldown timer for transitioning into muted state
+ protected final int mTimeoutMuted;
+ // Volume for polite state
+ protected final float mVolumePolite;
+ // Volume for muted state
+ protected final float mVolumeMuted;
+
+ public PolitenessStrategy(int timeoutPolite, int timeoutMuted, int volumePolite,
+ int volumeMuted) {
+ mVolumeStates = new HashMap<>();
+
+ this.mTimeoutPolite = timeoutPolite;
+ this.mTimeoutMuted = timeoutMuted;
+ this.mVolumePolite = volumePolite / 100.0f;
+ this.mVolumeMuted = volumeMuted / 100.0f;
+ }
+
+ abstract void onNotificationPosted(NotificationRecord record);
+
+ String getChannelKey(final NotificationRecord record) {
+ // use conversationId if it's a conversation
+ String channelId = record.getChannel().getConversationId() != null
+ ? record.getChannel().getConversationId() : record.getChannel().getId();
+ return record.getSbn().getNormalizedUserId() + ":" + record.getSbn().getPackageName()
+ + ":" + channelId;
+ }
+
+ public float getSoundVolume(final NotificationRecord record) {
+ float volume = DEFAULT_VOLUME;
+ final String key = getChannelKey(record);
+ final @PolitenessState int volState = getPolitenessState(record);
+
+ switch (volState) {
+ case POLITE_STATE_DEFAULT:
+ volume = DEFAULT_VOLUME;
+ break;
+ case POLITE_STATE_POLITE:
+ volume = mVolumePolite;
+ break;
+ case POLITE_STATE_MUTED:
+ volume = mVolumeMuted;
+ break;
+ default:
+ Log.w(TAG, "getSoundVolume unexpected volume state: " + volState);
+ break;
+ }
+
+ if (DEBUG) {
+ Log.i(TAG,
+ "getSoundVolume state: " + volState + " vol: " + volume + " key: " + key);
+ }
+
+ return volume;
+ }
+
+ private float getVibrationIntensity(final NotificationRecord record) {
+ // TODO b/270456865: maybe use different scaling for vibration/sound ?
+ return getSoundVolume(record);
+ }
+
+ public void onUserInteraction(final NotificationRecord record) {
+ final String key = getChannelKey(record);
+ // reset to default state after user interaction
+ mVolumeStates.put(key, POLITE_STATE_DEFAULT);
+ record.getChannel().setLastNotificationUpdateTimeMs(0);
+ }
+
+ public final @PolitenessState int getPolitenessState(final NotificationRecord record) {
+ return mVolumeStates.getOrDefault(getChannelKey(record), POLITE_STATE_DEFAULT);
+ }
+ }
+
+ // TODO b/270456865: Only one of the two strategies will be released.
+ // The other one need to be removed
+ /**
+ * Polite notification strategy 1:
+ * - Transitions from default (loud) => polite (lower volume) state if a notification
+ * alerts the same channel before timeoutPolite.
+ * - Transitions from polite => muted state if a notification alerts the same channel
+ * before timeoutMuted OR transitions back to the default state if a notification alerts
+ * after timeoutPolite.
+ * - Transitions from muted => default state if the muted channel received more than maxPosted
+ * notifications OR transitions back to the polite state if a notification alerts
+ * after timeoutMuted.
+ * - Transitions back to the default state after a user interaction with a notification.
+ */
+ public static class Strategy1 extends PolitenessStrategy {
+ // Keep track of the number of notifications posted per channel
+ private final Map<String, Integer> mNumPosted;
+ // Reset to default state if number of posted notifications exceed this value when muted
+ private final int mMaxPostedForReset;
+
+ public Strategy1(int timeoutPolite, int timeoutMuted, int volumePolite, int volumeMuted,
+ int maxPosted) {
+ super(timeoutPolite, timeoutMuted, volumePolite, volumeMuted);
+
+ mNumPosted = new HashMap<>();
+ mMaxPostedForReset = maxPosted;
+
+ if (DEBUG) {
+ Log.i(TAG, "Strategy1: " + timeoutPolite + " " + timeoutMuted);
+ }
+ }
+
+ @Override
+ public void onNotificationPosted(final NotificationRecord record) {
+ long timeSinceLastNotif = System.currentTimeMillis()
+ - record.getChannel().getLastNotificationUpdateTimeMs();
+
+ final String key = getChannelKey(record);
+ @PolitenessState int volState = getPolitenessState(record);
+
+ int numPosted = mNumPosted.getOrDefault(key, 0) + 1;
+ mNumPosted.put(key, numPosted);
+
+ switch (volState) {
+ case POLITE_STATE_DEFAULT:
+ if (timeSinceLastNotif < mTimeoutPolite) {
+ volState = POLITE_STATE_POLITE;
+ }
+ break;
+ case POLITE_STATE_POLITE:
+ if (timeSinceLastNotif < mTimeoutMuted) {
+ volState = POLITE_STATE_MUTED;
+ } else if (timeSinceLastNotif > mTimeoutPolite) {
+ volState = POLITE_STATE_DEFAULT;
+ } else {
+ volState = POLITE_STATE_POLITE;
+ }
+ break;
+ case POLITE_STATE_MUTED:
+ if (timeSinceLastNotif > mTimeoutMuted) {
+ volState = POLITE_STATE_POLITE;
+ } else {
+ volState = POLITE_STATE_MUTED;
+ }
+ if (numPosted >= mMaxPostedForReset) {
+ volState = POLITE_STATE_DEFAULT;
+ mNumPosted.put(key, 0);
+ }
+ break;
+ default:
+ Log.w(TAG, "onNotificationPosted unexpected volume state: " + volState);
+ break;
+ }
+
+ if (DEBUG) {
+ Log.i(TAG, "onNotificationPosted time delta: " + timeSinceLastNotif + " vol state: "
+ + volState + " key: " + key + " numposted " + numPosted);
+ }
+
+ mVolumeStates.put(key, volState);
+ }
+
+ @Override
+ public void onUserInteraction(final NotificationRecord record) {
+ super.onUserInteraction(record);
+ mNumPosted.put(getChannelKey(record), 0);
+ }
+ }
+
+ /**
+ * Polite notification strategy 2:
+ * - Transitions from default (loud) => muted state if a notification
+ * alerts the same channel before timeoutPolite.
+ * - Transitions from polite => default state if a notification
+ * alerts the same channel before timeoutMuted.
+ * - Transitions from muted => default state if a notification alerts after timeoutMuted,
+ * otherwise transitions to the polite state.
+ * - Transitions back to the default state after a user interaction with a notification.
+ */
+ public static class Strategy2 extends PolitenessStrategy {
+ public Strategy2(int timeoutPolite, int timeoutMuted, int volumePolite, int volumeMuted) {
+ super(timeoutPolite, timeoutMuted, volumePolite, volumeMuted);
+
+ if (DEBUG) {
+ Log.i(TAG, "Strategy2: " + timeoutPolite + " " + timeoutMuted);
+ }
+ }
+
+ @Override
+ public void onNotificationPosted(final NotificationRecord record) {
+ long timeSinceLastNotif = System.currentTimeMillis()
+ - record.getChannel().getLastNotificationUpdateTimeMs();
+
+ final String key = getChannelKey(record);
+ @PolitenessState int volState = getPolitenessState(record);
+
+ switch (volState) {
+ case POLITE_STATE_DEFAULT:
+ if (timeSinceLastNotif < mTimeoutPolite) {
+ volState = POLITE_STATE_MUTED;
+ }
+ break;
+ case POLITE_STATE_POLITE:
+ if (timeSinceLastNotif > mTimeoutMuted) {
+ volState = POLITE_STATE_DEFAULT;
+ }
+ break;
+ case POLITE_STATE_MUTED:
+ if (timeSinceLastNotif > mTimeoutMuted) {
+ volState = POLITE_STATE_DEFAULT;
+ } else {
+ volState = POLITE_STATE_POLITE;
+ }
+ break;
+ default:
+ Log.w(TAG, "onNotificationPosted unexpected volume state: " + volState);
+ break;
+ }
+
+ if (DEBUG) {
+ Log.i(TAG, "onNotificationPosted time delta: " + timeSinceLastNotif + " vol state: "
+ + volState + " key: " + key);
+ }
+
+ mVolumeStates.put(key, volState);
+ }
+ }
+
//====================== Observers =============================
private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
@@ -859,6 +1302,11 @@
if (mNotificationLight != null) {
mNotificationLight.turnOff();
}
+ } else if (action.equals(Intent.ACTION_USER_ADDED)
+ || action.equals(Intent.ACTION_USER_REMOVED)
+ || action.equals(Intent.ACTION_USER_SWITCHED)
+ || action.equals(Intent.ACTION_USER_UNLOCKED)) {
+ loadUserSettings();
}
}
};
@@ -867,6 +1315,12 @@
private static final Uri NOTIFICATION_LIGHT_PULSE_URI = Settings.System.getUriFor(
Settings.System.NOTIFICATION_LIGHT_PULSE);
+ private static final Uri NOTIFICATION_COOLDOWN_ENABLED_URI = Settings.System.getUriFor(
+ Settings.System.NOTIFICATION_COOLDOWN_ENABLED);
+ private static final Uri NOTIFICATION_COOLDOWN_ALL_URI = Settings.System.getUriFor(
+ Settings.System.NOTIFICATION_COOLDOWN_ALL);
+ private static final Uri NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED_URI =
+ Settings.System.getUriFor(Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED);
public SettingsObserver() {
super(null);
}
@@ -884,11 +1338,45 @@
updateLightsLocked();
}
}
+ if (mEnablePoliteNotificationsFeature) {
+ if (NOTIFICATION_COOLDOWN_ENABLED_URI.equals(uri)) {
+ mNotificationCooldownEnabled = Settings.System.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.System.NOTIFICATION_COOLDOWN_ENABLED,
+ DEFAULT_NOTIFICATION_COOLDOWN_ENABLED,
+ UserHandle.USER_CURRENT) != 0;
+
+ if (mCurrentWorkProfileId != UserHandle.USER_NULL) {
+ mNotificationCooldownForWorkEnabled = Settings.System.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.System.NOTIFICATION_COOLDOWN_ENABLED,
+ DEFAULT_NOTIFICATION_COOLDOWN_ENABLED_FOR_WORK,
+ mCurrentWorkProfileId)
+ != 0;
+ } else {
+ mNotificationCooldownForWorkEnabled = false;
+ }
+ }
+ if (NOTIFICATION_COOLDOWN_ALL_URI.equals(uri)) {
+ mNotificationCooldownApplyToAll = Settings.System.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.System.NOTIFICATION_COOLDOWN_ALL,
+ DEFAULT_NOTIFICATION_COOLDOWN_ALL, UserHandle.USER_CURRENT)
+ != 0;
+ }
+ if (NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED_URI.equals(uri)) {
+ mNotificationCooldownVibrateUnlocked = Settings.System.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED,
+ DEFAULT_NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED,
+ UserHandle.USER_CURRENT) != 0;
+ }
+ }
}
}
- //TODO: cleanup most (all?) of these
+ // TODO b/270456865: cleanup most (all?) of these
//======================= FOR TESTS =====================
@VisibleForTesting
void setIsAutomotive(boolean isAutomotive) {
@@ -931,6 +1419,11 @@
}
@VisibleForTesting
+ void setUserPresent(boolean userPresent) {
+ mUserPresent = userPresent;
+ }
+
+ @VisibleForTesting
void setLights(LogicalLight light) {
mNotificationLight = light;
mAttentionLight = light;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 87c3067..837b761 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2532,7 +2532,7 @@
if (mFlagResolver.isEnabled(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR)) {
mAttentionHelper = new NotificationAttentionHelper(getContext(), lightsManager,
- mAccessibilityManager, mPackageManagerClient, usageStats,
+ mAccessibilityManager, mPackageManagerClient, userManager, usageStats,
mNotificationManagerPrivate, mZenModeHelper, flagResolver);
}
@@ -3369,6 +3369,10 @@
mAppUsageStats.reportEvent(r.getSbn().getPackageName(),
getRealUserId(r.getSbn().getUserId()),
UsageEvents.Event.USER_INTERACTION);
+
+ if (Flags.politeNotifications()) {
+ mAttentionHelper.onUserInteraction(r);
+ }
}
private int getRealUserId(int userId) {
@@ -5730,13 +5734,18 @@
public void setNotificationListenerAccessGrantedForUser(ComponentName listener, int userId,
boolean granted, boolean userSet) {
Objects.requireNonNull(listener);
+ if (UserHandle.getCallingUserId() != userId) {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS,
+ "setNotificationListenerAccessGrantedForUser for user " + userId);
+ }
checkNotificationListenerAccess();
if (granted && listener.flattenToString().length()
> NotificationManager.MAX_SERVICE_COMPONENT_NAME_LENGTH) {
throw new IllegalArgumentException(
"Component name too long: " + listener.flattenToString());
}
- if (!userSet && isNotificationListenerAccessUserSet(listener)) {
+ if (!userSet && isNotificationListenerAccessUserSet(listener, userId)) {
// Don't override user's choice
return;
}
@@ -5762,9 +5771,8 @@
}
}
- private boolean isNotificationListenerAccessUserSet(ComponentName listener) {
- return mListeners.isPackageOrComponentUserSet(listener.flattenToString(),
- getCallingUserHandle().getIdentifier());
+ private boolean isNotificationListenerAccessUserSet(ComponentName listener, int userId) {
+ return mListeners.isPackageOrComponentUserSet(listener.flattenToString(), userId);
}
@Override
@@ -8612,7 +8620,7 @@
.setCategory(MetricsEvent.NOTIFICATION_ALERT)
.setType(MetricsEvent.TYPE_OPEN)
.setSubtype(buzzBeepBlink));
- EventLogTags.writeNotificationAlert(key, buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
+ EventLogTags.writeNotificationAlert(key, buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0, 0);
}
record.setAudiblyAlerted(buzz || beep);
return buzzBeepBlink;
@@ -8757,7 +8765,7 @@
if (DBG) Slog.v(TAG, "Playing sound " + soundUri
+ " with attributes " + record.getAudioAttributes());
player.playAsync(soundUri, record.getSbn().getUser(), looping,
- record.getAudioAttributes());
+ record.getAudioAttributes(), 1.0f);
return true;
}
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/notification/VibratorHelper.java b/services/core/java/com/android/server/notification/VibratorHelper.java
index e5d07bc..7204d05 100644
--- a/services/core/java/com/android/server/notification/VibratorHelper.java
+++ b/services/core/java/com/android/server/notification/VibratorHelper.java
@@ -50,6 +50,7 @@
private final long[] mFallbackPattern;
@Nullable private final float[] mDefaultPwlePattern;
@Nullable private final float[] mFallbackPwlePattern;
+ private final int mDefaultVibrationAmplitude;
public VibratorHelper(Context context) {
mVibrator = context.getSystemService(Vibrator.class);
@@ -65,6 +66,8 @@
com.android.internal.R.array.config_defaultNotificationVibeWaveform);
mFallbackPwlePattern = getFloatArray(context.getResources(),
com.android.internal.R.array.config_notificationFallbackVibeWaveform);
+ mDefaultVibrationAmplitude = context.getResources().getInteger(
+ com.android.internal.R.integer.config_defaultVibrationAmplitude);
}
/**
@@ -136,6 +139,14 @@
}
/**
+ * Scale vibration effect, valid range is [0.0f, 1.0f]
+ * Resolves default amplitude value if not already set.
+ */
+ public VibrationEffect scale(VibrationEffect effect, float scale) {
+ return effect.resolve(mDefaultVibrationAmplitude).scale(scale);
+ }
+
+ /**
* Vibrate the device with given {@code effect}.
*
* <p>We need to vibrate as "android" so we can breakthrough DND.
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index c6388e7..edb45aa 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -16,6 +16,7 @@
package com.android.server.pm;
+import static android.content.pm.Flags.preventSdkLibApp;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
@@ -994,10 +995,11 @@
return;
}
final boolean isApex = (request.getScanFlags() & SCAN_AS_APEX) != 0;
- if (!isApex) {
- createdAppId.put(packageName, optimisticallyRegisterAppId(request));
- } else {
+ final boolean isSdkLibrary = packageToScan.isSdkLibrary();
+ if (isApex || (isSdkLibrary && preventSdkLibApp())) {
request.getScannedPackageSetting().setAppId(Process.INVALID_UID);
+ } else {
+ createdAppId.put(packageName, optimisticallyRegisterAppId(request));
}
versionInfos.put(packageName,
mPm.getSettingsVersionForPackage(packageToScan));
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index 2d19282..7d822b5 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -22,6 +22,8 @@
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
import static android.os.Process.INVALID_UID;
+import static com.android.server.art.model.DexoptResult.DexContainerFileDexoptResult;
+import static com.android.server.art.model.DexoptResult.PackageDexoptResult;
import static com.android.server.pm.PackageManagerService.EMPTY_INT_ARRAY;
import static com.android.server.pm.PackageManagerService.SCAN_AS_INSTANT_APP;
import static com.android.server.pm.PackageManagerService.TAG;
@@ -32,6 +34,7 @@
import android.app.AppOpsManager;
import android.content.pm.ArchivedPackageParcel;
import android.content.pm.DataLoaderType;
+import android.content.pm.Flags;
import android.content.pm.IPackageInstallObserver2;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
@@ -56,6 +59,7 @@
import java.io.File;
import java.util.ArrayList;
+import java.util.LinkedHashSet;
import java.util.List;
final class InstallRequest {
@@ -147,6 +151,9 @@
@NonNull
private int[] mUpdateBroadcastInstantUserIds = EMPTY_INT_ARRAY;
+ @NonNull
+ private ArrayList<String> mWarnings = new ArrayList<>();
+
// New install
InstallRequest(InstallingSession params) {
mUserId = params.getUser().getIdentifier();
@@ -658,6 +665,11 @@
return mUpdateBroadcastInstantUserIds;
}
+ @NonNull
+ public ArrayList<String> getWarnings() {
+ return mWarnings;
+ }
+
public void setScanFlags(int scanFlags) {
mScanFlags = scanFlags;
}
@@ -855,6 +867,10 @@
}
}
+ public void addWarning(@NonNull String warning) {
+ mWarnings.add(warning);
+ }
+
public void onPrepareStarted() {
if (mPackageMetrics != null) {
mPackageMetrics.onStepStarted(PackageMetrics.STEP_PREPARE);
@@ -904,22 +920,37 @@
}
public void onDexoptFinished(DexoptResult dexoptResult) {
- if (mPackageMetrics == null) {
- return;
- }
- mDexoptStatus = dexoptResult.getFinalStatus();
- if (mDexoptStatus != DexoptResult.DEXOPT_PERFORMED) {
- return;
- }
- long durationMillis = 0;
- for (DexoptResult.PackageDexoptResult packageResult :
- dexoptResult.getPackageDexoptResults()) {
- for (DexoptResult.DexContainerFileDexoptResult fileResult :
- packageResult.getDexContainerFileDexoptResults()) {
- durationMillis += fileResult.getDex2oatWallTimeMillis();
+ // Only report external profile warnings when installing from adb. The goal is to warn app
+ // developers if they have provided bad external profiles, so it's not beneficial to report
+ // those warnings in the normal app install workflow.
+ if (isInstallFromAdb() && Flags.useArtServiceV2()) {
+ var externalProfileErrors = new LinkedHashSet<String>();
+ for (PackageDexoptResult packageResult : dexoptResult.getPackageDexoptResults()) {
+ for (DexContainerFileDexoptResult fileResult :
+ packageResult.getDexContainerFileDexoptResults()) {
+ externalProfileErrors.addAll(fileResult.getExternalProfileErrors());
+ }
+ }
+ if (!externalProfileErrors.isEmpty()) {
+ addWarning("Error occurred during dexopt when processing external profiles:\n "
+ + String.join("\n ", externalProfileErrors));
}
}
- mPackageMetrics.onStepFinished(PackageMetrics.STEP_DEXOPT, durationMillis);
+
+ // Report dexopt metrics.
+ if (mPackageMetrics != null) {
+ mDexoptStatus = dexoptResult.getFinalStatus();
+ if (mDexoptStatus == DexoptResult.DEXOPT_PERFORMED) {
+ long durationMillis = 0;
+ for (PackageDexoptResult packageResult : dexoptResult.getPackageDexoptResults()) {
+ for (DexContainerFileDexoptResult fileResult :
+ packageResult.getDexContainerFileDexoptResults()) {
+ durationMillis += fileResult.getDex2oatWallTimeMillis();
+ }
+ }
+ mPackageMetrics.onStepFinished(PackageMetrics.STEP_DEXOPT, durationMillis);
+ }
+ }
}
public void onInstallCompleted() {
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 11660a59..a161e8c 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -107,19 +107,22 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.SizedInputStream;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.wm.ActivityTaskManagerInternal;
+import java.io.DataInputStream;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.nio.file.StandardCopyOption;
+import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.PosixFilePermission;
import java.util.ArrayList;
import java.util.Arrays;
@@ -130,6 +133,8 @@
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import java.util.function.BiConsumer;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@@ -216,6 +221,7 @@
private final ShortcutChangeHandler mShortcutChangeHandler;
private final Handler mCallbackHandler;
+ private final ExecutorService mOnDumpExecutor = Executors.newSingleThreadExecutor();
private PackageInstallerService mPackageInstallerService;
@@ -1512,7 +1518,7 @@
forEachViewCaptureWindow((fileName, is) -> {
try {
zipOs.putNextEntry(new ZipEntry("FS" + fileName));
- is.transferTo(zipOs);
+ transferViewCaptureData(is, zipOs);
zipOs.closeEntry();
} catch (IOException e) {
getErrPrintWriter().write("Failed to output " + fileName
@@ -1553,8 +1559,9 @@
private void dumpViewCaptureDataToWmTrace(@NonNull String fileName,
@NonNull InputStream is) {
Path outPath = Paths.get(fileName);
- try {
- Files.copy(is, outPath, StandardCopyOption.REPLACE_EXISTING);
+ try (OutputStream os = Files.newOutputStream(outPath, StandardOpenOption.CREATE,
+ StandardOpenOption.TRUNCATE_EXISTING)) {
+ transferViewCaptureData(is, os);
Files.setPosixFilePermissions(outPath, WM_TRACE_FILE_PERMISSIONS);
} catch (IOException e) {
Log.d(TAG, "failed to write data to " + fileName + " in wmtrace dir", e);
@@ -1562,6 +1569,15 @@
}
/**
+ * Raw input stream reads hang on the final read when transferring data in via the pipe.
+ * The fix used below is to count and read the exact amount of bytes being sent.
+ */
+ private void transferViewCaptureData(InputStream is, OutputStream os) throws IOException {
+ DataInputStream dataInputStream = new DataInputStream(is);
+ new SizedInputStream(dataInputStream, dataInputStream.readInt()).transferTo(os);
+ }
+
+ /**
* IDumpCallback.onDump alerts the in-process ViewCapture instance to start sending data
* to LauncherAppsService via the pipe's input provided. This data (as well as an output
* file name) is provided to the consumer via an InputStream to output where it wants (for
@@ -1569,24 +1585,37 @@
*/
private void forEachViewCaptureWindow(
@NonNull BiConsumer<String, InputStream> outputtingConsumer) {
- for (int i = mDumpCallbacks.beginBroadcast() - 1; i >= 0; i--) {
- String packageName = (String) mDumpCallbacks.getBroadcastCookie(i);
- String fileName = WM_TRACE_DIR + packageName + "_" + i + VC_FILE_SUFFIX;
+ try {
+ // This multi-threading prevents ctrl-C command line command aborting from putting
+ // the mDumpCallbacks RemoteCallbackList in a bad Broadcast state. We need to wait
+ // for it to complete even though it is on a background thread.
+ mOnDumpExecutor.submit(() -> {
+ try {
+ for (int i = mDumpCallbacks.beginBroadcast() - 1; i >= 0; i--) {
+ String packageName = (String) mDumpCallbacks.getBroadcastCookie(i);
+ String fileName = WM_TRACE_DIR + packageName + "_" + i + VC_FILE_SUFFIX;
- try {
- // Order is important here. OnDump needs to be called before the BiConsumer
- // accepts & starts blocking on reading the input stream.
- ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
- mDumpCallbacks.getBroadcastItem(i).onDump(pipe[1]);
+ try {
+ // Order is important here. OnDump needs to be called before the
+ // BiConsumer accepts & starts blocking on reading the input stream.
+ ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
+ mDumpCallbacks.getBroadcastItem(i).onDump(pipe[1]);
- InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pipe[0]);
- outputtingConsumer.accept(fileName, is);
- is.close();
- } catch (Exception e) {
- Log.d(TAG, "failed to pipe view capture data", e);
- }
+ InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(
+ pipe[0]);
+ outputtingConsumer.accept(fileName, is);
+ is.close();
+ } catch (Exception e) {
+ Log.d(TAG, "failed to pipe view capture data", e);
+ }
+ }
+ } finally {
+ mDumpCallbacks.finishBroadcast();
+ }
+ }).get();
+ } catch (InterruptedException | ExecutionException e) {
+ Log.e(TAG, "background work was interrupted", e);
}
- mDumpCallbacks.finishBroadcast();
}
@RequiresPermission(READ_FRAME_BUFFER)
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 6627039..d0e5f96 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2930,15 +2930,40 @@
* @return a future that will be completed when the whole process is completed.
*/
private CompletableFuture<Void> install() {
+ // `futures` either contains only one session (`this`) or contains one parent session
+ // (`this`) and n-1 child sessions.
List<CompletableFuture<InstallResult>> futures = installNonStaged();
CompletableFuture<InstallResult>[] arr = new CompletableFuture[futures.size()];
return CompletableFuture.allOf(futures.toArray(arr)).whenComplete((r, t) -> {
if (t == null) {
setSessionApplied();
+ var multiPackageWarnings = new ArrayList<String>();
+ if (isMultiPackage()) {
+ // This is a parent session. Collect warnings from children.
+ for (CompletableFuture<InstallResult> f : futures) {
+ InstallResult result = f.join();
+ if (result.session != this && result.extras != null) {
+ ArrayList<String> childWarnings = result.extras.getStringArrayList(
+ PackageInstaller.EXTRA_WARNINGS);
+ if (!ArrayUtils.isEmpty(childWarnings)) {
+ multiPackageWarnings.addAll(childWarnings);
+ }
+ }
+ }
+ }
for (CompletableFuture<InstallResult> f : futures) {
InstallResult result = f.join();
+ Bundle extras = result.extras;
+ if (isMultiPackage() && result.session == this
+ && !multiPackageWarnings.isEmpty()) {
+ if (extras == null) {
+ extras = new Bundle();
+ }
+ extras.putStringArrayList(
+ PackageInstaller.EXTRA_WARNINGS, multiPackageWarnings);
+ }
result.session.dispatchSessionFinished(
- INSTALL_SUCCEEDED, "Session installed", result.extras);
+ INSTALL_SUCCEEDED, "Session installed", extras);
}
} else {
PackageManagerException e = (PackageManagerException) t.getCause();
@@ -5189,6 +5214,10 @@
if (!TextUtils.isEmpty(existing)) {
fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, existing);
}
+ ArrayList<String> warnings = extras.getStringArrayList(PackageInstaller.EXTRA_WARNINGS);
+ if (!ArrayUtils.isEmpty(warnings)) {
+ fillIn.putStringArrayListExtra(PackageInstaller.EXTRA_WARNINGS, warnings);
+ }
}
try {
final BroadcastOptions options = BroadcastOptions.makeBasic();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 6260dd5..68aa93d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1434,6 +1434,9 @@
break;
}
}
+ if (!request.getWarnings().isEmpty()) {
+ extras.putStringArrayList(PackageInstaller.EXTRA_WARNINGS, request.getWarnings());
+ }
return extras;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 3a9272d..7264e2e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -4397,10 +4397,21 @@
session.commit(receiver.getIntentSender());
if (!session.isStaged()) {
final Intent result = receiver.getResult();
- final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE);
+ int status = result.getIntExtra(
+ PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
+ List<String> warnings =
+ result.getStringArrayListExtra(PackageInstaller.EXTRA_WARNINGS);
if (status == PackageInstaller.STATUS_SUCCESS) {
- if (logSuccess) {
+ if (!ArrayUtils.isEmpty(warnings)) {
+ // Don't start the output string with "Success" because that will make adb
+ // treat this as a success.
+ for (String warning : warnings) {
+ pw.println("Warning: " + warning);
+ }
+ // Treat warnings as failure to draw app developers' attention.
+ status = PackageInstaller.STATUS_FAILURE;
+ pw.println("Completed with warning(s)");
+ } else if (logSuccess) {
pw.println("Success");
}
} else {
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 85b60a0..29e0c35 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -295,6 +295,8 @@
.setCredentialShareableWithParent(false)
.setMediaSharedWithParent(false)
.setShowInLauncher(UserProperties.SHOW_IN_LAUNCHER_SEPARATE)
+ .setShowInSettings(UserProperties.SHOW_IN_SETTINGS_SEPARATE)
+ .setHideInSettingsInQuietMode(true)
.setCrossProfileIntentFilterAccessControl(
UserProperties.CROSS_PROFILE_INTENT_FILTER_ACCESS_LEVEL_SYSTEM)
.setInheritDevicePolicy(UserProperties.INHERIT_DEVICE_POLICY_FROM_PARENT));
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
index f14941b..46121dc 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
@@ -18,6 +18,7 @@
import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.Flags.preventSdkLibApp;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
@@ -403,8 +404,9 @@
try {
final File baseApk = new File(lite.getBaseApkPath());
+ boolean shouldSkipComponents = lite.isIsSdkLibrary() && preventSdkLibApp();
final ParseResult<ParsingPackage> result = parseBaseApk(input, baseApk,
- lite.getPath(), assetLoader, flags);
+ lite.getPath(), assetLoader, flags, shouldSkipComponents);
if (result.isError()) {
return input.error(result);
}
@@ -456,10 +458,11 @@
final PackageLite lite = liteResult.getResult();
final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
try {
+ boolean shouldSkipComponents = lite.isIsSdkLibrary() && preventSdkLibApp();
final ParseResult<ParsingPackage> result = parseBaseApk(input,
apkFile,
apkFile.getCanonicalPath(),
- assetLoader, flags);
+ assetLoader, flags, shouldSkipComponents);
if (result.isError()) {
return input.error(result);
}
@@ -594,7 +597,8 @@
}
private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,
- String codePath, SplitAssetLoader assetLoader, int flags) {
+ String codePath, SplitAssetLoader assetLoader, int flags,
+ boolean shouldSkipComponents) {
final String apkPath = apkFile.getAbsolutePath();
final String volumeUuid = getVolumeUuid(apkPath);
@@ -619,7 +623,7 @@
final Resources res = new Resources(assets, mDisplayMetrics, null);
ParseResult<ParsingPackage> result = parseBaseApk(input, apkPath, codePath, res,
- parser, flags);
+ parser, flags, shouldSkipComponents);
if (result.isError()) {
return input.error(result.getErrorCode(),
apkPath + " (at " + parser.getPositionDescription() + "): "
@@ -719,11 +723,12 @@
* @param res The resources from which to resolve values
* @param parser The manifest parser
* @param flags Flags how to parse
+ * @param shouldSkipComponents If the package is a sdk-library
* @return Parsed package or null on error.
*/
private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, String apkPath,
- String codePath, Resources res, XmlResourceParser parser, int flags)
- throws XmlPullParserException, IOException {
+ String codePath, Resources res, XmlResourceParser parser, int flags,
+ boolean shouldSkipComponents) throws XmlPullParserException, IOException {
final String splitName;
final String pkgName;
@@ -751,7 +756,8 @@
final ParsingPackage pkg = mCallback.startParsingPackage(
pkgName, apkPath, codePath, manifestArray, isCoreApp);
final ParseResult<ParsingPackage> result =
- parseBaseApkTags(input, pkg, manifestArray, res, parser, flags);
+ parseBaseApkTags(input, pkg, manifestArray, res, parser, flags,
+ shouldSkipComponents);
if (result.isError()) {
return result;
}
@@ -987,10 +993,9 @@
return ParsingUtils.unknownTag("<application>", pkg, parser, input);
}
}
-
private ParseResult<ParsingPackage> parseBaseApkTags(ParseInput input, ParsingPackage pkg,
- TypedArray sa, Resources res, XmlResourceParser parser, int flags)
- throws XmlPullParserException, IOException {
+ TypedArray sa, Resources res, XmlResourceParser parser, int flags,
+ boolean shouldSkipComponents) throws XmlPullParserException, IOException {
ParseResult<ParsingPackage> sharedUserResult = parseSharedUser(input, pkg, sa);
if (sharedUserResult.isError()) {
return sharedUserResult;
@@ -1027,7 +1032,8 @@
}
} else {
foundApp = true;
- result = parseBaseApplication(input, pkg, res, parser, flags);
+ result = parseBaseApplication(input, pkg, res, parser, flags,
+ shouldSkipComponents);
}
} else {
result = parseBaseApkTag(tagName, input, pkg, res, parser, flags);
@@ -1972,8 +1978,8 @@
* code moves around.
*/
private ParseResult<ParsingPackage> parseBaseApplication(ParseInput input,
- ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)
- throws XmlPullParserException, IOException {
+ ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags,
+ boolean shouldSkipComponents) throws XmlPullParserException, IOException {
final String pkgName = pkg.getPackageName();
int targetSdk = pkg.getTargetSdkVersion();
@@ -2213,6 +2219,9 @@
isActivity = true;
// fall-through
case "receiver":
+ if (shouldSkipComponents) {
+ continue;
+ }
ParseResult<ParsedActivity> activityResult =
ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
res, parser, flags, sUseRoundIcon, null /*defaultSplitName*/,
@@ -2232,6 +2241,9 @@
result = activityResult;
break;
case "service":
+ if (shouldSkipComponents) {
+ continue;
+ }
ParseResult<ParsedService> serviceResult =
ParsedServiceUtils.parseService(mSeparateProcesses, pkg, res, parser,
flags, sUseRoundIcon, null /*defaultSplitName*/,
@@ -2245,6 +2257,9 @@
result = serviceResult;
break;
case "provider":
+ if (shouldSkipComponents) {
+ continue;
+ }
ParseResult<ParsedProvider> providerResult =
ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser,
flags, sUseRoundIcon, null /*defaultSplitName*/,
@@ -2256,6 +2271,9 @@
result = providerResult;
break;
case "activity-alias":
+ if (shouldSkipComponents) {
+ continue;
+ }
activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res,
parser, sUseRoundIcon, null /*defaultSplitName*/,
input);
@@ -2414,7 +2432,7 @@
/**
* For parsing non-MainComponents. Main ones have an order and some special handling which is
* done directly in {@link #parseBaseApplication(ParseInput, ParsingPackage, Resources,
- * XmlResourceParser, int)}.
+ * XmlResourceParser, int, boolean)}.
*/
private ParseResult parseBaseAppChildTag(ParseInput input, String tag, ParsingPackage pkg,
Resources res, XmlResourceParser parser, int flags)
diff --git a/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java b/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java
index c908acd..d5bc912 100644
--- a/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java
+++ b/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java
@@ -24,9 +24,10 @@
import android.os.Binder;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.security.keymaster.IKeyAttestationApplicationIdProvider;
-import android.security.keymaster.KeyAttestationApplicationId;
-import android.security.keymaster.KeyAttestationPackageInfo;
+import android.security.keystore.IKeyAttestationApplicationIdProvider;
+import android.security.keystore.KeyAttestationApplicationId;
+import android.security.keystore.KeyAttestationPackageInfo;
+import android.security.keystore.Signature;
/**
* @hide
@@ -64,14 +65,25 @@
for (int i = 0; i < packageNames.length; ++i) {
PackageInfo packageInfo = mPackageManager.getPackageInfoAsUser(packageNames[i],
PackageManager.GET_SIGNATURES, userId);
- keyAttestationPackageInfos[i] = new KeyAttestationPackageInfo(packageNames[i],
- packageInfo.getLongVersionCode(), packageInfo.signatures);
+ KeyAttestationPackageInfo pInfo = new KeyAttestationPackageInfo();
+ pInfo.packageName = new String(packageNames[i]);
+ pInfo.versionCode = packageInfo.getLongVersionCode();
+ pInfo.signatures = new Signature[packageInfo.signatures.length];
+ for (int index = 0; index < packageInfo.signatures.length; index++) {
+ Signature sign = new Signature();
+ sign.data = packageInfo.signatures[index].toByteArray();
+ pInfo.signatures[index] = sign;
+ }
+
+ keyAttestationPackageInfos[i] = pInfo;
}
} catch (NameNotFoundException nnfe) {
throw new RemoteException(nnfe.getMessage());
} finally {
Binder.restoreCallingIdentity(token);
}
- return new KeyAttestationApplicationId(keyAttestationPackageInfos);
+ KeyAttestationApplicationId attestAppId = new KeyAttestationApplicationId();
+ attestAppId.packageInfos = keyAttestationPackageInfos;
+ return attestAppId;
}
}
diff --git a/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java b/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
index bff6d50..6d580e9 100644
--- a/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
@@ -39,8 +39,10 @@
import android.speech.RecognitionService;
import android.speech.SpeechRecognizer;
import android.util.Slog;
+import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.modules.expresslog.Counter;
import com.android.server.infra.AbstractPerUserSystemService;
import java.util.HashMap;
@@ -64,6 +66,9 @@
private final Map<Integer, Set<RemoteSpeechRecognitionService>> mRemoteServicesByUid =
new HashMap<>();
+ @GuardedBy("mLock")
+ private final SparseIntArray mSessionCountByUid = new SparseIntArray();
+
SpeechRecognitionManagerServiceImpl(
@NonNull SpeechRecognitionManagerService master,
@NonNull Object lock, @UserIdInt int userId) {
@@ -216,6 +221,7 @@
service.shutdown(clientToken);
}
synchronized (mLock) {
+ decrementSessionCountForUidLocked(callingUid);
if (!service.hasActiveSessions()) {
removeService(callingUid, service);
}
@@ -239,6 +245,26 @@
return ComponentName.unflattenFromString(serviceName);
}
+ @GuardedBy("mLock")
+ private int getSessionCountByUidLocked(int uid) {
+ return mSessionCountByUid.get(uid, 0);
+ }
+
+ @GuardedBy("mLock")
+ private void incrementSessionCountForUidLocked(int uid) {
+ mSessionCountByUid.put(uid, mSessionCountByUid.get(uid, 0) + 1);
+ }
+
+ @GuardedBy("mLock")
+ private void decrementSessionCountForUidLocked(int uid) {
+ int newCount = mSessionCountByUid.get(uid, 1) - 1;
+ if (newCount > 0) {
+ mSessionCountByUid.put(uid, newCount);
+ } else {
+ mSessionCountByUid.delete(uid);
+ }
+ }
+
private RemoteSpeechRecognitionService createService(
int callingUid, ComponentName serviceComponent) {
synchronized (mLock) {
@@ -247,6 +273,18 @@
if (servicesForClient != null
&& servicesForClient.size() >= MAX_CONCURRENT_CONNECTIONS_BY_CLIENT) {
+ Slog.w(TAG, "Number of remote services exceeded for uid: " + callingUid);
+ Counter.logIncrementWithUid(
+ "speech_recognition.value_exceed_service_connections_count",
+ callingUid);
+ return null;
+ }
+
+ if (getSessionCountByUidLocked(callingUid) >= MAX_CONCURRENT_CONNECTIONS_BY_CLIENT) {
+ Slog.w(TAG, "Number of sessions exceeded for uid: " + callingUid);
+ Counter.logIncrementWithUid(
+ "speech_recognition.value_exceed_session_count",
+ callingUid);
return null;
}
@@ -262,6 +300,7 @@
Slog.i(TAG, "Reused existing connection to " + serviceComponent);
}
+ incrementSessionCountForUidLocked(callingUid);
return existingService.get();
}
}
@@ -282,6 +321,7 @@
Slog.i(TAG, "Creating a new connection to " + serviceComponent);
}
+ incrementSessionCountForUidLocked(callingUid);
return service;
}
}
diff --git a/services/core/java/com/android/server/vibrator/OWNERS b/services/core/java/com/android/server/vibrator/OWNERS
index 9afa682..da5a476 100644
--- a/services/core/java/com/android/server/vibrator/OWNERS
+++ b/services/core/java/com/android/server/vibrator/OWNERS
@@ -1,6 +1,5 @@
# Bug component: 345036
-
+khalilahmad@google.com
lsandrade@google.com
michaelwr@google.com
-sbowden@google.com
-khalilahmad@google.com
\ No newline at end of file
+roosa@google.com
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 9ffbc8e..c866dd0 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -9262,13 +9262,6 @@
Slog.w(TAG, errorMessage);
}
- // Configuration's equality doesn't consider seq so if only seq number changes in resolved
- // override configuration. Therefore ConfigurationContainer doesn't change merged override
- // configuration, but it's used to push configuration changes so explicitly update that.
- if (getMergedOverrideConfiguration().seq != getResolvedOverrideConfiguration().seq) {
- onMergedOverrideConfigurationChanged();
- }
-
// Before PiP animation is done, th windowing mode of the activity is still the previous
// mode (see RootWindowContainer#moveActivityToPinnedRootTask). So once the windowing mode
// of activity is changed, it is the signal of the last step to update the PiP states.
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 27315bb..7cccf6b 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -707,7 +707,7 @@
}
}
- int res;
+ int res = START_CANCELED;
synchronized (mService.mGlobalLock) {
final boolean globalConfigWillChange = mRequest.globalConfig != null
&& mService.getGlobalConfiguration().diff(mRequest.globalConfig) != 0;
@@ -719,22 +719,20 @@
+ "will change = %b", globalConfigWillChange);
final long origId = Binder.clearCallingIdentity();
-
- res = resolveToHeavyWeightSwitcherIfNeeded();
- if (res != START_SUCCESS) {
- return res;
- }
-
try {
+ res = resolveToHeavyWeightSwitcherIfNeeded();
+ if (res != START_SUCCESS) {
+ return res;
+ }
+
res = executeRequest(mRequest);
} finally {
+ Binder.restoreCallingIdentity(origId);
mRequest.logMessage.append(" result code=").append(res);
Slog.i(TAG, mRequest.logMessage.toString());
mRequest.logMessage.setLength(0);
}
- Binder.restoreCallingIdentity(origId);
-
if (globalConfigWillChange) {
// If the caller also wants to switch to a new configuration, do so now.
// This allows a clean switch, as we are waiting for the current activity
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 58d4e82..7947112 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -223,9 +223,9 @@
}
/**
- * Update merged override configuration based on corresponding parent's config and notify all
- * its children. If there is no parent, merged override configuration will set equal to current
- * override config.
+ * Update merged override configuration based on corresponding parent's config. If there is no
+ * parent, merged override configuration will set equal to current override config. This
+ * doesn't cascade on its own since it's called by {@link #onConfigurationChanged}.
* @see #mMergedOverrideConfiguration
*/
void onMergedOverrideConfigurationChanged() {
@@ -240,10 +240,6 @@
} else {
mMergedOverrideConfiguration.setTo(mResolvedOverrideConfiguration);
}
- for (int i = getChildCount() - 1; i >= 0; --i) {
- final ConfigurationContainer child = getChildAt(i);
- child.onMergedOverrideConfigurationChanged();
- }
}
/**
@@ -688,8 +684,6 @@
if (newParent != null) {
// Update full configuration of this container and all its children.
onConfigurationChanged(newParent.mFullConfiguration);
- // Update merged override configuration of this container and all its children.
- onMergedOverrideConfigurationChanged();
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index f81e5d4..df26b10 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -44,6 +44,7 @@
import android.window.DisplayAreaInfo;
import android.window.IDisplayAreaOrganizer;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.server.policy.WindowManagerPolicy;
@@ -79,6 +80,12 @@
private final Configuration mTmpConfiguration = new Configuration();
/**
+ * Prevent duplicate calls to onDisplayAreaAppeared, or early call of onDisplayAreaInfoChanged.
+ */
+ @VisibleForTesting
+ boolean mDisplayAreaAppearedSent;
+
+ /**
* Whether this {@link DisplayArea} should ignore fixed-orientation request. If {@code true}, it
* can never specify orientation, but shows the fixed-orientation apps below it in the
* letterbox; otherwise, it rotates based on the fixed-orientation request.
@@ -582,18 +589,31 @@
sendDisplayAreaVanished(lastOrganizer);
if (!skipDisplayAreaAppeared) {
sendDisplayAreaAppeared();
+ } else if (organizer != null) {
+ // Set as sent since the DisplayAreaAppearedInfo will be sent back when registered.
+ mDisplayAreaAppearedSent = true;
}
}
+ @VisibleForTesting
void sendDisplayAreaAppeared() {
- if (mOrganizer == null) return;
+ if (mOrganizer == null || mDisplayAreaAppearedSent) return;
mOrganizerController.onDisplayAreaAppeared(mOrganizer, this);
+ mDisplayAreaAppearedSent = true;
}
+ @VisibleForTesting
+ void sendDisplayAreaInfoChanged() {
+ if (mOrganizer == null || !mDisplayAreaAppearedSent) return;
+ mOrganizerController.onDisplayAreaInfoChanged(mOrganizer, this);
+ }
+
+ @VisibleForTesting
void sendDisplayAreaVanished(IDisplayAreaOrganizer organizer) {
- if (organizer == null) return;
+ if (organizer == null || !mDisplayAreaAppearedSent) return;
migrateToNewSurfaceControl(getSyncTransaction());
mOrganizerController.onDisplayAreaVanished(organizer, this);
+ mDisplayAreaAppearedSent = false;
}
@Override
@@ -603,7 +623,7 @@
super.onConfigurationChanged(newParentConfig);
if (mOrganizer != null && getConfiguration().diff(mTmpConfiguration) != 0) {
- mOrganizerController.onDisplayAreaInfoChanged(mOrganizer, this);
+ sendDisplayAreaInfoChanged();
}
}
diff --git a/services/core/java/com/android/server/wm/RefreshRatePolicy.java b/services/core/java/com/android/server/wm/RefreshRatePolicy.java
index 99831d3..23c135a 100644
--- a/services/core/java/com/android/server/wm/RefreshRatePolicy.java
+++ b/services/core/java/com/android/server/wm/RefreshRatePolicy.java
@@ -19,6 +19,8 @@
import static android.hardware.display.DisplayManager.SWITCHING_TYPE_NONE;
import static android.hardware.display.DisplayManager.SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY;
+import static com.android.window.flags.Flags.explicitRefreshRateHints;
+
import android.hardware.display.DisplayManager;
import android.view.Display;
import android.view.Display.Mode;
@@ -137,7 +139,7 @@
// to run in default refresh rate. But if the display size of default mode is different
// from the using preferred mode, then still keep the preferred mode to avoid disturbing
// the animation.
- if (w.isAnimationRunningSelfOrParent()) {
+ if (!explicitRefreshRateHints() && w.isAnimationRunningSelfOrParent()) {
Display.Mode preferredMode = null;
for (Display.Mode mode : mDisplayInfo.supportedModes) {
if (preferredDisplayModeId == mode.getModeId()) {
@@ -251,7 +253,7 @@
// If app is animating, it's not able to control refresh rate because we want the animation
// to run in default refresh rate.
- if (w.isAnimationRunningSelfOrParent()) {
+ if (!explicitRefreshRateHints() && w.isAnimationRunningSelfOrParent()) {
return w.mFrameRateVote.reset();
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 471dea8..f3fb7c4 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -703,6 +703,7 @@
if (dc == null || mTargetDisplays.contains(dc)) return;
mTargetDisplays.add(dc);
addOnTopTasks(dc, mOnTopTasksStart);
+ mController.startPerfHintForDisplay(dc.mDisplayId);
}
/**
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index e686f1b..f509463 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -22,8 +22,10 @@
import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS;
import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.window.SystemPerformanceHinter.HINT_SF;
import static com.android.server.wm.ActivityTaskManagerService.POWER_MODE_REASON_CHANGE_DISPLAY;
+import static com.android.window.flags.Flags.explicitRefreshRateHints;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -39,6 +41,7 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
@@ -48,6 +51,8 @@
import android.window.ITransitionMetricsReporter;
import android.window.ITransitionPlayer;
import android.window.RemoteTransition;
+import android.window.SystemPerformanceHinter;
+import android.window.SystemPerformanceHinter.HighPerfSession;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
import android.window.WindowContainerTransaction;
@@ -125,6 +130,8 @@
SnapshotController mSnapshotController;
TransitionTracer mTransitionTracer;
+ private SystemPerformanceHinter mSystemPerformanceHinter;
+
private final ArrayList<WindowManagerInternal.AppTransitionListener> mLegacyListeners =
new ArrayList<>();
@@ -176,6 +183,24 @@
private final IBinder.DeathRecipient mTransitionPlayerDeath;
+ /**
+ * Tracks active perf sessions that boost frame rate and hint sf to increase its
+ * estimated work duration.
+ */
+ private final ArraySet<HighPerfSession> mHighPerfSessions = new ArraySet<>();
+
+
+ /**
+ * Starts a perf hint session which will boost the refresh rate for the display and change
+ * sf duration to handle larger workloads.
+ */
+ void startPerfHintForDisplay(int displayId) {
+ if (explicitRefreshRateHints()) {
+ mHighPerfSessions.add(mSystemPerformanceHinter.startSession(HINT_SF, displayId,
+ "Transition collected"));
+ }
+ }
+
static class QueuedTransition {
final Transition mTransition;
final OnStartCollect mOnStartCollect;
@@ -255,6 +280,12 @@
mIsWaitingForDisplayEnabled = !wms.mDisplayEnabled;
registerLegacyListener(wms.mActivityManagerAppTransitionNotifier);
setSyncEngine(wms.mSyncEngine);
+ setSystemPerformanceHinter(wms.mSystemPerformanceHinter);
+ }
+
+ @VisibleForTesting
+ void setSystemPerformanceHinter(SystemPerformanceHinter hinter) {
+ mSystemPerformanceHinter = hinter;
}
@VisibleForTesting
@@ -1194,18 +1225,27 @@
final boolean animatingState = !mPlayingTransitions.isEmpty()
|| (mCollectingTransition != null && mCollectingTransition.isStarted());
if (animatingState && !mAnimatingState) {
- t.setEarlyWakeupStart();
+ if (!explicitRefreshRateHints()) {
+ t.setEarlyWakeupStart();
+ }
// Usually transitions put quite a load onto the system already (with all the things
// happening in app), so pause task snapshot persisting to not increase the load.
mSnapshotController.setPause(true);
mAnimatingState = true;
Transition.asyncTraceBegin("animating", 0x41bfaf1 /* hashcode of TAG */);
} else if (!animatingState && mAnimatingState) {
- t.setEarlyWakeupEnd();
+ if (!explicitRefreshRateHints()) {
+ t.setEarlyWakeupEnd();
+ }
mAtm.mWindowManager.scheduleAnimationLocked();
mSnapshotController.setPause(false);
mAnimatingState = false;
Transition.asyncTraceEnd(0x41bfaf1 /* hashcode of TAG */);
+ // We close all perf sessions here when all transitions finish. The sessions are created
+ // when we collect transitions because we have access to the display id.
+ for (HighPerfSession perfSession : mHighPerfSessions) {
+ perfSession.close();
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 7e5dabb..674ff48 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -85,13 +85,8 @@
// to another, and this is the previous wallpaper target.
private WindowState mPrevWallpaperTarget = null;
- private float mLastWallpaperX = -1;
- private float mLastWallpaperY = -1;
- private float mLastWallpaperXStep = -1;
- private float mLastWallpaperYStep = -1;
private float mLastWallpaperZoomOut = 0;
- private int mLastWallpaperDisplayOffsetX = Integer.MIN_VALUE;
- private int mLastWallpaperDisplayOffsetY = Integer.MIN_VALUE;
+
// Whether COMMAND_FREEZE was dispatched.
private boolean mLastFrozen = false;
@@ -116,8 +111,6 @@
private static final int WALLPAPER_DRAW_TIMEOUT = 2;
private int mWallpaperDrawState = WALLPAPER_DRAW_NORMAL;
- private boolean mShouldUpdateZoom;
-
@Nullable private Point mLargestDisplaySize = null;
private final FindWallpaperTargetResult mFindResults = new FindWallpaperTargetResult();
@@ -370,6 +363,7 @@
// Full size of the wallpaper (usually larger than bounds above to parallax scroll when
// swiping through Launcher pages).
final Rect wallpaperFrame = wallpaperWin.getFrame();
+ WallpaperWindowToken token = wallpaperWin.mToken.asWallpaperToken();
final int diffWidth = wallpaperFrame.width() - lastWallpaperBounds.width();
final int diffHeight = wallpaperFrame.height() - lastWallpaperBounds.height();
@@ -394,10 +388,10 @@
// The 0 to 1 scale is because the "length" varies depending on how many home screens you
// have, so 0 is the left of the first home screen, and 1 is the right of the last one (for
// LTR, and the opposite for RTL).
- float wpx = mLastWallpaperX >= 0 ? mLastWallpaperX : defaultWallpaperX;
+ float wpx = token.mWallpaperX >= 0 ? token.mWallpaperX : defaultWallpaperX;
// "Wallpaper X step size" is how much of that 0-1 is one "page" of the home screen
// when scrolling.
- float wpxs = mLastWallpaperXStep >= 0 ? mLastWallpaperXStep : -1.0f;
+ float wpxs = token.mWallpaperXStep >= 0 ? token.mWallpaperXStep : -1.0f;
// Difference between width of wallpaper image, and the last size of the wallpaper.
// This is the horizontal surplus from the prior configuration.
int availw = diffWidth;
@@ -406,10 +400,10 @@
wallpaperWin.isRtl());
availw -= displayOffset;
int offset = availw > 0 ? -(int)(availw * wpx + .5f) : 0;
- if (mLastWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
+ if (token.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
// if device is LTR, then offset wallpaper to the left (the wallpaper is drawn
// always starting from the left of the screen).
- offset += mLastWallpaperDisplayOffsetX;
+ offset += token.mWallpaperDisplayOffsetX;
} else if (!wallpaperWin.isRtl()) {
// In RTL the offset is calculated so that the wallpaper ends up right aligned (see
// offset above).
@@ -423,11 +417,11 @@
rawChanged = true;
}
- float wpy = mLastWallpaperY >= 0 ? mLastWallpaperY : 0.5f;
- float wpys = mLastWallpaperYStep >= 0 ? mLastWallpaperYStep : -1.0f;
+ float wpy = token.mWallpaperY >= 0 ? token.mWallpaperY : 0.5f;
+ float wpys = token.mWallpaperYStep >= 0 ? token.mWallpaperYStep : -1.0f;
offset = diffHeight > 0 ? -(int) (diffHeight * wpy + .5f) : 0;
- if (mLastWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
- offset += mLastWallpaperDisplayOffsetY;
+ if (token.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
+ offset += token.mWallpaperDisplayOffsetY;
}
newYOffset = offset;
@@ -549,8 +543,10 @@
void setWallpaperZoomOut(WindowState window, float zoom) {
if (Float.compare(window.mWallpaperZoomOut, zoom) != 0) {
window.mWallpaperZoomOut = zoom;
- mShouldUpdateZoom = true;
- updateWallpaperOffsetLocked(window, false);
+ computeLastWallpaperZoomOut();
+ for (WallpaperWindowToken token : mWallpaperTokens) {
+ token.updateWallpaperOffset(false);
+ }
}
}
@@ -598,43 +594,48 @@
// zoom effect from home.
target = changingTarget;
}
- if (target != null) {
- if (target.mWallpaperX >= 0) {
- mLastWallpaperX = target.mWallpaperX;
- } else if (changingTarget.mWallpaperX >= 0) {
- mLastWallpaperX = changingTarget.mWallpaperX;
- }
- if (target.mWallpaperY >= 0) {
- mLastWallpaperY = target.mWallpaperY;
- } else if (changingTarget.mWallpaperY >= 0) {
- mLastWallpaperY = changingTarget.mWallpaperY;
- }
- computeLastWallpaperZoomOut();
- if (target.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
- mLastWallpaperDisplayOffsetX = target.mWallpaperDisplayOffsetX;
- } else if (changingTarget.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
- mLastWallpaperDisplayOffsetX = changingTarget.mWallpaperDisplayOffsetX;
- }
- if (target.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
- mLastWallpaperDisplayOffsetY = target.mWallpaperDisplayOffsetY;
- } else if (changingTarget.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
- mLastWallpaperDisplayOffsetY = changingTarget.mWallpaperDisplayOffsetY;
- }
- if (target.mWallpaperXStep >= 0) {
- mLastWallpaperXStep = target.mWallpaperXStep;
- } else if (changingTarget.mWallpaperXStep >= 0) {
- mLastWallpaperXStep = changingTarget.mWallpaperXStep;
- }
- if (target.mWallpaperYStep >= 0) {
- mLastWallpaperYStep = target.mWallpaperYStep;
- } else if (changingTarget.mWallpaperYStep >= 0) {
- mLastWallpaperYStep = changingTarget.mWallpaperYStep;
- }
- }
- for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
- mWallpaperTokens.get(curTokenNdx).updateWallpaperOffset(sync);
+ WallpaperWindowToken token = getTokenForTarget(target);
+ if (token == null) return;
+
+ if (target.mWallpaperX >= 0) {
+ token.mWallpaperX = target.mWallpaperX;
+ } else if (changingTarget.mWallpaperX >= 0) {
+ token.mWallpaperX = changingTarget.mWallpaperX;
}
+ if (target.mWallpaperY >= 0) {
+ token.mWallpaperY = target.mWallpaperY;
+ } else if (changingTarget.mWallpaperY >= 0) {
+ token.mWallpaperY = changingTarget.mWallpaperY;
+ }
+ if (target.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
+ token.mWallpaperDisplayOffsetX = target.mWallpaperDisplayOffsetX;
+ } else if (changingTarget.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
+ token.mWallpaperDisplayOffsetX = changingTarget.mWallpaperDisplayOffsetX;
+ }
+ if (target.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
+ token.mWallpaperDisplayOffsetY = target.mWallpaperDisplayOffsetY;
+ } else if (changingTarget.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
+ token.mWallpaperDisplayOffsetY = changingTarget.mWallpaperDisplayOffsetY;
+ }
+ if (target.mWallpaperXStep >= 0) {
+ token.mWallpaperXStep = target.mWallpaperXStep;
+ } else if (changingTarget.mWallpaperXStep >= 0) {
+ token.mWallpaperXStep = changingTarget.mWallpaperXStep;
+ }
+ if (target.mWallpaperYStep >= 0) {
+ token.mWallpaperYStep = target.mWallpaperYStep;
+ } else if (changingTarget.mWallpaperYStep >= 0) {
+ token.mWallpaperYStep = changingTarget.mWallpaperYStep;
+ }
+ token.updateWallpaperOffset(sync);
+ }
+
+ private WallpaperWindowToken getTokenForTarget(WindowState target) {
+ if (target == null) return null;
+ WindowState window = mFindResults.getTopWallpaper(
+ target.canShowWhenLocked() && mService.isKeyguardLocked());
+ return window == null ? null : window.mToken.asWallpaperToken();
}
void clearLastWallpaperTimeoutTime() {
@@ -805,10 +806,11 @@
// all wallpapers go behind it.
findWallpaperTarget();
updateWallpaperWindowsTarget(mFindResults);
+ WallpaperWindowToken token = getTokenForTarget(mWallpaperTarget);
// The window is visible to the compositor...but is it visible to the user?
// That is what the wallpaper cares about.
- final boolean visible = mWallpaperTarget != null;
+ final boolean visible = token != null;
if (DEBUG_WALLPAPER) {
Slog.v(TAG, "Wallpaper visibility: " + visible + " at display "
+ mDisplayContent.getDisplayId());
@@ -816,19 +818,18 @@
if (visible) {
if (mWallpaperTarget.mWallpaperX >= 0) {
- mLastWallpaperX = mWallpaperTarget.mWallpaperX;
- mLastWallpaperXStep = mWallpaperTarget.mWallpaperXStep;
+ token.mWallpaperX = mWallpaperTarget.mWallpaperX;
+ token.mWallpaperXStep = mWallpaperTarget.mWallpaperXStep;
}
- computeLastWallpaperZoomOut();
if (mWallpaperTarget.mWallpaperY >= 0) {
- mLastWallpaperY = mWallpaperTarget.mWallpaperY;
- mLastWallpaperYStep = mWallpaperTarget.mWallpaperYStep;
+ token.mWallpaperY = mWallpaperTarget.mWallpaperY;
+ token.mWallpaperYStep = mWallpaperTarget.mWallpaperYStep;
}
if (mWallpaperTarget.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
- mLastWallpaperDisplayOffsetX = mWallpaperTarget.mWallpaperDisplayOffsetX;
+ token.mWallpaperDisplayOffsetX = mWallpaperTarget.mWallpaperDisplayOffsetX;
}
if (mWallpaperTarget.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
- mLastWallpaperDisplayOffsetY = mWallpaperTarget.mWallpaperDisplayOffsetY;
+ token.mWallpaperDisplayOffsetY = mWallpaperTarget.mWallpaperDisplayOffsetY;
}
}
@@ -1020,13 +1021,11 @@
* we'll have conflicts and break the "depth system" mental model.
*/
private void computeLastWallpaperZoomOut() {
- if (mShouldUpdateZoom) {
- mLastWallpaperZoomOut = 0;
- mDisplayContent.forAllWindows(mComputeMaxZoomOutFunction, true);
- mShouldUpdateZoom = false;
- }
+ mLastWallpaperZoomOut = 0;
+ mDisplayContent.forAllWindows(mComputeMaxZoomOutFunction, true);
}
+
private float zoomOutToScale(float zoomOut) {
return MathUtils.lerp(mMinWallpaperScale, mMaxWallpaperScale, 1 - zoomOut);
}
@@ -1034,19 +1033,28 @@
void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("displayId="); pw.println(mDisplayContent.getDisplayId());
pw.print(prefix); pw.print("mWallpaperTarget="); pw.println(mWallpaperTarget);
+ pw.print(prefix); pw.print("mLastWallpaperZoomOut="); pw.println(mLastWallpaperZoomOut);
if (mPrevWallpaperTarget != null) {
pw.print(prefix); pw.print("mPrevWallpaperTarget="); pw.println(mPrevWallpaperTarget);
}
- pw.print(prefix); pw.print("mLastWallpaperX="); pw.print(mLastWallpaperX);
- pw.print(" mLastWallpaperY="); pw.println(mLastWallpaperY);
- if (mLastWallpaperDisplayOffsetX != Integer.MIN_VALUE
- || mLastWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
- pw.print(prefix);
- pw.print("mLastWallpaperDisplayOffsetX="); pw.print(mLastWallpaperDisplayOffsetX);
- pw.print(" mLastWallpaperDisplayOffsetY="); pw.println(mLastWallpaperDisplayOffsetY);
+
+ for (WallpaperWindowToken t : mWallpaperTokens) {
+ pw.print(prefix); pw.println("token " + t + ":");
+ pw.print(prefix); pw.print(" canShowWhenLocked="); pw.println(t.canShowWhenLocked());
+ dumpValue(pw, prefix, "mWallpaperX", t.mWallpaperX);
+ dumpValue(pw, prefix, "mWallpaperY", t.mWallpaperY);
+ dumpValue(pw, prefix, "mWallpaperXStep", t.mWallpaperXStep);
+ dumpValue(pw, prefix, "mWallpaperYStep", t.mWallpaperYStep);
+ dumpValue(pw, prefix, "mWallpaperDisplayOffsetX", t.mWallpaperDisplayOffsetX);
+ dumpValue(pw, prefix, "mWallpaperDisplayOffsetY", t.mWallpaperDisplayOffsetY);
}
}
+ private void dumpValue(PrintWriter pw, String prefix, String valueName, float value) {
+ pw.print(prefix); pw.print(" " + valueName + "=");
+ pw.println(value >= 0 ? value : "NA");
+ }
+
/** Helper class for storing the results of a wallpaper target find operation. */
final private static class FindWallpaperTargetResult {
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index c7fd147..50ef52a 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -42,6 +42,12 @@
private static final String TAG = TAG_WITH_CLASS_NAME ? "WallpaperWindowToken" : TAG_WM;
private boolean mShowWhenLocked = false;
+ float mWallpaperX = -1;
+ float mWallpaperY = -1;
+ float mWallpaperXStep = -1;
+ float mWallpaperYStep = -1;
+ int mWallpaperDisplayOffsetX = Integer.MIN_VALUE;
+ int mWallpaperDisplayOffsetY = Integer.MIN_VALUE;
WallpaperWindowToken(WindowManagerService service, IBinder token, boolean explicit,
DisplayContent dc, boolean ownerCanManageAppTokens) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerFlags.java b/services/core/java/com/android/server/wm/WindowManagerFlags.java
new file mode 100644
index 0000000..00b9b4c
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WindowManagerFlags.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import com.android.window.flags.Flags;
+
+/**
+ * Utility class to read the flags used in the WindowManager server.
+ *
+ * It is not very cheap to read trunk stable flag, so having a centralized place to cache the flag
+ * values in the system server side.
+ *
+ * Flags should be defined in `core.java.android.window.flags` to allow access from client side.
+ *
+ * To override flag:
+ * adb shell device_config put [namespace] [package].[name] [true/false]
+ * adb reboot
+ *
+ * To access in wm:
+ * {@link WindowManagerService#mFlags}
+ *
+ * Notes:
+ * The system may use flags at anytime, so changing flags will only take effect after device
+ * reboot. Otherwise, it may result unexpected behavior, such as broken transition.
+ * When a flag needs to be read from both the server side and the client side, changing the flag
+ * value will result difference in server and client until device reboot.
+ */
+class WindowManagerFlags {
+
+ /* Start Available Flags */
+
+ final boolean mSyncWindowConfigUpdateFlag = Flags.syncWindowConfigUpdateFlag();
+
+ /* End Available Flags */
+}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c9107e8..f339d24 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -304,6 +304,7 @@
import android.window.ISurfaceSyncGroupCompletedListener;
import android.window.ITaskFpsCallback;
import android.window.ScreenCapture;
+import android.window.SystemPerformanceHinter;
import android.window.TaskSnapshot;
import android.window.WindowContainerToken;
import android.window.WindowContextInfo;
@@ -547,6 +548,8 @@
@VisibleForTesting
WindowManagerPolicy mPolicy;
+ final WindowManagerFlags mFlags;
+
final IActivityManager mActivityManager;
final ActivityManagerInternal mAmInternal;
final UserManagerInternal mUmInternal;
@@ -1036,6 +1039,8 @@
sThreadPriorityBooster.reset();
}
+ SystemPerformanceHinter mSystemPerformanceHinter;
+
void openSurfaceTransaction() {
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "openSurfaceTransaction");
@@ -1153,6 +1158,7 @@
mGlobalLock = atm.getGlobalLock();
mAtmService = atm;
mContext = context;
+ mFlags = new WindowManagerFlags();
mIsPc = mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
mAllowBootMessages = showBootMsgs;
mLimitedAlphaCompositing = context.getResources().getBoolean(
@@ -1329,6 +1335,13 @@
mBlurController = new BlurController(mContext, mPowerManager);
mTaskFpsCallbackController = new TaskFpsCallbackController(mContext);
mAccessibilityController = new AccessibilityController(this);
+ mSystemPerformanceHinter = new SystemPerformanceHinter(mContext, displayId -> {
+ synchronized (mGlobalLock) {
+ DisplayContent dc = mRoot.getDisplayContent(displayId);
+ return (dc == null) ? null : dc.getSurfaceControl();
+ }
+
+ }, mTransactionFactory);
}
DisplayAreaPolicy.Provider getDisplayAreaPolicyProvider() {
@@ -3101,10 +3114,15 @@
@Override
public void notifyKeyguardTrustedChanged() {
- synchronized (mGlobalLock) {
- if (mAtmService.mKeyguardController.isKeyguardShowing(DEFAULT_DISPLAY)) {
- mRoot.ensureActivitiesVisible(null, 0, false /* preserveWindows */);
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ if (mAtmService.mKeyguardController.isKeyguardShowing(DEFAULT_DISPLAY)) {
+ mRoot.ensureActivitiesVisible(null, 0, false /* preserveWindows */);
+ }
}
+ } finally {
+ Binder.restoreCallingIdentity(origId);
}
}
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index 08df651..a4adf58 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -65,7 +65,6 @@
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.content.PackageMonitor;
import com.android.server.credentials.metrics.ApiName;
import com.android.server.credentials.metrics.ApiStatus;
import com.android.server.infra.AbstractMasterSystemService;
@@ -90,7 +89,7 @@
*/
public final class CredentialManagerService
extends AbstractMasterSystemService<
- CredentialManagerService, CredentialManagerServiceImpl> {
+ CredentialManagerService, CredentialManagerServiceImpl> {
private static final String TAG = "CredManSysService";
private static final String PERMISSION_DENIED_ERROR = "permission_denied";
@@ -111,7 +110,8 @@
/** Cache of all ongoing request sessions per user id. */
@GuardedBy("mLock")
- private final SparseArray<Map<IBinder, RequestSession>> mRequestSessions = new SparseArray<>();
+ private final SparseArray<Map<IBinder, RequestSession>> mRequestSessions =
+ new SparseArray<>();
private final SessionManager mSessionManager = new SessionManager();
@@ -123,8 +123,6 @@
null,
PACKAGE_UPDATE_POLICY_REFRESH_EAGER);
mContext = context;
-
- mPackageMonitor.register(context, context.getMainLooper(), false);
}
@NonNull
@@ -141,7 +139,8 @@
serviceInfos.forEach(
info -> {
services.add(
- new CredentialManagerServiceImpl(this, mLock, resolvedUserId, info));
+ new CredentialManagerServiceImpl(this, mLock, resolvedUserId,
+ info));
});
return services;
}
@@ -217,8 +216,8 @@
for (CredentialManagerServiceImpl serviceToBeRemoved : servicesToBeRemoved) {
removeServiceFromCache(serviceToBeRemoved, userId);
removeServiceFromSystemServicesCache(serviceToBeRemoved, userId);
- removeServiceFromMultiModeSettings(
- serviceToBeRemoved.getComponentName().flattenToString(), userId);
+ removeServiceFromMultiModeSettings(serviceToBeRemoved.getComponentName()
+ .flattenToString(), userId);
CredentialDescriptionRegistry.forUser(userId)
.evictProviderWithPackageName(serviceToBeRemoved.getServicePackageName());
}
@@ -287,20 +286,13 @@
}
private static Set<ComponentName> getPrimaryProvidersForUserId(Context context, int userId) {
- final int resolvedUserId =
- ActivityManager.handleIncomingUser(
- Binder.getCallingPid(),
- Binder.getCallingUid(),
- userId,
- false,
- false,
- "getPrimaryProvidersForUserId",
- null);
- SecureSettingsServiceNameResolver resolver =
- new SecureSettingsServiceNameResolver(
- context,
- Settings.Secure.CREDENTIAL_SERVICE_PRIMARY,
- /* isMultipleMode= */ true);
+ final int resolvedUserId = ActivityManager.handleIncomingUser(
+ Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, false, false,
+ "getPrimaryProvidersForUserId", null);
+ SecureSettingsServiceNameResolver resolver = new SecureSettingsServiceNameResolver(
+ context, Settings.Secure.CREDENTIAL_SERVICE_PRIMARY,
+ /* isMultipleMode= */ true);
String[] serviceNames = resolver.readServiceNameList(resolvedUserId);
if (serviceNames == null) {
return new HashSet<ComponentName>();
@@ -337,8 +329,7 @@
final long origId = Binder.clearCallingIdentity();
try {
return DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_CREDENTIAL,
- DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API,
+ DeviceConfig.NAMESPACE_CREDENTIAL, DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API,
false);
} finally {
Binder.restoreCallingIdentity(origId);
@@ -354,14 +345,13 @@
List<ProviderSession> providerSessions = new ArrayList<>();
for (Pair<CredentialOption, CredentialDescriptionRegistry.FilterResult> result :
activeCredentialContainers) {
- ProviderSession providerSession =
- ProviderRegistryGetSession.createNewSession(
- mContext,
- UserHandle.getCallingUserId(),
- session,
- session.mClientAppInfo,
- result.second.mPackageName,
- result.first);
+ ProviderSession providerSession = ProviderRegistryGetSession.createNewSession(
+ mContext,
+ UserHandle.getCallingUserId(),
+ session,
+ session.mClientAppInfo,
+ result.second.mPackageName,
+ result.first);
providerSessions.add(providerSession);
session.addProviderSession(providerSession.getComponentName(), providerSession);
}
@@ -377,23 +367,23 @@
List<ProviderSession> providerSessions = new ArrayList<>();
for (Pair<CredentialOption, CredentialDescriptionRegistry.FilterResult> result :
activeCredentialContainers) {
- ProviderSession providerSession =
- ProviderRegistryGetSession.createNewSession(
- mContext,
- UserHandle.getCallingUserId(),
- session,
- session.mClientAppInfo,
- result.second.mPackageName,
- result.first);
+ ProviderSession providerSession = ProviderRegistryGetSession.createNewSession(
+ mContext,
+ UserHandle.getCallingUserId(),
+ session,
+ session.mClientAppInfo,
+ result.second.mPackageName,
+ result.first);
providerSessions.add(providerSession);
session.addProviderSession(providerSession.getComponentName(), providerSession);
}
return providerSessions;
}
+
@NonNull
private Set<Pair<CredentialOption, CredentialDescriptionRegistry.FilterResult>>
- getFilteredResultFromRegistry(List<CredentialOption> options) {
+ getFilteredResultFromRegistry(List<CredentialOption> options) {
// Session for active/provisioned credential descriptions;
CredentialDescriptionRegistry registry =
CredentialDescriptionRegistry.forUser(UserHandle.getCallingUserId());
@@ -403,12 +393,10 @@
options.stream()
.map(
getCredentialOption ->
- new HashSet<>(
- getCredentialOption
- .getCredentialRetrievalData()
- .getStringArrayList(
- CredentialOption
- .SUPPORTED_ELEMENT_KEYS)))
+ new HashSet<>(getCredentialOption
+ .getCredentialRetrievalData()
+ .getStringArrayList(
+ CredentialOption.SUPPORTED_ELEMENT_KEYS)))
.collect(Collectors.toSet());
// All requested credential descriptions based on the given request.
@@ -420,14 +408,12 @@
for (CredentialDescriptionRegistry.FilterResult filterResult : filterResults) {
for (CredentialOption credentialOption : options) {
- Set<String> requestedElementKeys =
- new HashSet<>(
- credentialOption
- .getCredentialRetrievalData()
- .getStringArrayList(
- CredentialOption.SUPPORTED_ELEMENT_KEYS));
- if (CredentialDescriptionRegistry.checkForMatch(
- filterResult.mElementKeys, requestedElementKeys)) {
+ Set<String> requestedElementKeys = new HashSet<>(
+ credentialOption
+ .getCredentialRetrievalData()
+ .getStringArrayList(CredentialOption.SUPPORTED_ELEMENT_KEYS));
+ if (CredentialDescriptionRegistry.checkForMatch(filterResult.mElementKeys,
+ requestedElementKeys)) {
result.add(new Pair<>(credentialOption, filterResult));
}
}
@@ -463,7 +449,9 @@
}
private CallingAppInfo constructCallingAppInfo(
- String realPackageName, int userId, @Nullable String origin) {
+ String realPackageName,
+ int userId,
+ @Nullable String origin) {
final PackageInfo packageInfo;
CallingAppInfo callingAppInfo;
try {
@@ -489,7 +477,8 @@
GetCredentialRequest request,
IGetCandidateCredentialsCallback callback,
final String callingPackage) {
- Slog.i(TAG, "starting getCandidateCredentials with callingPackage: " + callingPackage);
+ Slog.i(TAG, "starting getCandidateCredentials with callingPackage: "
+ + callingPackage);
ICancellationSignal cancelTransport = CancellationSignal.createTransport();
final int userId = UserHandle.getCallingUserId();
@@ -507,7 +496,8 @@
request,
constructCallingAppInfo(callingPackage, userId, request.getOrigin()),
getEnabledProvidersForUser(userId),
- CancellationSignal.fromTransport(cancelTransport));
+ CancellationSignal.fromTransport(cancelTransport)
+ );
addSessionLocked(userId, session);
List<ProviderSession> providerSessions =
@@ -541,7 +531,8 @@
IGetCredentialCallback callback,
final String callingPackage) {
final long timestampBegan = System.nanoTime();
- Slog.i(TAG, "starting executeGetCredential with callingPackage: " + callingPackage);
+ Slog.i(TAG, "starting executeGetCredential with callingPackage: "
+ + callingPackage);
ICancellationSignal cancelTransport = CancellationSignal.createTransport();
final int userId = UserHandle.getCallingUserId();
@@ -566,7 +557,8 @@
timestampBegan);
addSessionLocked(userId, session);
- List<ProviderSession> providerSessions = prepareProviderSessions(request, session);
+ List<ProviderSession> providerSessions =
+ prepareProviderSessions(request, session);
if (providerSessions.isEmpty()) {
try {
@@ -625,17 +617,15 @@
if (providerSessions.isEmpty()) {
try {
prepareGetCredentialCallback.onResponse(
- new PrepareGetCredentialResponseInternal(
- PermissionUtils.hasPermission(
- mContext,
- callingPackage,
- Manifest.permission
- .CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS
- ),
- /* credentialResultTypes= */ null,
- /* hasAuthenticationResults= */ false,
- /* hasRemoteResults= */ false,
- /* pendingIntent= */ null));
+ new PrepareGetCredentialResponseInternal(PermissionUtils.hasPermission(
+ mContext,
+ callingPackage,
+ Manifest.permission
+ .CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS),
+ /*credentialResultTypes=*/null,
+ /*hasAuthenticationResults=*/false,
+ /*hasRemoteResults=*/false,
+ /*pendingIntent=*/null));
} catch (RemoteException e) {
Slog.e(
TAG,
@@ -651,32 +641,27 @@
}
private List<ProviderSession> prepareProviderSessions(
- GetCredentialRequest request, GetRequestSession session) {
+ GetCredentialRequest request,
+ GetRequestSession session) {
List<ProviderSession> providerSessions;
if (isCredentialDescriptionApiEnabled()) {
List<CredentialOption> optionsThatRequireActiveCredentials =
request.getCredentialOptions().stream()
- .filter(
- credentialOption ->
- credentialOption
- .getCredentialRetrievalData()
- .getStringArrayList(
- CredentialOption
- .SUPPORTED_ELEMENT_KEYS)
- != null)
+ .filter(credentialOption -> credentialOption
+ .getCredentialRetrievalData()
+ .getStringArrayList(
+ CredentialOption
+ .SUPPORTED_ELEMENT_KEYS) != null)
.toList();
List<CredentialOption> optionsThatDoNotRequireActiveCredentials =
request.getCredentialOptions().stream()
- .filter(
- credentialOption ->
- credentialOption
- .getCredentialRetrievalData()
- .getStringArrayList(
- CredentialOption
- .SUPPORTED_ELEMENT_KEYS)
- == null)
+ .filter(credentialOption -> credentialOption
+ .getCredentialRetrievalData()
+ .getStringArrayList(
+ CredentialOption
+ .SUPPORTED_ELEMENT_KEYS) == null)
.toList();
List<ProviderSession> sessionsWithoutRemoteService =
@@ -721,7 +706,8 @@
ICreateCredentialCallback callback,
String callingPackage) {
final long timestampBegan = System.nanoTime();
- Slog.i(TAG, "starting executeCreateCredential with callingPackage: " + callingPackage);
+ Slog.i(TAG, "starting executeCreateCredential with callingPackage: "
+ + callingPackage);
ICancellationSignal cancelTransport = CancellationSignal.createTransport();
if (request.getOrigin() != null) {
@@ -770,8 +756,8 @@
} catch (RemoteException e) {
Slog.e(
TAG,
- "Issue invoking onError on ICreateCredentialCallback " + "callback: ",
- e);
+ "Issue invoking onError on ICreateCredentialCallback "
+ + "callback: ", e);
}
}
@@ -784,8 +770,8 @@
try {
var initMetric = session.mRequestSessionMetric.getInitialPhaseMetric();
initMetric.setCredentialServiceBeginQueryTimeNanoseconds(System.nanoTime());
- MetricUtilities.logApiCalledInitialPhase(
- initMetric, session.mRequestSessionMetric.returnIncrementSequence());
+ MetricUtilities.logApiCalledInitialPhase(initMetric,
+ session.mRequestSessionMetric.returnIncrementSequence());
} catch (Exception e) {
Slog.i(TAG, "Unexpected error during metric logging: ", e);
}
@@ -793,32 +779,25 @@
@Override
public void setEnabledProviders(
- List<String> primaryProviders,
- List<String> providers,
- int userId,
+ List<String> primaryProviders, List<String> providers, int userId,
ISetEnabledProvidersCallback callback) {
final int callingUid = Binder.getCallingUid();
if (!hasWriteSecureSettingsPermission()) {
try {
MetricUtilities.logApiCalledSimpleV2(
- ApiName.SET_ENABLED_PROVIDERS, ApiStatus.FAILURE, callingUid);
+ ApiName.SET_ENABLED_PROVIDERS,
+ ApiStatus.FAILURE, callingUid);
callback.onError(
PERMISSION_DENIED_ERROR, PERMISSION_DENIED_WRITE_SECURE_SETTINGS_ERROR);
} catch (RemoteException e) {
MetricUtilities.logApiCalledSimpleV2(
- ApiName.SET_ENABLED_PROVIDERS, ApiStatus.FAILURE, callingUid);
+ ApiName.SET_ENABLED_PROVIDERS,
+ ApiStatus.FAILURE, callingUid);
Slog.e(TAG, "Issue with invoking response: ", e);
}
return;
}
- // If we don't have any primary providers enabled anymore then
- // we should erase all the providers since the feature is
- // now disabled.
- if (primaryProviders.isEmpty()) {
- providers.clear();
- }
-
userId =
ActivityManager.handleIncomingUser(
Binder.getCallingPid(),
@@ -829,19 +808,17 @@
"setEnabledProviders",
null);
- Set<String> enabledProviders = new HashSet<>(providers);
- enabledProviders.addAll(primaryProviders);
+ Set<String> enableProvider = new HashSet<>(providers);
+ enableProvider.addAll(primaryProviders);
boolean writeEnabledStatus =
- Settings.Secure.putStringForUser(
- getContext().getContentResolver(),
+ Settings.Secure.putStringForUser(getContext().getContentResolver(),
Settings.Secure.CREDENTIAL_SERVICE,
- String.join(":", enabledProviders),
+ String.join(":", enableProvider),
userId);
boolean writePrimaryStatus =
- Settings.Secure.putStringForUser(
- getContext().getContentResolver(),
+ Settings.Secure.putStringForUser(getContext().getContentResolver(),
Settings.Secure.CREDENTIAL_SERVICE_PRIMARY,
String.join(":", primaryProviders),
userId);
@@ -850,13 +827,15 @@
Slog.e(TAG, "Failed to store setting containing enabled or primary providers");
try {
MetricUtilities.logApiCalledSimpleV2(
- ApiName.SET_ENABLED_PROVIDERS, ApiStatus.FAILURE, callingUid);
+ ApiName.SET_ENABLED_PROVIDERS,
+ ApiStatus.FAILURE, callingUid);
callback.onError(
"failed_setting_store",
"Failed to store setting containing enabled or primary providers");
} catch (RemoteException e) {
MetricUtilities.logApiCalledSimpleV2(
- ApiName.SET_ENABLED_PROVIDERS, ApiStatus.FAILURE, callingUid);
+ ApiName.SET_ENABLED_PROVIDERS,
+ ApiStatus.FAILURE, callingUid);
Slog.e(TAG, "Issue with invoking error response: ", e);
return;
}
@@ -865,11 +844,13 @@
// Call the callback.
try {
MetricUtilities.logApiCalledSimpleV2(
- ApiName.SET_ENABLED_PROVIDERS, ApiStatus.SUCCESS, callingUid);
+ ApiName.SET_ENABLED_PROVIDERS,
+ ApiStatus.SUCCESS, callingUid);
callback.onResponse();
} catch (RemoteException e) {
MetricUtilities.logApiCalledSimpleV2(
- ApiName.SET_ENABLED_PROVIDERS, ApiStatus.FAILURE, callingUid);
+ ApiName.SET_ENABLED_PROVIDERS,
+ ApiStatus.FAILURE, callingUid);
Slog.e(TAG, "Issue with invoking response: ", e);
// TODO: Propagate failure
}
@@ -878,10 +859,8 @@
@Override
public boolean isEnabledCredentialProviderService(
ComponentName componentName, String callingPackage) {
- Slog.i(
- TAG,
- "isEnabledCredentialProviderService with componentName: "
- + componentName.flattenToString());
+ Slog.i(TAG, "isEnabledCredentialProviderService with componentName: "
+ + componentName.flattenToString());
// TODO(253157366): Check additional set of services.
final int userId = UserHandle.getCallingUserId();
@@ -898,8 +877,7 @@
// The component name and the package name do not match.
MetricUtilities.logApiCalledSimpleV2(
ApiName.IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE,
- ApiStatus.FAILURE,
- callingUid);
+ ApiStatus.FAILURE, callingUid);
Slog.w(
TAG,
"isEnabledCredentialProviderService: Component name does "
@@ -908,8 +886,7 @@
}
MetricUtilities.logApiCalledSimpleV2(
ApiName.IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE,
- ApiStatus.SUCCESS,
- callingUid);
+ ApiStatus.SUCCESS, callingUid);
return true;
}
}
@@ -924,14 +901,13 @@
verifyGetProvidersPermission();
final int callingUid = Binder.getCallingUid();
MetricUtilities.logApiCalledSimpleV2(
- ApiName.GET_CREDENTIAL_PROVIDER_SERVICES, ApiStatus.SUCCESS, callingUid);
+ ApiName.GET_CREDENTIAL_PROVIDER_SERVICES,
+ ApiStatus.SUCCESS, callingUid);
+ return CredentialProviderInfoFactory
+ .getCredentialProviderServices(
+ mContext, userId, providerFilter, getEnabledProvidersForUser(userId),
+ getPrimaryProvidersForUserId(mContext, userId));
- return CredentialProviderInfoFactory.getCredentialProviderServices(
- mContext,
- userId,
- providerFilter,
- getEnabledProvidersForUser(userId),
- getPrimaryProvidersForUserId(mContext, userId));
}
@Override
@@ -941,10 +917,7 @@
final int userId = UserHandle.getCallingUserId();
return CredentialProviderInfoFactory.getCredentialProviderServicesForTesting(
- mContext,
- userId,
- providerFilter,
- getEnabledProvidersForUser(userId),
+ mContext, userId, providerFilter, getEnabledProvidersForUser(userId),
getPrimaryProvidersForUserId(mContext, userId));
}
@@ -962,22 +935,15 @@
}
private Set<ComponentName> getEnabledProvidersForUser(int userId) {
- final int resolvedUserId =
- ActivityManager.handleIncomingUser(
- Binder.getCallingPid(),
- Binder.getCallingUid(),
- userId,
- false,
- false,
- "getEnabledProvidersForUser",
- null);
+ final int resolvedUserId = ActivityManager.handleIncomingUser(
+ Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, false, false,
+ "getEnabledProvidersForUser", null);
Set<ComponentName> enabledProviders = new HashSet<>();
- String directValue =
- Settings.Secure.getStringForUser(
- mContext.getContentResolver(),
- Settings.Secure.CREDENTIAL_SERVICE,
- resolvedUserId);
+ String directValue = Settings.Secure.getStringForUser(
+ mContext.getContentResolver(), Settings.Secure.CREDENTIAL_SERVICE,
+ resolvedUserId);
if (!TextUtils.isEmpty(directValue)) {
String[] components = directValue.split(":");
@@ -998,7 +964,8 @@
IClearCredentialStateCallback callback,
String callingPackage) {
final long timestampBegan = System.nanoTime();
- Slog.i(TAG, "starting clearCredentialState with callingPackage: " + callingPackage);
+ Slog.i(TAG, "starting clearCredentialState with callingPackage: "
+ + callingPackage);
final int userId = UserHandle.getCallingUserId();
int callingUid = Binder.getCallingUid();
enforceCallingPackage(callingPackage, callingUid);
@@ -1029,13 +996,13 @@
if (providerSessions.isEmpty()) {
try {
// TODO("Replace with properly defined error type")
- callback.onError("UNKNOWN", "No credentials available on " + "this device");
+ callback.onError("UNKNOWN", "No credentials available on "
+ + "this device");
} catch (RemoteException e) {
Slog.e(
TAG,
"Issue invoking onError on IClearCredentialStateCallback "
- + "callback: ",
- e);
+ + "callback: ", e);
}
}
@@ -1068,7 +1035,9 @@
public void unregisterCredentialDescription(
UnregisterCredentialDescriptionRequest request, String callingPackage)
throws IllegalArgumentException {
- Slog.i(TAG, "unregisterCredentialDescription with callingPackage: " + callingPackage);
+ Slog.i(TAG, "unregisterCredentialDescription with callingPackage: "
+ + callingPackage);
+
if (!isCredentialDescriptionApiEnabled()) {
throw new UnsupportedOperationException("Feature not supported");
@@ -1092,18 +1061,18 @@
}
private void enforcePermissionForAllowedProviders(GetCredentialRequest request) {
- boolean containsAllowedProviders =
- request.getCredentialOptions().stream()
- .anyMatch(
- option ->
- option.getAllowedProviders() != null
- && !option.getAllowedProviders().isEmpty());
+ boolean containsAllowedProviders = request.getCredentialOptions()
+ .stream()
+ .anyMatch(option -> option.getAllowedProviders() != null
+ && !option.getAllowedProviders().isEmpty());
if (containsAllowedProviders) {
- mContext.enforceCallingPermission(CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS, null);
+ mContext.enforceCallingPermission(CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS,
+ null);
}
}
- private void addSessionLocked(@UserIdInt int userId, RequestSession requestSession) {
+ private void addSessionLocked(@UserIdInt int userId,
+ RequestSession requestSession) {
synchronized (mLock) {
mSessionManager.addSession(userId, requestSession.mRequestId, requestSession);
}
@@ -1111,11 +1080,11 @@
private void enforceCallingPackage(String callingPackage, int callingUid) {
int packageUid;
- PackageManager pm =
- mContext.createContextAsUser(UserHandle.getUserHandleForUid(callingUid), 0)
- .getPackageManager();
+ PackageManager pm = mContext.createContextAsUser(
+ UserHandle.getUserHandleForUid(callingUid), 0).getPackageManager();
try {
- packageUid = pm.getPackageUid(callingPackage, PackageManager.PackageInfoFlags.of(0));
+ packageUid = pm.getPackageUid(callingPackage,
+ PackageManager.PackageInfoFlags.of(0));
} catch (PackageManager.NameNotFoundException e) {
throw new SecurityException(callingPackage + " not found");
}
@@ -1141,72 +1110,4 @@
mRequestSessions.get(userId).put(token, requestSession);
}
}
-
- /** Updates settings when packages are removed. */
- private final PackageMonitor mPackageMonitor =
- new PackageMonitor() {
-
- @Override
- public void onPackageRemoved(String packageName, int uid) {
- Slog.d(TAG, "onPackageRemoved: " + packageName);
-
- // Remove any providers from the primary setting that contain the package name
- // being removed.
- Set<String> primaryProviders =
- getStoredProviders(
- Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, packageName);
- if (!Settings.Secure.putString(
- getContext().getContentResolver(),
- Settings.Secure.CREDENTIAL_SERVICE_PRIMARY,
- String.join(":", primaryProviders))) {
- Slog.w(TAG, "Failed to remove primary package: " + packageName);
- return;
- }
-
- // Get the secondary providers and if there are no primary providers then
- // we should erase all the providers from the secondary list because the
- // feature is now disabled.
- if (!primaryProviders.isEmpty()) {
- return;
- }
-
- if (!Settings.Secure.putString(
- getContext().getContentResolver(),
- Settings.Secure.CREDENTIAL_SERVICE,
- "")) {
- Slog.w(TAG, "Failed to remove secondary package: " + packageName);
- return;
- }
- }
-
- private Set<String> getStoredProviders(String key, String packageName) {
- // Get the current providers.
- String rawProviders =
- Settings.Secure.getStringForUser(
- getContext().getContentResolver(), key,
- UserHandle.myUserId());
- if (rawProviders == null) {
- Slog.w(TAG, "settings key is null: " + key);
- return new HashSet<>();
- }
-
- // If the app being removed matches any of the package names from
- // this list then don't add it in the output.
- Set<String> providers = new HashSet<>();
- for (String rawComponentName : rawProviders.split(":")) {
- if (TextUtils.isEmpty(rawComponentName)
- || rawComponentName.equals("null")) {
- Slog.d(TAG, "provider component name is empty or null");
- continue;
- }
-
- ComponentName cn = ComponentName.unflattenFromString(rawComponentName);
- if (cn != null && !cn.getPackageName().equals(packageName)) {
- providers.add(cn.flattenToString());
- }
- }
-
- return providers;
- }
- };
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 43e47d7..49af89b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -872,17 +872,9 @@
"enable_permission_based_access";
private static final boolean DEFAULT_VALUE_PERMISSION_BASED_ACCESS_FLAG = false;
- private static final String ENABLE_DEVICE_POLICY_ENGINE_FOR_FINANCE_FLAG =
- "enable_device_policy_engine";
- private static final boolean DEFAULT_ENABLE_DEVICE_POLICY_ENGINE_FOR_FINANCE_FLAG = true;
-
// TODO(b/265683382) remove the flag after rollout.
public static final boolean DEFAULT_KEEP_PROFILES_RUNNING_FLAG = false;
- // TODO(b/261999445) remove the flag after rollout.
- private static final String HEADLESS_FLAG = "headless";
- private static final boolean DEFAULT_HEADLESS_FLAG = true;
-
// TODO(b/266831522) remove the flag after rollout.
private static final String APPLICATION_EXEMPTIONS_FLAG = "application_exemptions";
private static final boolean DEFAULT_APPLICATION_EXEMPTIONS_FLAG = true;
@@ -4025,75 +4017,41 @@
}
private void clearDeviceOwnerUserRestriction(UserHandle userHandle) {
- if (isHeadlessFlagEnabled()) {
- for (int userId : mUserManagerInternal.getUserIds()) {
- UserHandle user = UserHandle.of(userId);
- // ManagedProvisioning/DPC sets DISALLOW_ADD_USER. Clear to recover to the
- // original state
- if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_USER, user)) {
- mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER,
- false, user);
- }
- // When a device owner is set, the system automatically restricts adding a
- // managed profile.
- // Remove this restriction when the device owner is cleared.
- if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE,
- user)) {
- mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE,
- false,
- user);
- }
- // When a device owner is set, the system automatically restricts adding a
- // clone profile.
- // Remove this restriction when the device owner is cleared.
- if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE, user)) {
- mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE,
- false, user);
- }
-
- // When a device owner is set, the system automatically restricts adding a
- // private profile.
- // Remove this restriction when the device owner is cleared.
- if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_PRIVATE_PROFILE,
- user)) {
- mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_PRIVATE_PROFILE,
- false, user);
- }
- }
- } else {
- // ManagedProvisioning/DPC sets DISALLOW_ADD_USER. Clear to recover to the original state
- if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_USER, userHandle)) {
- mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, false,
- userHandle);
+ for (int userId : mUserManagerInternal.getUserIds()) {
+ UserHandle user = UserHandle.of(userId);
+ // ManagedProvisioning/DPC sets DISALLOW_ADD_USER. Clear to recover to the
+ // original state
+ if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_USER, user)) {
+ mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER,
+ false, user);
}
// When a device owner is set, the system automatically restricts adding a
// managed profile.
// Remove this restriction when the device owner is cleared.
if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE,
- userHandle)) {
+ user)) {
mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE,
false,
- userHandle);
+ user);
}
- // When a device owner is set, the system automatically restricts adding a clone
- // profile.
+ // When a device owner is set, the system automatically restricts adding a
+ // clone profile.
// Remove this restriction when the device owner is cleared.
- if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE,
- userHandle)) {
+ if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE, user)) {
mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE,
- false,
- userHandle);
+ false, user);
}
// When a device owner is set, the system automatically restricts adding a
// private profile.
// Remove this restriction when the device owner is cleared.
if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_PRIVATE_PROFILE,
- userHandle)) {
+ user)) {
mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_PRIVATE_PROFILE,
- false, userHandle);
+ false, user);
}
}
+
}
/**
@@ -6476,7 +6434,7 @@
KeyChain.bindAsUser(mContext, userHandle)) {
IKeyChainService keyChain = keyChainConnection.getService();
return keyChain.setGrant(granteeUid, alias, hasGrant);
- } catch (RemoteException e) {
+ } catch (RemoteException | AssertionError e) {
Slogf.e(LOG_TAG, "Setting grant for package.", e);
return false;
}
@@ -7956,14 +7914,8 @@
hasCallingOrSelfPermission(permission.TRIGGER_LOST_MODE));
synchronized (getLockObject()) {
- // TODO(b/261999445): Remove
- ActiveAdmin admin;
- if (isHeadlessFlagEnabled()) {
- admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
- } else {
- admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
- UserHandle.USER_SYSTEM);
- }
+ ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
+
Preconditions.checkState(admin != null,
"Lost mode location updates can only be sent on an organization-owned device.");
mInjector.binderWithCleanCallingIdentity(() -> {
@@ -9449,39 +9401,24 @@
// profile, such that the admin on that managed profile has extended management
// capabilities that can affect the entire device (but not access private data
// on the primary profile).
- if (isHeadlessFlagEnabled()) {
- for (int u : mUserManagerInternal.getUserIds()) {
- mUserManager.setUserRestriction(
- UserManager.DISALLOW_ADD_MANAGED_PROFILE, true,
- UserHandle.of(u));
- // Restrict adding a clone profile when a device owner is set on the device.
- // That is to prevent the co-existence of a clone profile and a device owner
- // on the same device.
- // CDD for reference : https://source.android.com/compatibility/12/android-12-cdd#95_multi-user_support
- mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE,
- true,
- UserHandle.of(u));
-
- // Restrict adding a private profile when a device owner is set.
- mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_PRIVATE_PROFILE,
- true,
- UserHandle.of(u));
- }
- } else {
- mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE,
- true,
- UserHandle.of(userId));
+ for (int u : mUserManagerInternal.getUserIds()) {
+ mUserManager.setUserRestriction(
+ UserManager.DISALLOW_ADD_MANAGED_PROFILE, true,
+ UserHandle.of(u));
// Restrict adding a clone profile when a device owner is set on the device.
// That is to prevent the co-existence of a clone profile and a device owner
// on the same device.
// CDD for reference : https://source.android.com/compatibility/12/android-12-cdd#95_multi-user_support
mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_CLONE_PROFILE,
true,
- UserHandle.of(userId));
+ UserHandle.of(u));
+
+ // Restrict adding a private profile when a device owner is set.
mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_PRIVATE_PROFILE,
true,
- UserHandle.of(userId));
+ UserHandle.of(u));
}
+
// TODO Send to system too?
sendOwnerChangedBroadcast(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED, userId);
});
@@ -20119,14 +20056,8 @@
synchronized (getLockObject()) {
// Only DO or COPE PO can turn on CC mode, so take a shortcut here and only look at
// their ActiveAdmin, instead of iterating through all admins.
- ActiveAdmin admin;
- // TODO(b/261999445): remove
- if (isHeadlessFlagEnabled()) {
- admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
- } else {
- admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
- UserHandle.USER_SYSTEM);
- }
+ ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
+
return admin != null ? admin.mCommonCriteriaMode : false;
}
}
@@ -21393,7 +21324,7 @@
}
private void disallowAddUser() {
- if (!isHeadlessFlagEnabled() || mIsAutomotive) {
+ if (mIsAutomotive) {
// Auto still enables adding users due to the communal nature of those devices
if (mInjector.userManagerIsHeadlessSystemUserMode()) {
Slogf.i(LOG_TAG, "Not setting DISALLOW_ADD_USER on headless system user mode.");
@@ -21711,14 +21642,7 @@
}
private boolean isUsbDataSignalingEnabledInternalLocked() {
- // TODO(b/261999445): remove
- ActiveAdmin admin;
- if (isHeadlessFlagEnabled()) {
- admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
- } else {
- admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
- UserHandle.USER_SYSTEM);
- }
+ ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
return admin == null || admin.mUsbDataSignalingEnabled;
}
@@ -21785,14 +21709,7 @@
@Override
public int getMinimumRequiredWifiSecurityLevel() {
synchronized (getLockObject()) {
- ActiveAdmin admin;
- // TODO(b/261999445): remove
- if (isHeadlessFlagEnabled()) {
- admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
- } else {
- admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
- UserHandle.USER_SYSTEM);
- }
+ ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
return (admin == null) ? DevicePolicyManager.WIFI_SECURITY_OPEN
: admin.mWifiMinimumSecurityLevel;
}
@@ -23169,16 +23086,8 @@
|| isProfileOwnerOfOrganizationOwnedDevice(caller));
}
synchronized (getLockObject()) {
- // TODO(b/261999445): Remove
- ActiveAdmin admin;
- if (isHeadlessFlagEnabled()) {
- admin =
+ ActiveAdmin admin =
getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
- } else {
- admin =
- getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
- UserHandle.USER_SYSTEM);
- }
if (admin != null) {
final String memtagProperty = "arm64.memtag.bootctl";
@@ -23211,29 +23120,14 @@
|| isSystemUid(caller));
}
synchronized (getLockObject()) {
- // TODO(b/261999445): Remove
- ActiveAdmin admin;
- if (isHeadlessFlagEnabled()) {
- admin =
+ ActiveAdmin admin =
getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
- } else {
- admin =
- getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
- UserHandle.USER_SYSTEM);
- }
return admin != null
? admin.mtePolicy
: DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY;
}
}
- private boolean isHeadlessFlagEnabled() {
- return DeviceConfig.getBoolean(
- NAMESPACE_DEVICE_POLICY_MANAGER,
- HEADLESS_FLAG,
- DEFAULT_HEADLESS_FLAG);
- }
-
@Override
public ManagedSubscriptionsPolicy getManagedSubscriptionsPolicy() {
synchronized (getLockObject()) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 49ad84a..59f1edc 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -51,6 +51,7 @@
import android.database.sqlite.SQLiteCompatibilityWalFlags;
import android.database.sqlite.SQLiteGlobal;
import android.graphics.GraphicsStatsService;
+import android.graphics.Typeface;
import android.hardware.display.DisplayManagerInternal;
import android.net.ConnectivityManager;
import android.net.ConnectivityModuleConnector;
@@ -916,6 +917,14 @@
SystemServerInitThreadPool tp = SystemServerInitThreadPool.start();
mDumper.addDumpable(tp);
+ // Lazily load the pre-installed system font map in SystemServer only if we're not doing
+ // the optimized font loading in the FontManagerService.
+ if (!com.android.text.flags.Flags.useOptimizedBoottimeFontLoading()
+ && Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) {
+ Slog.i(TAG, "Loading pre-installed system font map.");
+ Typeface.loadPreinstalledSystemFontMap();
+ }
+
// Attach JVMTI agent if this is a debuggable build and the system property is set.
if (Build.IS_DEBUGGABLE) {
// Property is of the form "library_path=parameters".
diff --git a/services/permission/java/com/android/server/permission/access/AccessCheckingService.kt b/services/permission/java/com/android/server/permission/access/AccessCheckingService.kt
index c1d137f..93530cf 100644
--- a/services/permission/java/com/android/server/permission/access/AccessCheckingService.kt
+++ b/services/permission/java/com/android/server/permission/access/AccessCheckingService.kt
@@ -43,8 +43,7 @@
@Keep
class AccessCheckingService(context: Context) : SystemService(context) {
- @Volatile
- private lateinit var state: AccessState
+ @Volatile private lateinit var state: AccessState
private val stateLock = Any()
private val policy = AccessPolicy()
@@ -86,17 +85,22 @@
val state = MutableAccessState()
policy.initialize(
- state, userIds, packageStates, disabledSystemPackageStates, knownPackages, isLeanback,
- configPermissions, privilegedPermissionAllowlistPackages, permissionAllowlist,
+ state,
+ userIds,
+ packageStates,
+ disabledSystemPackageStates,
+ knownPackages,
+ isLeanback,
+ configPermissions,
+ privilegedPermissionAllowlistPackages,
+ permissionAllowlist,
implicitToSourcePermissions
)
persistence.initialize()
persistence.read(state)
this.state = state
- mutateState {
- with(policy) { onInitialized() }
- }
+ mutateState { with(policy) { onInitialized() } }
appOpService.initialize()
permissionService.initialize()
@@ -106,40 +110,40 @@
get() = PackageManager.FEATURE_LEANBACK in availableFeatures
private val SystemConfig.privilegedPermissionAllowlistPackages: IndexedListSet<String>
- get() = MutableIndexedListSet<String>().apply {
- this += "android"
- if (PackageManager.FEATURE_AUTOMOTIVE in availableFeatures) {
- // Note that SystemProperties.get(String, String) forces returning an empty string
- // even if we pass null for the def parameter.
- val carServicePackage = SystemProperties.get("ro.android.car.carservice.package")
- if (carServicePackage.isNotEmpty()) {
- this += carServicePackage
+ get() =
+ MutableIndexedListSet<String>().apply {
+ this += "android"
+ if (PackageManager.FEATURE_AUTOMOTIVE in availableFeatures) {
+ // Note that SystemProperties.get(String, String) forces returning an empty
+ // string
+ // even if we pass null for the def parameter.
+ val carServicePackage =
+ SystemProperties.get("ro.android.car.carservice.package")
+ if (carServicePackage.isNotEmpty()) {
+ this += carServicePackage
+ }
}
}
- }
private val SystemConfig.implicitToSourcePermissions: IndexedMap<String, IndexedListSet<String>>
@Suppress("UNCHECKED_CAST")
- get() = MutableIndexedMap<String, MutableIndexedListSet<String>>().apply {
- splitPermissions.forEach { splitPermissionInfo ->
- val sourcePermissionName = splitPermissionInfo.splitPermission
- splitPermissionInfo.newPermissions.forEach { implicitPermissionName ->
- getOrPut(implicitPermissionName) { MutableIndexedListSet() } +=
- sourcePermissionName
+ get() =
+ MutableIndexedMap<String, MutableIndexedListSet<String>>().apply {
+ splitPermissions.forEach { splitPermissionInfo ->
+ val sourcePermissionName = splitPermissionInfo.splitPermission
+ splitPermissionInfo.newPermissions.forEach { implicitPermissionName ->
+ getOrPut(implicitPermissionName) { MutableIndexedListSet() } +=
+ sourcePermissionName
+ }
}
- }
- } as IndexedMap<String, IndexedListSet<String>>
+ } as IndexedMap<String, IndexedListSet<String>>
internal fun onUserAdded(userId: Int) {
- mutateState {
- with(policy) { onUserAdded(userId) }
- }
+ mutateState { with(policy) { onUserAdded(userId) } }
}
internal fun onUserRemoved(userId: Int) {
- mutateState {
- with(policy) { onUserRemoved(userId) }
- }
+ mutateState { with(policy) { onUserRemoved(userId) } }
}
internal fun onStorageVolumeMounted(
@@ -152,8 +156,12 @@
mutateState {
with(policy) {
onStorageVolumeMounted(
- packageStates, disabledSystemPackageStates, knownPackages, volumeUuid,
- packageNames, isSystemUpdated
+ packageStates,
+ disabledSystemPackageStates,
+ knownPackages,
+ volumeUuid,
+ packageNames,
+ isSystemUpdated
)
}
}
@@ -165,7 +173,10 @@
mutateState {
with(policy) {
onPackageAdded(
- packageStates, disabledSystemPackageStates, knownPackages, packageName
+ packageStates,
+ disabledSystemPackageStates,
+ knownPackages,
+ packageName
)
}
}
@@ -177,7 +188,11 @@
mutateState {
with(policy) {
onPackageRemoved(
- packageStates, disabledSystemPackageStates, knownPackages, packageName, appId
+ packageStates,
+ disabledSystemPackageStates,
+ knownPackages,
+ packageName,
+ appId
)
}
}
@@ -189,7 +204,11 @@
mutateState {
with(policy) {
onPackageInstalled(
- packageStates, disabledSystemPackageStates, knownPackages, packageName, userId
+ packageStates,
+ disabledSystemPackageStates,
+ knownPackages,
+ packageName,
+ userId
)
}
}
@@ -201,7 +220,11 @@
mutateState {
with(policy) {
onPackageUninstalled(
- packageStates, disabledSystemPackageStates, knownPackages, packageName, appId,
+ packageStates,
+ disabledSystemPackageStates,
+ knownPackages,
+ packageName,
+ appId,
userId
)
}
@@ -224,34 +247,42 @@
private fun PackageManagerInternal.getKnownPackages(
packageStates: Map<String, PackageState>
- ): IntMap<Array<String>> = MutableIntMap<Array<String>>().apply {
- this[KnownPackages.PACKAGE_INSTALLER] =
- getKnownPackageNames(KnownPackages.PACKAGE_INSTALLER, UserHandle.USER_SYSTEM)
- this[KnownPackages.PACKAGE_PERMISSION_CONTROLLER] = getKnownPackageNames(
- KnownPackages.PACKAGE_PERMISSION_CONTROLLER, UserHandle.USER_SYSTEM
- )
- this[KnownPackages.PACKAGE_VERIFIER] =
- getKnownPackageNames(KnownPackages.PACKAGE_VERIFIER, UserHandle.USER_SYSTEM)
- this[KnownPackages.PACKAGE_SETUP_WIZARD] =
- getKnownPackageNames(KnownPackages.PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM)
- this[KnownPackages.PACKAGE_SYSTEM_TEXT_CLASSIFIER] = getKnownPackageNames(
- KnownPackages.PACKAGE_SYSTEM_TEXT_CLASSIFIER, UserHandle.USER_SYSTEM
- )
- this[KnownPackages.PACKAGE_CONFIGURATOR] =
- getKnownPackageNames(KnownPackages.PACKAGE_CONFIGURATOR, UserHandle.USER_SYSTEM)
- this[KnownPackages.PACKAGE_INCIDENT_REPORT_APPROVER] = getKnownPackageNames(
- KnownPackages.PACKAGE_INCIDENT_REPORT_APPROVER, UserHandle.USER_SYSTEM
- )
- this[KnownPackages.PACKAGE_APP_PREDICTOR] =
- getKnownPackageNames(KnownPackages.PACKAGE_APP_PREDICTOR, UserHandle.USER_SYSTEM)
- this[KnownPackages.PACKAGE_COMPANION] =
- getKnownPackageNames(KnownPackages.PACKAGE_COMPANION, UserHandle.USER_SYSTEM)
- this[KnownPackages.PACKAGE_RETAIL_DEMO] =
- getKnownPackageNames(KnownPackages.PACKAGE_RETAIL_DEMO, UserHandle.USER_SYSTEM)
- .filter { isProfileOwner(it, packageStates) }.toTypedArray()
- this[KnownPackages.PACKAGE_RECENTS] =
- getKnownPackageNames(KnownPackages.PACKAGE_RECENTS, UserHandle.USER_SYSTEM)
- }
+ ): IntMap<Array<String>> =
+ MutableIntMap<Array<String>>().apply {
+ this[KnownPackages.PACKAGE_INSTALLER] =
+ getKnownPackageNames(KnownPackages.PACKAGE_INSTALLER, UserHandle.USER_SYSTEM)
+ this[KnownPackages.PACKAGE_PERMISSION_CONTROLLER] =
+ getKnownPackageNames(
+ KnownPackages.PACKAGE_PERMISSION_CONTROLLER,
+ UserHandle.USER_SYSTEM
+ )
+ this[KnownPackages.PACKAGE_VERIFIER] =
+ getKnownPackageNames(KnownPackages.PACKAGE_VERIFIER, UserHandle.USER_SYSTEM)
+ this[KnownPackages.PACKAGE_SETUP_WIZARD] =
+ getKnownPackageNames(KnownPackages.PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM)
+ this[KnownPackages.PACKAGE_SYSTEM_TEXT_CLASSIFIER] =
+ getKnownPackageNames(
+ KnownPackages.PACKAGE_SYSTEM_TEXT_CLASSIFIER,
+ UserHandle.USER_SYSTEM
+ )
+ this[KnownPackages.PACKAGE_CONFIGURATOR] =
+ getKnownPackageNames(KnownPackages.PACKAGE_CONFIGURATOR, UserHandle.USER_SYSTEM)
+ this[KnownPackages.PACKAGE_INCIDENT_REPORT_APPROVER] =
+ getKnownPackageNames(
+ KnownPackages.PACKAGE_INCIDENT_REPORT_APPROVER,
+ UserHandle.USER_SYSTEM
+ )
+ this[KnownPackages.PACKAGE_APP_PREDICTOR] =
+ getKnownPackageNames(KnownPackages.PACKAGE_APP_PREDICTOR, UserHandle.USER_SYSTEM)
+ this[KnownPackages.PACKAGE_COMPANION] =
+ getKnownPackageNames(KnownPackages.PACKAGE_COMPANION, UserHandle.USER_SYSTEM)
+ this[KnownPackages.PACKAGE_RETAIL_DEMO] =
+ getKnownPackageNames(KnownPackages.PACKAGE_RETAIL_DEMO, UserHandle.USER_SYSTEM)
+ .filter { isProfileOwner(it, packageStates) }
+ .toTypedArray()
+ this[KnownPackages.PACKAGE_RECENTS] =
+ getKnownPackageNames(KnownPackages.PACKAGE_RECENTS, UserHandle.USER_SYSTEM)
+ }
private fun isProfileOwner(
packageName: String,
diff --git a/services/permission/java/com/android/server/permission/access/AccessPersistence.kt b/services/permission/java/com/android/server/permission/access/AccessPersistence.kt
index a3f65af..d0913d2 100644
--- a/services/permission/java/com/android/server/permission/access/AccessPersistence.kt
+++ b/services/permission/java/com/android/server/permission/access/AccessPersistence.kt
@@ -38,16 +38,11 @@
import java.io.File
import java.io.FileNotFoundException
-class AccessPersistence(
- private val policy: AccessPolicy
-) {
+class AccessPersistence(private val policy: AccessPolicy) {
private val scheduleLock = Any()
- @GuardedBy("scheduleLock")
- private val pendingMutationTimesMillis = SparseLongArray()
- @GuardedBy("scheduleLock")
- private val pendingStates = MutableIntMap<AccessState>()
- @GuardedBy("scheduleLock")
- private lateinit var writeHandler: WriteHandler
+ @GuardedBy("scheduleLock") private val pendingMutationTimesMillis = SparseLongArray()
+ @GuardedBy("scheduleLock") private val pendingStates = MutableIntMap<AccessState>()
+ @GuardedBy("scheduleLock") private lateinit var writeHandler: WriteHandler
private val writeLock = Any()
@@ -60,17 +55,16 @@
*/
fun read(state: MutableAccessState) {
readSystemState(state)
- state.externalState.userIds.forEachIndexed { _, userId ->
- readUserState(state, userId)
- }
+ state.externalState.userIds.forEachIndexed { _, userId -> readUserState(state, userId) }
}
private fun readSystemState(state: MutableAccessState) {
- val fileExists = systemFile.parse {
- // This is the canonical way to call an extension function in a different class.
- // TODO(b/259469752): Use context receiver for this when it becomes stable.
- with(policy) { parseSystemState(state) }
- }
+ val fileExists =
+ systemFile.parse {
+ // This is the canonical way to call an extension function in a different class.
+ // TODO(b/259469752): Use context receiver for this when it becomes stable.
+ with(policy) { parseSystemState(state) }
+ }
if (!fileExists) {
policy.migrateSystemState(state)
@@ -79,9 +73,8 @@
}
private fun readUserState(state: MutableAccessState, userId: Int) {
- val fileExists = getUserFile(userId).parse {
- with(policy) { parseUserState(state, userId) }
- }
+ val fileExists =
+ getUserFile(userId).parse { with(policy) { parseUserState(state, userId) } }
if (!fileExists) {
policy.migrateUserState(state, userId)
@@ -90,8 +83,8 @@
}
/**
- * @return {@code true} if the file is successfully read from the disk; {@code false} if
- * the file doesn't exist yet.
+ * @return {@code true} if the file is successfully read from the disk; {@code false} if the
+ * file doesn't exist yet.
*/
private inline fun File.parse(block: BinaryXmlPullParser.() -> Unit): Boolean =
try {
@@ -106,9 +99,7 @@
fun write(state: AccessState) {
state.systemState.write(state, UserHandle.USER_ALL)
- state.userStates.forEachIndexed { _, userId, userState ->
- userState.write(state, userId)
- }
+ state.userStates.forEachIndexed { _, userId, userState -> userState.write(state, userId) }
}
private fun WritableState.write(state: AccessState, userId: Int) {
@@ -127,8 +118,10 @@
if (currentDelayMillis > MAX_WRITE_DELAY_MILLIS) {
message.sendToTarget()
} else {
- val newDelayMillis = WRITE_DELAY_TIME_MILLIS
- .coerceAtMost(MAX_WRITE_DELAY_MILLIS - currentDelayMillis)
+ val newDelayMillis =
+ WRITE_DELAY_TIME_MILLIS.coerceAtMost(
+ MAX_WRITE_DELAY_MILLIS - currentDelayMillis
+ )
writeHandler.sendMessageDelayed(message, newDelayMillis)
}
}
@@ -161,15 +154,11 @@
}
private fun writeSystemState(state: AccessState) {
- systemFile.serialize {
- with(policy) { serializeSystemState(state) }
- }
+ systemFile.serialize { with(policy) { serializeSystemState(state) } }
}
private fun writeUserState(state: AccessState, userId: Int) {
- getUserFile(userId).serialize {
- with(policy) { serializeUserState(state, userId) }
- }
+ getUserFile(userId).serialize { with(policy) { serializeUserState(state, userId) } }
}
private inline fun File.serialize(block: BinaryXmlSerializer.() -> Unit) {
diff --git a/services/permission/java/com/android/server/permission/access/AccessPolicy.kt b/services/permission/java/com/android/server/permission/access/AccessPolicy.kt
index 6a349e2..754f77ec 100644
--- a/services/permission/java/com/android/server/permission/access/AccessPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/AccessPolicy.kt
@@ -37,21 +37,24 @@
import com.android.server.pm.permission.PermissionAllowlist
import com.android.server.pm.pkg.PackageState
-class AccessPolicy private constructor(
+class AccessPolicy
+private constructor(
private val schemePolicies: IndexedMap<String, IndexedMap<String, SchemePolicy>>
) {
@Suppress("UNCHECKED_CAST")
- constructor() : this(
- MutableIndexedMap<String, MutableIndexedMap<String, SchemePolicy>>().apply {
- fun addPolicy(policy: SchemePolicy) {
- getOrPut(policy.subjectScheme) { MutableIndexedMap() }[policy.objectScheme] = policy
- }
- addPolicy(AppIdPermissionPolicy())
- addPolicy(DevicePermissionPolicy())
- addPolicy(AppIdAppOpPolicy())
- addPolicy(PackageAppOpPolicy())
- } as IndexedMap<String, IndexedMap<String, SchemePolicy>>
- )
+ constructor() :
+ this(
+ MutableIndexedMap<String, MutableIndexedMap<String, SchemePolicy>>().apply {
+ fun addPolicy(policy: SchemePolicy) {
+ getOrPut(policy.subjectScheme) { MutableIndexedMap() }[policy.objectScheme] =
+ policy
+ }
+ addPolicy(AppIdPermissionPolicy())
+ addPolicy(DevicePermissionPolicy())
+ addPolicy(AppIdAppOpPolicy())
+ addPolicy(PackageAppOpPolicy())
+ } as IndexedMap<String, IndexedMap<String, SchemePolicy>>
+ )
fun getSchemePolicy(subjectScheme: String, objectScheme: String): SchemePolicy =
checkNotNull(schemePolicies[subjectScheme]?.get(objectScheme)) {
@@ -92,23 +95,17 @@
}
fun GetStateScope.onStateMutated() {
- forEachSchemePolicy {
- with(it) { onStateMutated() }
- }
+ forEachSchemePolicy { with(it) { onStateMutated() } }
}
fun MutateStateScope.onInitialized() {
- forEachSchemePolicy {
- with(it) { onInitialized() }
- }
+ forEachSchemePolicy { with(it) { onInitialized() } }
}
fun MutateStateScope.onUserAdded(userId: Int) {
newState.mutateExternalState().mutateUserIds() += userId
newState.mutateUserStatesNoWrite()[userId] = MutableUserState()
- forEachSchemePolicy {
- with(it) { onUserAdded(userId) }
- }
+ forEachSchemePolicy { with(it) { onUserAdded(userId) } }
newState.externalState.packageStates.forEach { (_, packageState) ->
upgradePackageVersion(packageState, userId)
}
@@ -117,9 +114,7 @@
fun MutateStateScope.onUserRemoved(userId: Int) {
newState.mutateExternalState().mutateUserIds() -= userId
newState.mutateUserStatesNoWrite() -= userId
- forEachSchemePolicy {
- with(it) { onUserRemoved(userId) }
- }
+ forEachSchemePolicy { with(it) { onUserRemoved(userId) } }
}
fun MutateStateScope.onStorageVolumeMounted(
@@ -154,9 +149,7 @@
setKnownPackages(knownPackages)
}
addedAppIds.forEachIndexed { _, appId ->
- forEachSchemePolicy {
- with(it) { onAppIdAdded(appId) }
- }
+ forEachSchemePolicy { with(it) { onAppIdAdded(appId) } }
}
forEachSchemePolicy {
with(it) { onStorageVolumeMounted(volumeUuid, packageNames, isSystemUpdated) }
@@ -192,13 +185,9 @@
setKnownPackages(knownPackages)
}
if (isAppIdAdded) {
- forEachSchemePolicy {
- with(it) { onAppIdAdded(appId) }
- }
+ forEachSchemePolicy { with(it) { onAppIdAdded(appId) } }
}
- forEachSchemePolicy {
- with(it) { onPackageAdded(packageState) }
- }
+ forEachSchemePolicy { with(it) { onPackageAdded(packageState) } }
newState.userStates.forEachIndexed { _, userId, _ ->
upgradePackageVersion(packageState, userId)
}
@@ -227,13 +216,9 @@
}
setKnownPackages(knownPackages)
}
- forEachSchemePolicy {
- with(it) { onPackageRemoved(packageName, appId) }
- }
+ forEachSchemePolicy { with(it) { onPackageRemoved(packageName, appId) } }
if (isAppIdRemoved) {
- forEachSchemePolicy {
- with(it) { onAppIdRemoved(appId) }
- }
+ forEachSchemePolicy { with(it) { onAppIdRemoved(appId) } }
}
newState.userStates.forEachIndexed { userStateIndex, _, userState ->
if (packageName in userState.packageVersions) {
@@ -258,9 +243,7 @@
checkNotNull(packageState) {
"Installed package $packageName isn't found in packageStates in onPackageInstalled()"
}
- forEachSchemePolicy {
- with(it) { onPackageInstalled(packageState, userId) }
- }
+ forEachSchemePolicy { with(it) { onPackageInstalled(packageState, userId) } }
}
fun MutateStateScope.onPackageUninstalled(
@@ -276,9 +259,7 @@
setDisabledSystemPackageStates(disabledSystemPackageStates)
setKnownPackages(knownPackages)
}
- forEachSchemePolicy {
- with(it) { onPackageUninstalled(packageName, appId, userId) }
- }
+ forEachSchemePolicy { with(it) { onPackageUninstalled(packageName, appId, userId) } }
}
fun MutateStateScope.onSystemReady(
@@ -292,21 +273,15 @@
setKnownPackages(knownPackages)
setSystemReady(true)
}
- forEachSchemePolicy {
- with(it) { onSystemReady() }
- }
+ forEachSchemePolicy { with(it) { onSystemReady() } }
}
fun migrateSystemState(state: MutableAccessState) {
- forEachSchemePolicy {
- with(it) { migrateSystemState(state) }
- }
+ forEachSchemePolicy { with(it) { migrateSystemState(state) } }
}
fun migrateUserState(state: MutableAccessState, userId: Int) {
- forEachSchemePolicy {
- with(it) { migrateUserState(state, userId) }
- }
+ forEachSchemePolicy { with(it) { migrateUserState(state, userId) } }
}
private fun MutateStateScope.upgradePackageVersion(packageState: PackageState, userId: Int) {
@@ -330,10 +305,12 @@
VERSION_LATEST
}
version == VERSION_LATEST -> {}
- else -> Slog.w(
- LOG_TAG, "Unexpected version $version for package $packageName," +
- "latest version is $VERSION_LATEST"
- )
+ else ->
+ Slog.w(
+ LOG_TAG,
+ "Unexpected version $version for package $packageName," +
+ "latest version is $VERSION_LATEST"
+ )
}
}
@@ -341,11 +318,7 @@
forEachTag {
when (tagName) {
TAG_ACCESS -> {
- forEachTag {
- forEachSchemePolicy {
- with(it) { parseSystemState(state) }
- }
- }
+ forEachTag { forEachSchemePolicy { with(it) { parseSystemState(state) } } }
}
else -> Slog.w(LOG_TAG, "Ignoring unknown tag $tagName when parsing system state")
}
@@ -353,11 +326,7 @@
}
fun BinaryXmlSerializer.serializeSystemState(state: AccessState) {
- tag(TAG_ACCESS) {
- forEachSchemePolicy {
- with(it) { serializeSystemState(state) }
- }
- }
+ tag(TAG_ACCESS) { forEachSchemePolicy { with(it) { serializeSystemState(state) } } }
}
fun BinaryXmlPullParser.parseUserState(state: MutableAccessState, userId: Int) {
@@ -370,9 +339,7 @@
TAG_DEFAULT_PERMISSION_GRANT ->
parseDefaultPermissionGrant(state, userId)
else -> {
- forEachSchemePolicy {
- with(it) { parseUserState(state, userId) }
- }
+ forEachSchemePolicy { with(it) { parseUserState(state, userId) } }
}
}
}
@@ -428,9 +395,7 @@
serializeDefaultPermissionGrantFingerprint(
state.userStates[userId]!!.defaultPermissionGrantFingerprint
)
- forEachSchemePolicy {
- with(it) { serializeUserState(state, userId) }
- }
+ forEachSchemePolicy { with(it) { serializeUserState(state, userId) } }
}
}
@@ -451,9 +416,7 @@
fingerprint: String?
) {
if (fingerprint != null) {
- tag(TAG_DEFAULT_PERMISSION_GRANT) {
- attributeInterned(ATTR_FINGERPRINT, fingerprint)
- }
+ tag(TAG_DEFAULT_PERMISSION_GRANT) { attributeInterned(ATTR_FINGERPRINT, fingerprint) }
}
}
@@ -462,9 +425,7 @@
private inline fun forEachSchemePolicy(action: (SchemePolicy) -> Unit) {
schemePolicies.forEachIndexed { _, _, objectSchemePolicies ->
- objectSchemePolicies.forEachIndexed { _, _, schemePolicy ->
- action(schemePolicy)
- }
+ objectSchemePolicies.forEachIndexed { _, _, schemePolicy -> action(schemePolicy) }
}
}
diff --git a/services/permission/java/com/android/server/permission/access/AccessState.kt b/services/permission/java/com/android/server/permission/access/AccessState.kt
index 94c878a..49d2f81 100644
--- a/services/permission/java/com/android/server/permission/access/AccessState.kt
+++ b/services/permission/java/com/android/server/permission/access/AccessState.kt
@@ -28,7 +28,9 @@
private typealias SystemStateReference = MutableReference<SystemState, MutableSystemState>
typealias UserStates = IntReferenceMap<UserState, MutableUserState>
+
typealias MutableUserStates = MutableIntReferenceMap<UserState, MutableUserState>
+
private typealias UserStatesReference = MutableReference<UserStates, MutableUserStates>
sealed class AccessState(
@@ -48,22 +50,22 @@
override fun toMutable(): MutableAccessState = MutableAccessState(this)
}
-class MutableAccessState private constructor(
+class MutableAccessState
+private constructor(
externalStateReference: ExternalStateReference,
systemStateReference: SystemStateReference,
userStatesReference: UserStatesReference
-) : AccessState(
- externalStateReference,
- systemStateReference,
- userStatesReference
-) {
- constructor() : this(
- ExternalStateReference(MutableExternalState()),
- SystemStateReference(MutableSystemState()),
- UserStatesReference(MutableUserStates())
- )
+) : AccessState(externalStateReference, systemStateReference, userStatesReference) {
+ constructor() :
+ this(
+ ExternalStateReference(MutableExternalState()),
+ SystemStateReference(MutableSystemState()),
+ UserStatesReference(MutableUserStates())
+ )
- internal constructor(accessState: AccessState) : this(
+ internal constructor(
+ accessState: AccessState
+ ) : this(
accessState.externalStateReference.toImmutable(),
accessState.systemStateReference.toImmutable(),
accessState.userStatesReference.toImmutable()
@@ -86,8 +88,10 @@
private typealias UserIdsReference = MutableReference<IntSet, MutableIntSet>
typealias AppIdPackageNames = IntReferenceMap<IndexedListSet<String>, MutableIndexedListSet<String>>
+
typealias MutableAppIdPackageNames =
MutableIntReferenceMap<IndexedListSet<String>, MutableIndexedListSet<String>>
+
private typealias AppIdPackageNamesReference =
MutableReference<AppIdPackageNames, MutableAppIdPackageNames>
@@ -142,7 +146,8 @@
override fun toMutable(): MutableExternalState = MutableExternalState(this)
}
-class MutableExternalState private constructor(
+class MutableExternalState
+private constructor(
userIdsReference: UserIdsReference,
packageStates: Map<String, PackageState>,
disabledSystemPackageStates: Map<String, PackageState>,
@@ -154,34 +159,38 @@
permissionAllowlist: PermissionAllowlist,
implicitToSourcePermissions: IndexedMap<String, IndexedListSet<String>>,
isSystemReady: Boolean
-) : ExternalState(
- userIdsReference,
- packageStates,
- disabledSystemPackageStates,
- appIdPackageNamesReference,
- knownPackages,
- isLeanback,
- configPermissions,
- privilegedPermissionAllowlistPackages,
- permissionAllowlist,
- implicitToSourcePermissions,
- isSystemReady
-) {
- constructor() : this(
- UserIdsReference(MutableIntSet()),
- emptyMap(),
- emptyMap(),
- AppIdPackageNamesReference(MutableAppIdPackageNames()),
- MutableIntMap(),
- false,
- emptyMap(),
- MutableIndexedListSet(),
- PermissionAllowlist(),
- MutableIndexedMap(),
- false
- )
+) :
+ ExternalState(
+ userIdsReference,
+ packageStates,
+ disabledSystemPackageStates,
+ appIdPackageNamesReference,
+ knownPackages,
+ isLeanback,
+ configPermissions,
+ privilegedPermissionAllowlistPackages,
+ permissionAllowlist,
+ implicitToSourcePermissions,
+ isSystemReady
+ ) {
+ constructor() :
+ this(
+ UserIdsReference(MutableIntSet()),
+ emptyMap(),
+ emptyMap(),
+ AppIdPackageNamesReference(MutableAppIdPackageNames()),
+ MutableIntMap(),
+ false,
+ emptyMap(),
+ MutableIndexedListSet(),
+ PermissionAllowlist(),
+ MutableIndexedMap(),
+ false
+ )
- internal constructor(externalState: ExternalState) : this(
+ internal constructor(
+ externalState: ExternalState
+ ) : this(
externalState.userIdsReference.toImmutable(),
externalState.packageStates,
externalState.disabledSystemPackageStates,
@@ -249,9 +258,10 @@
}
}
-private typealias PermissionGroupsReference = MutableReference<
- IndexedMap<String, PermissionGroupInfo>, MutableIndexedMap<String, PermissionGroupInfo>
->
+private typealias PermissionGroupsReference =
+ MutableReference<
+ IndexedMap<String, PermissionGroupInfo>, MutableIndexedMap<String, PermissionGroupInfo>
+ >
private typealias PermissionTreesReference =
MutableReference<IndexedMap<String, Permission>, MutableIndexedMap<String, Permission>>
@@ -280,25 +290,31 @@
override fun toMutable(): MutableSystemState = MutableSystemState(this)
}
-class MutableSystemState private constructor(
+class MutableSystemState
+private constructor(
permissionGroupsReference: PermissionGroupsReference,
permissionTreesReference: PermissionTreesReference,
permissionsReference: PermissionsReference,
writeMode: Int
-) : SystemState(
- permissionGroupsReference,
- permissionTreesReference,
- permissionsReference,
- writeMode
-), MutableWritableState {
- constructor() : this(
- PermissionGroupsReference(MutableIndexedMap()),
- PermissionTreesReference(MutableIndexedMap()),
- PermissionsReference(MutableIndexedMap()),
- WriteMode.NONE
- )
+) :
+ SystemState(
+ permissionGroupsReference,
+ permissionTreesReference,
+ permissionsReference,
+ writeMode
+ ),
+ MutableWritableState {
+ constructor() :
+ this(
+ PermissionGroupsReference(MutableIndexedMap()),
+ PermissionTreesReference(MutableIndexedMap()),
+ PermissionsReference(MutableIndexedMap()),
+ WriteMode.NONE
+ )
- internal constructor(systemState: SystemState) : this(
+ internal constructor(
+ systemState: SystemState
+ ) : this(
systemState.permissionGroupsReference.toImmutable(),
systemState.permissionTreesReference.toImmutable(),
systemState.permissionsReference.toImmutable(),
@@ -311,8 +327,7 @@
fun mutatePermissionTrees(): MutableIndexedMap<String, Permission> =
permissionTreesReference.mutate()
- fun mutatePermissions(): MutableIndexedMap<String, Permission> =
- permissionsReference.mutate()
+ fun mutatePermissions(): MutableIndexedMap<String, Permission> = permissionsReference.mutate()
override fun requestWriteMode(writeMode: Int) {
this.writeMode = maxOf(this.writeMode, writeMode)
@@ -324,34 +339,42 @@
typealias AppIdPermissionFlags =
IntReferenceMap<IndexedMap<String, Int>, MutableIndexedMap<String, Int>>
+
typealias MutableAppIdPermissionFlags =
MutableIntReferenceMap<IndexedMap<String, Int>, MutableIndexedMap<String, Int>>
+
private typealias AppIdPermissionFlagsReference =
MutableReference<AppIdPermissionFlags, MutableAppIdPermissionFlags>
-
typealias DevicePermissionFlags =
IndexedReferenceMap<String, IndexedMap<String, Int>, MutableIndexedMap<String, Int>>
+
typealias MutableDevicePermissionFlags =
MutableIndexedReferenceMap<String, IndexedMap<String, Int>, MutableIndexedMap<String, Int>>
+
typealias AppIdDevicePermissionFlags =
IntReferenceMap<DevicePermissionFlags, MutableDevicePermissionFlags>
+
typealias MutableAppIdDevicePermissionFlags =
MutableIntReferenceMap<DevicePermissionFlags, MutableDevicePermissionFlags>
+
private typealias AppIdDevicePermissionFlagsReference =
MutableReference<AppIdDevicePermissionFlags, MutableAppIdDevicePermissionFlags>
-typealias AppIdAppOpModes =
- IntReferenceMap<IndexedMap<String, Int>, MutableIndexedMap<String, Int>>
+typealias AppIdAppOpModes = IntReferenceMap<IndexedMap<String, Int>, MutableIndexedMap<String, Int>>
+
typealias MutableAppIdAppOpModes =
MutableIntReferenceMap<IndexedMap<String, Int>, MutableIndexedMap<String, Int>>
+
private typealias AppIdAppOpModesReference =
MutableReference<AppIdAppOpModes, MutableAppIdAppOpModes>
typealias PackageAppOpModes =
IndexedReferenceMap<String, IndexedMap<String, Int>, MutableIndexedMap<String, Int>>
+
typealias MutablePackageAppOpModes =
MutableIndexedReferenceMap<String, IndexedMap<String, Int>, MutableIndexedMap<String, Int>>
+
private typealias PackageAppOpModesReference =
MutableReference<PackageAppOpModes, MutablePackageAppOpModes>
@@ -388,7 +411,8 @@
override fun toMutable(): MutableUserState = MutableUserState(this)
}
-class MutableUserState private constructor(
+class MutableUserState
+private constructor(
packageVersionsReference: PackageVersionsReference,
appIdPermissionFlagsReference: AppIdPermissionFlagsReference,
appIdDevicePermissionFlagsReference: AppIdDevicePermissionFlagsReference,
@@ -396,26 +420,31 @@
packageAppOpModesReference: PackageAppOpModesReference,
defaultPermissionGrantFingerprint: String?,
writeMode: Int
-) : UserState(
- packageVersionsReference,
- appIdPermissionFlagsReference,
- appIdDevicePermissionFlagsReference,
- appIdAppOpModesReference,
- packageAppOpModesReference,
- defaultPermissionGrantFingerprint,
- writeMode
-), MutableWritableState {
- constructor() : this(
- PackageVersionsReference(MutableIndexedMap<String, Int>()),
- AppIdPermissionFlagsReference(MutableAppIdPermissionFlags()),
- AppIdDevicePermissionFlagsReference(MutableAppIdDevicePermissionFlags()),
- AppIdAppOpModesReference(MutableAppIdAppOpModes()),
- PackageAppOpModesReference(MutablePackageAppOpModes()),
- null,
- WriteMode.NONE
- )
+) :
+ UserState(
+ packageVersionsReference,
+ appIdPermissionFlagsReference,
+ appIdDevicePermissionFlagsReference,
+ appIdAppOpModesReference,
+ packageAppOpModesReference,
+ defaultPermissionGrantFingerprint,
+ writeMode
+ ),
+ MutableWritableState {
+ constructor() :
+ this(
+ PackageVersionsReference(MutableIndexedMap<String, Int>()),
+ AppIdPermissionFlagsReference(MutableAppIdPermissionFlags()),
+ AppIdDevicePermissionFlagsReference(MutableAppIdDevicePermissionFlags()),
+ AppIdAppOpModesReference(MutableAppIdAppOpModes()),
+ PackageAppOpModesReference(MutablePackageAppOpModes()),
+ null,
+ WriteMode.NONE
+ )
- internal constructor(userState: UserState) : this(
+ internal constructor(
+ userState: UserState
+ ) : this(
userState.packageVersionsReference.toImmutable(),
userState.appIdPermissionFlagsReference.toImmutable(),
userState.appIdDevicePermissionFlagsReference.toImmutable(),
@@ -461,11 +490,7 @@
fun requestWriteMode(writeMode: Int)
}
-open class GetStateScope(
- val state: AccessState
-)
+open class GetStateScope(val state: AccessState)
-class MutateStateScope(
- val oldState: AccessState,
- val newState: MutableAccessState
-) : GetStateScope(newState)
+class MutateStateScope(val oldState: AccessState, val newState: MutableAccessState) :
+ GetStateScope(newState)
diff --git a/services/permission/java/com/android/server/permission/access/AccessUri.kt b/services/permission/java/com/android/server/permission/access/AccessUri.kt
index 1d46ca7..1f5a4df 100644
--- a/services/permission/java/com/android/server/permission/access/AccessUri.kt
+++ b/services/permission/java/com/android/server/permission/access/AccessUri.kt
@@ -18,9 +18,7 @@
import android.os.UserHandle
-sealed class AccessUri(
- val scheme: String
-) {
+sealed class AccessUri(val scheme: String) {
override fun equals(other: Any?): Boolean {
throw NotImplementedError()
}
@@ -34,9 +32,7 @@
}
}
-data class AppOpUri(
- val appOpName: String
-) : AccessUri(SCHEME) {
+data class AppOpUri(val appOpName: String) : AccessUri(SCHEME) {
override fun toString(): String = "$scheme:///$appOpName"
companion object {
@@ -44,10 +40,7 @@
}
}
-data class PackageUri(
- val packageName: String,
- val userId: Int
-) : AccessUri(SCHEME) {
+data class PackageUri(val packageName: String, val userId: Int) : AccessUri(SCHEME) {
override fun toString(): String = "$scheme:///$packageName/$userId"
companion object {
@@ -55,9 +48,7 @@
}
}
-data class PermissionUri(
- val permissionName: String
-) : AccessUri(SCHEME) {
+data class PermissionUri(val permissionName: String) : AccessUri(SCHEME) {
override fun toString(): String = "$scheme:///$permissionName"
companion object {
@@ -65,10 +56,7 @@
}
}
-data class DevicePermissionUri(
- val permissionName: String,
- val deviceId: Int
-) : AccessUri(SCHEME) {
+data class DevicePermissionUri(val permissionName: String, val deviceId: Int) : AccessUri(SCHEME) {
override fun toString(): String = "$scheme:///$permissionName/$deviceId"
companion object {
@@ -76,9 +64,7 @@
}
}
-data class UidUri(
- val uid: Int
-) : AccessUri(SCHEME) {
+data class UidUri(val uid: Int) : AccessUri(SCHEME) {
val userId: Int
get() = UserHandle.getUserId(uid)
diff --git a/services/permission/java/com/android/server/permission/access/appop/AppIdAppOpMigration.kt b/services/permission/java/com/android/server/permission/access/appop/AppIdAppOpMigration.kt
index 96d315e..6c1b080 100644
--- a/services/permission/java/com/android/server/permission/access/appop/AppIdAppOpMigration.kt
+++ b/services/permission/java/com/android/server/permission/access/appop/AppIdAppOpMigration.kt
@@ -46,9 +46,7 @@
val appOpModes = MutableIndexedMap<String, Int>()
appIdAppOpModes[appId] = appOpModes
- legacyAppOpModes.forEach { (appOpName, appOpMode) ->
- appOpModes[appOpName] = appOpMode
- }
+ legacyAppOpModes.forEach { (appOpName, appOpMode) -> appOpModes[appOpName] = appOpMode }
if (packageNames != null) {
val packageVersions = userState.mutatePackageVersions()
diff --git a/services/permission/java/com/android/server/permission/access/appop/AppIdAppOpPersistence.kt b/services/permission/java/com/android/server/permission/access/appop/AppIdAppOpPersistence.kt
index 4c7e946..f291b1a 100644
--- a/services/permission/java/com/android/server/permission/access/appop/AppIdAppOpPersistence.kt
+++ b/services/permission/java/com/android/server/permission/access/appop/AppIdAppOpPersistence.kt
@@ -51,8 +51,10 @@
}
userState.appIdAppOpModes.forEachReversedIndexed { appIdIndex, appId, _ ->
// Non-application UIDs may not have an Android package but may still have app op state.
- if (appId !in state.externalState.appIdPackageNames &&
- appId >= Process.FIRST_APPLICATION_UID) {
+ if (
+ appId !in state.externalState.appIdPackageNames &&
+ appId >= Process.FIRST_APPLICATION_UID
+ ) {
Slog.w(LOG_TAG, "Dropping unknown app ID $appId when parsing app-op state")
appIdAppOpModes.removeAt(appIdIndex)
userState.requestWriteMode(WriteMode.ASYNCHRONOUS)
diff --git a/services/permission/java/com/android/server/permission/access/appop/AppIdAppOpPolicy.kt b/services/permission/java/com/android/server/permission/access/appop/AppIdAppOpPolicy.kt
index c02fe4d..94caf28 100644
--- a/services/permission/java/com/android/server/permission/access/appop/AppIdAppOpPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/appop/AppIdAppOpPolicy.kt
@@ -46,7 +46,9 @@
newState.userStates.forEachIndexed { userStateIndex, _, userState ->
val appIdIndex = userState.appIdAppOpModes.indexOfKey(appId)
if (appIdIndex >= 0) {
- newState.mutateUserStateAt(userStateIndex).mutateAppIdAppOpModes()
+ newState
+ .mutateUserStateAt(userStateIndex)
+ .mutateAppIdAppOpModes()
.removeAt(appIdIndex)
// Skip notifying the change listeners since the app ID no longer exists.
}
@@ -61,8 +63,8 @@
if (userStateIndex < 0) {
return false
}
- val appIdIndex = newState.userStates.valueAt(userStateIndex).appIdAppOpModes
- .indexOfKey(appId)
+ val appIdIndex =
+ newState.userStates.valueAt(userStateIndex).appIdAppOpModes.indexOfKey(appId)
if (appIdIndex < 0) {
return false
}
@@ -71,7 +73,9 @@
}
fun GetStateScope.getAppOpMode(appId: Int, userId: Int, appOpName: String): Int =
- state.userStates[userId]?.appIdAppOpModes?.get(appId)
+ state.userStates[userId]
+ ?.appIdAppOpModes
+ ?.get(appId)
.getWithDefault(appOpName, AppOpsManager.opToDefaultMode(appOpName))
fun MutateStateScope.setAppOpMode(
@@ -81,8 +85,10 @@
mode: Int
): Boolean {
val defaultMode = AppOpsManager.opToDefaultMode(appOpName)
- val oldMode = newState.userStates[userId]!!.appIdAppOpModes[appId]
- .getWithDefault(appOpName, defaultMode)
+ val oldMode =
+ newState.userStates[userId]!!
+ .appIdAppOpModes[appId]
+ .getWithDefault(appOpName, defaultMode)
if (oldMode == mode) {
return false
}
@@ -122,9 +128,7 @@
with(upgrade) { upgradePackageState(packageState, userId, version) }
}
- /**
- * Listener for app op mode changes.
- */
+ /** Listener for app op mode changes. */
abstract class OnAppOpModeChangedListener {
/**
* Called when an app op mode change has been made to the upcoming new state.
diff --git a/services/permission/java/com/android/server/permission/access/appop/AppIdAppOpUpgrade.kt b/services/permission/java/com/android/server/permission/access/appop/AppIdAppOpUpgrade.kt
index 12df95e..10c7764 100644
--- a/services/permission/java/com/android/server/permission/access/appop/AppIdAppOpUpgrade.kt
+++ b/services/permission/java/com/android/server/permission/access/appop/AppIdAppOpUpgrade.kt
@@ -28,11 +28,13 @@
) {
if (version <= 2) {
with(policy) {
- val appOpMode = getAppOpMode(
- packageState.appId, userId, AppOpsManager.OPSTR_RUN_IN_BACKGROUND
- )
+ val appOpMode =
+ getAppOpMode(packageState.appId, userId, AppOpsManager.OPSTR_RUN_IN_BACKGROUND)
setAppOpMode(
- packageState.appId, userId, AppOpsManager.OPSTR_RUN_ANY_IN_BACKGROUND, appOpMode
+ packageState.appId,
+ userId,
+ AppOpsManager.OPSTR_RUN_ANY_IN_BACKGROUND,
+ appOpMode
)
}
}
@@ -40,14 +42,19 @@
val permissionName = AppOpsManager.opToPermission(AppOpsManager.OP_SCHEDULE_EXACT_ALARM)
if (permissionName in packageState.androidPackage!!.requestedPermissions) {
with(policy) {
- val appOpMode = getAppOpMode(
- packageState.appId, userId, AppOpsManager.OPSTR_SCHEDULE_EXACT_ALARM
- )
+ val appOpMode =
+ getAppOpMode(
+ packageState.appId,
+ userId,
+ AppOpsManager.OPSTR_SCHEDULE_EXACT_ALARM
+ )
val defaultAppOpMode =
AppOpsManager.opToDefaultMode(AppOpsManager.OP_SCHEDULE_EXACT_ALARM)
if (appOpMode == defaultAppOpMode) {
setAppOpMode(
- packageState.appId, userId, AppOpsManager.OPSTR_SCHEDULE_EXACT_ALARM,
+ packageState.appId,
+ userId,
+ AppOpsManager.OPSTR_SCHEDULE_EXACT_ALARM,
AppOpsManager.MODE_ALLOWED
)
}
diff --git a/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt b/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
index 5b91ad9..26ea9d2 100644
--- a/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
+++ b/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
@@ -33,19 +33,16 @@
import com.android.server.permission.access.collection.forEachIndexed
import com.android.server.permission.access.collection.set
-class AppOpService(
- private val service: AccessCheckingService
-) : AppOpsCheckingServiceInterface {
- private val packagePolicy = service.getSchemePolicy(PackageUri.SCHEME, AppOpUri.SCHEME)
- as PackageAppOpPolicy
- private val appIdPolicy = service.getSchemePolicy(UidUri.SCHEME, AppOpUri.SCHEME)
- as AppIdAppOpPolicy
+class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingServiceInterface {
+ private val packagePolicy =
+ service.getSchemePolicy(PackageUri.SCHEME, AppOpUri.SCHEME) as PackageAppOpPolicy
+ private val appIdPolicy =
+ service.getSchemePolicy(UidUri.SCHEME, AppOpUri.SCHEME) as AppIdAppOpPolicy
private val context = service.context
private lateinit var handler: Handler
- @Volatile
- private var listeners = ArraySet<AppOpsModeChangedListener>()
+ @Volatile private var listeners = ArraySet<AppOpsModeChangedListener>()
private val listenersLock = Any()
fun initialize() {
@@ -86,9 +83,7 @@
val appId = UserHandle.getAppId(uid)
val userId = UserHandle.getUserId(uid)
val opName = AppOpsManager.opToPublicName(op)
- return service.getState {
- with(appIdPolicy) { getAppOpMode(appId, userId, opName) }
- }
+ return service.getState { with(appIdPolicy) { getAppOpMode(appId, userId, opName) } }
}
private fun getUidModes(uid: Int): ArrayMap<String, Int>? {
@@ -115,10 +110,7 @@
}
}
- private fun getPackageModes(
- packageName: String,
- userId: Int
- ): ArrayMap<String, Int>? =
+ private fun getPackageModes(packageName: String, userId: Int): ArrayMap<String, Int>? =
service.getState { with(packagePolicy) { getAppOpModes(packageName, userId) } }?.map
override fun setPackageMode(packageName: String, op: Int, mode: Int, userId: Int) {
@@ -131,15 +123,13 @@
override fun removeUid(uid: Int) {
val appId = UserHandle.getAppId(uid)
val userId = UserHandle.getUserId(uid)
- service.mutateState {
- with(appIdPolicy) { removeAppOpModes(appId, userId) }
- }
+ service.mutateState { with(appIdPolicy) { removeAppOpModes(appId, userId) } }
}
override fun removePackage(packageName: String, userId: Int): Boolean {
var wasChanged = false
service.mutateState {
- wasChanged = with (packagePolicy) { removeAppOpModes(packageName, userId) }
+ wasChanged = with(packagePolicy) { removeAppOpModes(packageName, userId) }
}
return wasChanged
}
diff --git a/services/permission/java/com/android/server/permission/access/appop/BaseAppOpPersistence.kt b/services/permission/java/com/android/server/permission/access/appop/BaseAppOpPersistence.kt
index a267637..edeef71 100644
--- a/services/permission/java/com/android/server/permission/access/appop/BaseAppOpPersistence.kt
+++ b/services/permission/java/com/android/server/permission/access/appop/BaseAppOpPersistence.kt
@@ -52,9 +52,7 @@
}
protected fun BinaryXmlSerializer.serializeAppOps(appOpModes: IndexedMap<String, Int>) {
- appOpModes.forEachIndexed { _, name, mode ->
- serializeAppOp(name, mode)
- }
+ appOpModes.forEachIndexed { _, name, mode -> serializeAppOp(name, mode) }
}
private fun BinaryXmlSerializer.serializeAppOp(name: String, mode: Int) {
diff --git a/services/permission/java/com/android/server/permission/access/appop/BaseAppOpPolicy.kt b/services/permission/java/com/android/server/permission/access/appop/BaseAppOpPolicy.kt
index c0a85f8..758cec0 100644
--- a/services/permission/java/com/android/server/permission/access/appop/BaseAppOpPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/appop/BaseAppOpPolicy.kt
@@ -23,9 +23,7 @@
import com.android.server.permission.access.MutableAccessState
import com.android.server.permission.access.SchemePolicy
-abstract class BaseAppOpPolicy(
- private val persistence: BaseAppOpPersistence
-) : SchemePolicy() {
+abstract class BaseAppOpPolicy(private val persistence: BaseAppOpPersistence) : SchemePolicy() {
override val objectScheme: String
get() = AppOpUri.SCHEME
diff --git a/services/permission/java/com/android/server/permission/access/appop/PackageAppOpMigration.kt b/services/permission/java/com/android/server/permission/access/appop/PackageAppOpMigration.kt
index 03311a2..8797e39 100644
--- a/services/permission/java/com/android/server/permission/access/appop/PackageAppOpMigration.kt
+++ b/services/permission/java/com/android/server/permission/access/appop/PackageAppOpMigration.kt
@@ -44,9 +44,7 @@
val appOpModes = MutableIndexedMap<String, Int>()
packageAppOpModes[packageName] = appOpModes
- legacyAppOpModes.forEach { (appOpName, appOpMode) ->
- appOpModes[appOpName] = appOpMode
- }
+ legacyAppOpModes.forEach { (appOpName, appOpMode) -> appOpModes[appOpName] = appOpMode }
userState.mutatePackageVersions()[packageName] = version
}
diff --git a/services/permission/java/com/android/server/permission/access/appop/PackageAppOpPolicy.kt b/services/permission/java/com/android/server/permission/access/appop/PackageAppOpPolicy.kt
index 5398a57..0d9470e 100644
--- a/services/permission/java/com/android/server/permission/access/appop/PackageAppOpPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/appop/PackageAppOpPolicy.kt
@@ -46,7 +46,9 @@
newState.userStates.forEachIndexed { userStateIndex, _, userState ->
val packageNameIndex = userState.packageAppOpModes.indexOfKey(packageName)
if (packageNameIndex >= 0) {
- newState.mutateUserStateAt(userStateIndex).mutatePackageAppOpModes()
+ newState
+ .mutateUserStateAt(userStateIndex)
+ .mutatePackageAppOpModes()
.removeAt(packageNameIndex)
// Skip notifying the change listeners since the package no longer exists.
}
@@ -61,18 +63,22 @@
if (userStateIndex < 0) {
return false
}
- val packageNameIndex = newState.userStates.valueAt(userStateIndex).packageAppOpModes
- .indexOfKey(packageName)
+ val packageNameIndex =
+ newState.userStates.valueAt(userStateIndex).packageAppOpModes.indexOfKey(packageName)
if (packageNameIndex < 0) {
return false
}
- newState.mutateUserStateAt(userStateIndex).mutatePackageAppOpModes()
+ newState
+ .mutateUserStateAt(userStateIndex)
+ .mutatePackageAppOpModes()
.removeAt(packageNameIndex)
return true
}
fun GetStateScope.getAppOpMode(packageName: String, userId: Int, appOpName: String): Int =
- state.userStates[userId]?.packageAppOpModes?.get(packageName)
+ state.userStates[userId]
+ ?.packageAppOpModes
+ ?.get(packageName)
.getWithDefault(appOpName, AppOpsManager.opToDefaultMode(appOpName))
fun MutateStateScope.setAppOpMode(
@@ -82,8 +88,10 @@
mode: Int
): Boolean {
val defaultMode = AppOpsManager.opToDefaultMode(appOpName)
- val oldMode = newState.userStates[userId]!!.packageAppOpModes[packageName]
- .getWithDefault(appOpName, defaultMode)
+ val oldMode =
+ newState.userStates[userId]!!
+ .packageAppOpModes[packageName]
+ .getWithDefault(appOpName, defaultMode)
if (oldMode == mode) {
return false
}
@@ -123,9 +131,7 @@
with(upgrade) { upgradePackageState(packageState, userId, version) }
}
- /**
- * Listener for app op mode changes.
- */
+ /** Listener for app op mode changes. */
abstract class OnAppOpModeChangedListener {
/**
* Called when an app op mode change has been made to the upcoming new state.
diff --git a/services/permission/java/com/android/server/permission/access/appop/PackageAppOpUpgrade.kt b/services/permission/java/com/android/server/permission/access/appop/PackageAppOpUpgrade.kt
index 8e37093..f5eedf7 100644
--- a/services/permission/java/com/android/server/permission/access/appop/PackageAppOpUpgrade.kt
+++ b/services/permission/java/com/android/server/permission/access/appop/PackageAppOpUpgrade.kt
@@ -28,11 +28,16 @@
) {
if (version <= 2) {
with(policy) {
- val appOpMode = getAppOpMode(
- packageState.packageName, userId, AppOpsManager.OPSTR_RUN_IN_BACKGROUND
- )
+ val appOpMode =
+ getAppOpMode(
+ packageState.packageName,
+ userId,
+ AppOpsManager.OPSTR_RUN_IN_BACKGROUND
+ )
setAppOpMode(
- packageState.packageName, userId, AppOpsManager.OPSTR_RUN_ANY_IN_BACKGROUND,
+ packageState.packageName,
+ userId,
+ AppOpsManager.OPSTR_RUN_ANY_IN_BACKGROUND,
appOpMode
)
}
diff --git a/services/permission/java/com/android/server/permission/access/collection/ArrayMapExtensions.kt b/services/permission/java/com/android/server/permission/access/collection/ArrayMapExtensions.kt
index 686db42..b74f477 100644
--- a/services/permission/java/com/android/server/permission/access/collection/ArrayMapExtensions.kt
+++ b/services/permission/java/com/android/server/permission/access/collection/ArrayMapExtensions.kt
@@ -49,7 +49,9 @@
}
inline fun <K, V> ArrayMap<K, V>.getOrPut(key: K, defaultValue: () -> V): V {
- get(key)?.let { return it }
+ get(key)?.let {
+ return it
+ }
return defaultValue().also { put(key, it) }
}
diff --git a/services/permission/java/com/android/server/permission/access/immutable/IndexedList.kt b/services/permission/java/com/android/server/permission/access/immutable/IndexedList.kt
index ce4aa44..ea8e07f 100644
--- a/services/permission/java/com/android/server/permission/access/immutable/IndexedList.kt
+++ b/services/permission/java/com/android/server/permission/access/immutable/IndexedList.kt
@@ -16,12 +16,8 @@
package com.android.server.permission.access.immutable
-/**
- * Immutable list with index-based access.
- */
-sealed class IndexedList<T>(
- internal val list: ArrayList<T>
-) : Immutable<MutableIndexedList<T>> {
+/** Immutable list with index-based access. */
+sealed class IndexedList<T>(internal val list: ArrayList<T>) : Immutable<MutableIndexedList<T>> {
val size: Int
get() = list.size
@@ -29,20 +25,15 @@
operator fun contains(element: T): Boolean = list.contains(element)
- @Suppress("ReplaceGetOrSet")
- operator fun get(index: Int): T = list.get(index)
+ @Suppress("ReplaceGetOrSet") operator fun get(index: Int): T = list.get(index)
override fun toMutable(): MutableIndexedList<T> = MutableIndexedList(this)
override fun toString(): String = list.toString()
}
-/**
- * Mutable list with index-based access.
- */
-class MutableIndexedList<T>(
- list: ArrayList<T> = ArrayList()
-) : IndexedList<T>(list) {
+/** Mutable list with index-based access. */
+class MutableIndexedList<T>(list: ArrayList<T> = ArrayList()) : IndexedList<T>(list) {
constructor(indexedList: IndexedList<T>) : this(ArrayList(indexedList.list))
@Suppress("ReplaceGetOrSet")
diff --git a/services/permission/java/com/android/server/permission/access/immutable/IndexedListExtensions.kt b/services/permission/java/com/android/server/permission/access/immutable/IndexedListExtensions.kt
index dc9bae3..a9d804e 100644
--- a/services/permission/java/com/android/server/permission/access/immutable/IndexedListExtensions.kt
+++ b/services/permission/java/com/android/server/permission/access/immutable/IndexedListExtensions.kt
@@ -70,9 +70,7 @@
accumulator: (Int, Int, T) -> Int
): Int {
var value = initialValue
- forEachIndexed { index, element ->
- value = accumulator(value, index, element)
- }
+ forEachIndexed { index, element -> value = accumulator(value, index, element) }
return value
}
diff --git a/services/permission/java/com/android/server/permission/access/immutable/IndexedListSet.kt b/services/permission/java/com/android/server/permission/access/immutable/IndexedListSet.kt
index 77e71ba..3a2fd2f 100644
--- a/services/permission/java/com/android/server/permission/access/immutable/IndexedListSet.kt
+++ b/services/permission/java/com/android/server/permission/access/immutable/IndexedListSet.kt
@@ -16,12 +16,9 @@
package com.android.server.permission.access.immutable
-/**
- * Immutable set with index-based access, implemented using a list.
- */
-sealed class IndexedListSet<T>(
- internal val list: ArrayList<T>
-) : Immutable<MutableIndexedListSet<T>> {
+/** Immutable set with index-based access, implemented using a list. */
+sealed class IndexedListSet<T>(internal val list: ArrayList<T>) :
+ Immutable<MutableIndexedListSet<T>> {
val size: Int
get() = list.size
@@ -31,20 +28,15 @@
fun indexOf(element: T): Int = list.indexOf(element)
- @Suppress("ReplaceGetOrSet")
- fun elementAt(index: Int): T = list.get(index)
+ @Suppress("ReplaceGetOrSet") fun elementAt(index: Int): T = list.get(index)
override fun toMutable(): MutableIndexedListSet<T> = MutableIndexedListSet(this)
override fun toString(): String = list.toString()
}
-/**
- * Mutable set with index-based access, implemented using a list.
- */
-class MutableIndexedListSet<T>(
- list: ArrayList<T> = ArrayList()
-) : IndexedListSet<T>(list) {
+/** Mutable set with index-based access, implemented using a list. */
+class MutableIndexedListSet<T>(list: ArrayList<T> = ArrayList()) : IndexedListSet<T>(list) {
constructor(indexedListSet: IndexedListSet<T>) : this(ArrayList(indexedListSet.list))
fun add(element: T): Boolean =
diff --git a/services/permission/java/com/android/server/permission/access/immutable/IndexedListSetExtensions.kt b/services/permission/java/com/android/server/permission/access/immutable/IndexedListSetExtensions.kt
index 13fc141..2634b53 100644
--- a/services/permission/java/com/android/server/permission/access/immutable/IndexedListSetExtensions.kt
+++ b/services/permission/java/com/android/server/permission/access/immutable/IndexedListSetExtensions.kt
@@ -70,9 +70,7 @@
accumulator: (Int, Int, T) -> Int
): Int {
var value = initialValue
- forEachIndexed { index, element ->
- value = accumulator(value, index, element)
- }
+ forEachIndexed { index, element -> value = accumulator(value, index, element) }
return value
}
diff --git a/services/permission/java/com/android/server/permission/access/immutable/IndexedMap.kt b/services/permission/java/com/android/server/permission/access/immutable/IndexedMap.kt
index 299cc89..873c9c8 100644
--- a/services/permission/java/com/android/server/permission/access/immutable/IndexedMap.kt
+++ b/services/permission/java/com/android/server/permission/access/immutable/IndexedMap.kt
@@ -18,12 +18,9 @@
import android.util.ArrayMap
-/**
- * Immutable map with index-based access.
- */
-sealed class IndexedMap<K, V>(
- internal val map: ArrayMap<K, V>
-) : Immutable<MutableIndexedMap<K, V>> {
+/** Immutable map with index-based access. */
+sealed class IndexedMap<K, V>(internal val map: ArrayMap<K, V>) :
+ Immutable<MutableIndexedMap<K, V>> {
val size: Int
get() = map.size
@@ -31,8 +28,7 @@
operator fun contains(key: K): Boolean = map.containsKey(key)
- @Suppress("ReplaceGetOrSet")
- operator fun get(key: K): V? = map.get(key)
+ @Suppress("ReplaceGetOrSet") operator fun get(key: K): V? = map.get(key)
fun indexOfKey(key: K): Int = map.indexOfKey(key)
@@ -45,12 +41,8 @@
override fun toString(): String = map.toString()
}
-/**
- * Mutable map with index-based access.
- */
-class MutableIndexedMap<K, V>(
- map: ArrayMap<K, V> = ArrayMap()
-) : IndexedMap<K, V>(map) {
+/** Mutable map with index-based access. */
+class MutableIndexedMap<K, V>(map: ArrayMap<K, V> = ArrayMap()) : IndexedMap<K, V>(map) {
constructor(indexedMap: IndexedMap<K, V>) : this(ArrayMap(indexedMap.map))
fun put(key: K, value: V): V? = map.put(key, value)
diff --git a/services/permission/java/com/android/server/permission/access/immutable/IndexedMapExtensions.kt b/services/permission/java/com/android/server/permission/access/immutable/IndexedMapExtensions.kt
index 69f1779c..48637cc 100644
--- a/services/permission/java/com/android/server/permission/access/immutable/IndexedMapExtensions.kt
+++ b/services/permission/java/com/android/server/permission/access/immutable/IndexedMapExtensions.kt
@@ -36,7 +36,9 @@
inline fun <K, V, R> IndexedMap<K, V>.firstNotNullOfOrNullIndexed(transform: (Int, K, V) -> R): R? {
forEachIndexed { index, key, value ->
- transform(index, key, value)?.let { return it }
+ transform(index, key, value)?.let {
+ return it
+ }
}
return null
}
@@ -75,9 +77,7 @@
destination: C,
transform: (Int, K, V) -> R,
): C {
- forEachIndexed { index, key, value ->
- transform(index, key, value).let { destination += it }
- }
+ forEachIndexed { index, key, value -> transform(index, key, value).let { destination += it } }
return destination
}
@@ -85,14 +85,14 @@
destination: C,
transform: (Int, K, V) -> R?
): C {
- forEachIndexed { index, key, value ->
- transform(index, key, value)?.let { destination += it }
- }
+ forEachIndexed { index, key, value -> transform(index, key, value)?.let { destination += it } }
return destination
}
inline fun <K, V> MutableIndexedMap<K, V>.getOrPut(key: K, defaultValue: () -> V): V {
- get(key)?.let { return it }
+ get(key)?.let {
+ return it
+ }
return defaultValue().also { put(key, it) }
}
diff --git a/services/permission/java/com/android/server/permission/access/immutable/IndexedReferenceMap.kt b/services/permission/java/com/android/server/permission/access/immutable/IndexedReferenceMap.kt
index ff76a47..6fe4718 100644
--- a/services/permission/java/com/android/server/permission/access/immutable/IndexedReferenceMap.kt
+++ b/services/permission/java/com/android/server/permission/access/immutable/IndexedReferenceMap.kt
@@ -33,8 +33,7 @@
operator fun contains(key: K): Boolean = map.containsKey(key)
- @Suppress("ReplaceGetOrSet")
- operator fun get(key: K): I? = map.get(key)?.get()
+ @Suppress("ReplaceGetOrSet") operator fun get(key: K): I? = map.get(key)?.get()
fun indexOfKey(key: K): Int = map.indexOfKey(key)
@@ -55,7 +54,9 @@
class MutableIndexedReferenceMap<K, I : Immutable<M>, M : I>(
map: ArrayMap<K, MutableReference<I, M>> = ArrayMap()
) : IndexedReferenceMap<K, I, M>(map) {
- constructor(indexedReferenceMap: IndexedReferenceMap<K, I, M>) : this(
+ constructor(
+ indexedReferenceMap: IndexedReferenceMap<K, I, M>
+ ) : this(
ArrayMap(indexedReferenceMap.map).apply {
for (i in 0 until size) {
setValueAt(i, valueAt(i).toImmutable())
@@ -63,8 +64,7 @@
}
)
- @Suppress("ReplaceGetOrSet")
- fun mutate(key: K): M? = map.get(key)?.mutate()
+ @Suppress("ReplaceGetOrSet") fun mutate(key: K): M? = map.get(key)?.mutate()
fun put(key: K, value: M): I? = map.put(key, MutableReference(value))?.get()
diff --git a/services/permission/java/com/android/server/permission/access/immutable/IndexedReferenceMapExtensions.kt b/services/permission/java/com/android/server/permission/access/immutable/IndexedReferenceMapExtensions.kt
index 22b4d52..43a902b 100644
--- a/services/permission/java/com/android/server/permission/access/immutable/IndexedReferenceMapExtensions.kt
+++ b/services/permission/java/com/android/server/permission/access/immutable/IndexedReferenceMapExtensions.kt
@@ -72,7 +72,9 @@
key: K,
defaultValue: () -> M
): M {
- mutate(key)?.let { return it }
+ mutate(key)?.let {
+ return it
+ }
return defaultValue().also { put(key, it) }
}
diff --git a/services/permission/java/com/android/server/permission/access/immutable/IndexedSet.kt b/services/permission/java/com/android/server/permission/access/immutable/IndexedSet.kt
index 547e56c..cbc24b1 100644
--- a/services/permission/java/com/android/server/permission/access/immutable/IndexedSet.kt
+++ b/services/permission/java/com/android/server/permission/access/immutable/IndexedSet.kt
@@ -18,12 +18,8 @@
import android.util.ArraySet
-/**
- * Immutable set with index-based access.
- */
-sealed class IndexedSet<T>(
- internal val set: ArraySet<T>
-) : Immutable<MutableIndexedSet<T>> {
+/** Immutable set with index-based access. */
+sealed class IndexedSet<T>(internal val set: ArraySet<T>) : Immutable<MutableIndexedSet<T>> {
val size: Int
get() = set.size
@@ -40,12 +36,8 @@
override fun toString(): String = set.toString()
}
-/**
- * Mutable set with index-based access.
- */
-class MutableIndexedSet<T>(
- set: ArraySet<T> = ArraySet()
-) : IndexedSet<T>(set) {
+/** Mutable set with index-based access. */
+class MutableIndexedSet<T>(set: ArraySet<T> = ArraySet()) : IndexedSet<T>(set) {
constructor(indexedSet: IndexedSet<T>) : this(ArraySet(indexedSet.set))
fun add(element: T): Boolean = set.add(element)
diff --git a/services/permission/java/com/android/server/permission/access/immutable/IntMap.kt b/services/permission/java/com/android/server/permission/access/immutable/IntMap.kt
index 7ed29e8..e9a405f 100644
--- a/services/permission/java/com/android/server/permission/access/immutable/IntMap.kt
+++ b/services/permission/java/com/android/server/permission/access/immutable/IntMap.kt
@@ -18,12 +18,8 @@
import android.util.SparseArray
-/**
- * Immutable map with index-based access and [Int] keys.
- */
-sealed class IntMap<T>(
- internal val array: SparseArray<T>
-) : Immutable<MutableIntMap<T>> {
+/** Immutable map with index-based access and [Int] keys. */
+sealed class IntMap<T>(internal val array: SparseArray<T>) : Immutable<MutableIntMap<T>> {
val size: Int
get() = array.size()
@@ -44,12 +40,8 @@
override fun toString(): String = array.toString()
}
-/**
- * Mutable map with index-based access and [Int] keys.
- */
-class MutableIntMap<T>(
- array: SparseArray<T> = SparseArray()
-) : IntMap<T>(array) {
+/** Mutable map with index-based access and [Int] keys. */
+class MutableIntMap<T>(array: SparseArray<T> = SparseArray()) : IntMap<T>(array) {
constructor(intMap: IntMap<T>) : this(intMap.array.clone())
fun put(key: Int, value: T): T? = array.putReturnOld(key, value)
diff --git a/services/permission/java/com/android/server/permission/access/immutable/IntMapExtensions.kt b/services/permission/java/com/android/server/permission/access/immutable/IntMapExtensions.kt
index 9aa0a41..09d7319 100644
--- a/services/permission/java/com/android/server/permission/access/immutable/IntMapExtensions.kt
+++ b/services/permission/java/com/android/server/permission/access/immutable/IntMapExtensions.kt
@@ -36,7 +36,9 @@
inline fun <T, R> IntMap<T>.firstNotNullOfOrNullIndexed(transform: (Int, Int, T) -> R): R? {
forEachIndexed { index, key, value ->
- transform(index, key, value)?.let { return it }
+ transform(index, key, value)?.let {
+ return it
+ }
}
return null
}
@@ -72,7 +74,9 @@
}
inline fun <T> MutableIntMap<T>.getOrPut(key: Int, defaultValue: () -> T): T {
- get(key)?.let { return it }
+ get(key)?.let {
+ return it
+ }
return defaultValue().also { put(key, it) }
}
diff --git a/services/permission/java/com/android/server/permission/access/immutable/IntReferenceMap.kt b/services/permission/java/com/android/server/permission/access/immutable/IntReferenceMap.kt
index 160b227..3f265173 100644
--- a/services/permission/java/com/android/server/permission/access/immutable/IntReferenceMap.kt
+++ b/services/permission/java/com/android/server/permission/access/immutable/IntReferenceMap.kt
@@ -33,8 +33,7 @@
operator fun contains(key: Int): Boolean = array.contains(key)
- @Suppress("ReplaceGetOrSet")
- operator fun get(key: Int): I? = array.get(key)?.get()
+ @Suppress("ReplaceGetOrSet") operator fun get(key: Int): I? = array.get(key)?.get()
fun indexOfKey(key: Int): Int = array.indexOfKey(key)
@@ -55,7 +54,9 @@
class MutableIntReferenceMap<I : Immutable<M>, M : I>(
array: SparseArray<MutableReference<I, M>> = SparseArray()
) : IntReferenceMap<I, M>(array) {
- constructor(intReferenceMap: IntReferenceMap<I, M>) : this(
+ constructor(
+ intReferenceMap: IntReferenceMap<I, M>
+ ) : this(
intReferenceMap.array.clone().apply {
for (i in 0 until size()) {
setValueAt(i, valueAt(i).toImmutable())
@@ -63,8 +64,7 @@
}
)
- @Suppress("ReplaceGetOrSet")
- fun mutate(key: Int): M? = array.get(key)?.mutate()
+ @Suppress("ReplaceGetOrSet") fun mutate(key: Int): M? = array.get(key)?.mutate()
fun put(key: Int, value: M): I? = array.putReturnOld(key, MutableReference(value))?.get()
diff --git a/services/permission/java/com/android/server/permission/access/immutable/IntReferenceMapExtensions.kt b/services/permission/java/com/android/server/permission/access/immutable/IntReferenceMapExtensions.kt
index 1ed4f8a..a1bab95 100644
--- a/services/permission/java/com/android/server/permission/access/immutable/IntReferenceMapExtensions.kt
+++ b/services/permission/java/com/android/server/permission/access/immutable/IntReferenceMapExtensions.kt
@@ -72,7 +72,9 @@
key: Int,
defaultValue: () -> M
): M {
- mutate(key)?.let { return it }
+ mutate(key)?.let {
+ return it
+ }
return defaultValue().also { put(key, it) }
}
diff --git a/services/permission/java/com/android/server/permission/access/immutable/IntSet.kt b/services/permission/java/com/android/server/permission/access/immutable/IntSet.kt
index 21f2af2..1254797 100644
--- a/services/permission/java/com/android/server/permission/access/immutable/IntSet.kt
+++ b/services/permission/java/com/android/server/permission/access/immutable/IntSet.kt
@@ -18,12 +18,8 @@
import android.util.SparseBooleanArray
-/**
- * Immutable set with index-based access and [Int] elements.
- */
-sealed class IntSet(
- internal val array: SparseBooleanArray
-) : Immutable<MutableIntSet> {
+/** Immutable set with index-based access and [Int] elements. */
+sealed class IntSet(internal val array: SparseBooleanArray) : Immutable<MutableIntSet> {
val size: Int
get() = array.size()
@@ -40,12 +36,8 @@
override fun toString(): String = array.toString()
}
-/**
- * Mutable set with index-based access and [Int] elements.
- */
-class MutableIntSet(
- array: SparseBooleanArray = SparseBooleanArray()
-) : IntSet(array) {
+/** Mutable set with index-based access and [Int] elements. */
+class MutableIntSet(array: SparseBooleanArray = SparseBooleanArray()) : IntSet(array) {
constructor(intSet: IntSet) : this(intSet.array.clone())
fun add(element: Int): Boolean =
diff --git a/services/permission/java/com/android/server/permission/access/immutable/IntSetExtensions.kt b/services/permission/java/com/android/server/permission/access/immutable/IntSetExtensions.kt
index 163ebbf..9d0d14f 100644
--- a/services/permission/java/com/android/server/permission/access/immutable/IntSetExtensions.kt
+++ b/services/permission/java/com/android/server/permission/access/immutable/IntSetExtensions.kt
@@ -66,7 +66,7 @@
operator fun IntSet.plus(element: Int): MutableIntSet = toMutable().apply { this += element }
-fun MutableIntSet(values: IntArray): MutableIntSet = MutableIntSet().apply{ this += values }
+fun MutableIntSet(values: IntArray): MutableIntSet = MutableIntSet().apply { this += values }
operator fun MutableIntSet.plusAssign(element: Int) {
array.put(element, true)
diff --git a/services/permission/java/com/android/server/permission/access/immutable/MutableReference.kt b/services/permission/java/com/android/server/permission/access/immutable/MutableReference.kt
index 171cfeb..471a71b 100644
--- a/services/permission/java/com/android/server/permission/access/immutable/MutableReference.kt
+++ b/services/permission/java/com/android/server/permission/access/immutable/MutableReference.kt
@@ -27,21 +27,17 @@
* exposed on the immutable interface of the data structure as a `getFoo` method, and the [mutate]
* method exposed on the mutable interface of the data structure as a `mutateFoo` method. When the
* data structure is mutated/copied, a new instance of this class should be obtained with
- * [toImmutable], which makes the wrapped reference immutable-only again and thus prevents
- * further modifications to a data structure accessed with its immutable interface.
+ * [toImmutable], which makes the wrapped reference immutable-only again and thus prevents further
+ * modifications to a data structure accessed with its immutable interface.
*
* @see MutableIndexedReferenceMap
* @see MutableIntReferenceMap
*/
-class MutableReference<I : Immutable<M>, M : I> private constructor(
- private var immutable: I,
- private var mutable: M?
-) {
+class MutableReference<I : Immutable<M>, M : I>
+private constructor(private var immutable: I, private var mutable: M?) {
constructor(mutable: M) : this(mutable, mutable)
- /**
- * Return an immutable reference to the wrapped mutable data structure.
- */
+ /** Return an immutable reference to the wrapped mutable data structure. */
fun get(): I = immutable
/**
@@ -50,7 +46,9 @@
* already mutable.
*/
fun mutate(): M {
- mutable?.let { return it }
+ mutable?.let {
+ return it
+ }
return immutable.toMutable().also {
immutable = it
mutable = it
diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionMigration.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionMigration.kt
index 691ed8f..2983895 100644
--- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionMigration.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionMigration.kt
@@ -23,9 +23,7 @@
import com.android.server.permission.access.util.PackageVersionMigration
import com.android.server.pm.permission.PermissionMigrationHelper
-/**
- * This class migrate legacy permissions to unified permission subsystem
- */
+/** This class migrate legacy permissions to unified permission subsystem */
class AppIdPermissionMigration {
internal fun migrateSystemState(state: MutableAccessState) {
val legacyPermissionsManager =
@@ -34,10 +32,15 @@
return
}
- migratePermissions(state.mutateSystemState().mutatePermissions(),
- legacyPermissionsManager.legacyPermissions)
- migratePermissions(state.mutateSystemState().mutatePermissionTrees(),
- legacyPermissionsManager.legacyPermissionTrees, true)
+ migratePermissions(
+ state.mutateSystemState().mutatePermissions(),
+ legacyPermissionsManager.legacyPermissions
+ )
+ migratePermissions(
+ state.mutateSystemState().mutatePermissionTrees(),
+ legacyPermissionsManager.legacyPermissionTrees,
+ true
+ )
}
private fun migratePermissions(
@@ -46,14 +49,15 @@
isPermissionTree: Boolean = false
) {
legacyPermissions.forEach { (_, legacyPermission) ->
- val permission = Permission(
- legacyPermission.permissionInfo, false, legacyPermission.type, 0
- )
+ val permission =
+ Permission(legacyPermission.permissionInfo, false, legacyPermission.type, 0)
permissions[permission.name] = permission
if (DEBUG_MIGRATION) {
- Slog.v(LOG_TAG, "Migrated permission: ${permission.name}, type: " +
- "${permission.type}, appId: ${permission.appId}, protectionLevel: " +
- "${permission.protectionLevel}, tree: $isPermissionTree"
+ Slog.v(
+ LOG_TAG,
+ "Migrated permission: ${permission.name}, type: " +
+ "${permission.type}, appId: ${permission.appId}, protectionLevel: " +
+ "${permission.protectionLevel}, tree: $isPermissionTree"
)
}
}
@@ -81,25 +85,23 @@
val permissionFlags = MutableIndexedMap<String, Int>()
appIdPermissionFlags[appId] = permissionFlags
- legacyPermissionStates.forEach forEachPermission@ {
+ legacyPermissionStates.forEach forEachPermission@{
(permissionName, legacyPermissionState) ->
val permission = state.systemState.permissions[permissionName]
if (permission == null) {
Slog.w(
- LOG_TAG, "Dropping unknown permission $permissionName for app ID $appId" +
+ LOG_TAG,
+ "Dropping unknown permission $permissionName for app ID $appId" +
" when migrating permission state"
)
return@forEachPermission
}
- permissionFlags[permissionName] = migratePermissionFlags(
- permission, legacyPermissionState, appId, userId
- )
+ permissionFlags[permissionName] =
+ migratePermissionFlags(permission, legacyPermissionState, appId, userId)
}
val packageVersions = userState.mutatePackageVersions()
- packageNames.forEachIndexed { _, packageName ->
- packageVersions[packageName] = version
- }
+ packageNames.forEachIndexed { _, packageName -> packageVersions[packageName] = version }
}
}
@@ -109,29 +111,35 @@
appId: Int,
userId: Int
): Int {
- var flags = when {
- permission.isNormal -> if (legacyPermissionState.isGranted) {
- PermissionFlags.INSTALL_GRANTED
- } else {
- PermissionFlags.INSTALL_REVOKED
- }
- permission.isSignature || permission.isInternal ->
- if (legacyPermissionState.isGranted) {
- if (permission.isDevelopment || permission.isRole) {
- PermissionFlags.PROTECTION_GRANTED or PermissionFlags.RUNTIME_GRANTED
+ var flags =
+ when {
+ permission.isNormal ->
+ if (legacyPermissionState.isGranted) {
+ PermissionFlags.INSTALL_GRANTED
} else {
- PermissionFlags.PROTECTION_GRANTED
+ PermissionFlags.INSTALL_REVOKED
}
- } else {
- 0
- }
- permission.isRuntime ->
- if (legacyPermissionState.isGranted) PermissionFlags.RUNTIME_GRANTED else 0
- else -> 0
- }
- flags = PermissionFlags.updateFlags(
- permission, flags, legacyPermissionState.flags, legacyPermissionState.flags
- )
+ permission.isSignature || permission.isInternal ->
+ if (legacyPermissionState.isGranted) {
+ if (permission.isDevelopment || permission.isRole) {
+ PermissionFlags.PROTECTION_GRANTED or PermissionFlags.RUNTIME_GRANTED
+ } else {
+ PermissionFlags.PROTECTION_GRANTED
+ }
+ } else {
+ 0
+ }
+ permission.isRuntime ->
+ if (legacyPermissionState.isGranted) PermissionFlags.RUNTIME_GRANTED else 0
+ else -> 0
+ }
+ flags =
+ PermissionFlags.updateFlags(
+ permission,
+ flags,
+ legacyPermissionState.flags,
+ legacyPermissionState.flags
+ )
if (DEBUG_MIGRATION) {
val oldFlagString = PermissionFlags.apiFlagsToString(legacyPermissionState.flags)
val newFlagString = PermissionFlags.toString(flags)
@@ -139,7 +147,8 @@
val newGrantState = PermissionFlags.isPermissionGranted(flags)
val flagsMismatch = legacyPermissionState.flags != PermissionFlags.toApiFlags(flags)
Slog.v(
- LOG_TAG, "Migrated appId: $appId, permission: " +
+ LOG_TAG,
+ "Migrated appId: $appId, permission: " +
"${permission.name}, user: $userId, oldGrantState: $oldGrantState" +
", oldFlags: $oldFlagString, newFlags: $newFlagString, grantMismatch: " +
"${oldGrantState != newGrantState}, flagsMismatch: $flagsMismatch"
diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPersistence.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPersistence.kt
index 2c8175b..1f40f01 100644
--- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPersistence.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPersistence.kt
@@ -57,11 +57,12 @@
isPermissionTree: Boolean
) {
val systemState = state.mutateSystemState(WriteMode.NONE)
- val permissions = if (isPermissionTree) {
- systemState.mutatePermissionTrees()
- } else {
- systemState.mutatePermissions()
- }
+ val permissions =
+ if (isPermissionTree) {
+ systemState.mutatePermissionTrees()
+ } else {
+ systemState.mutatePermissions()
+ }
forEachTag {
when (val tagName = tagName) {
TAG_PERMISSION -> parsePermission(permissions)
@@ -71,10 +72,13 @@
permissions.forEachReversedIndexed { permissionIndex, _, permission ->
val packageName = permission.packageName
val externalState = state.externalState
- if (packageName !in externalState.packageStates &&
- packageName !in externalState.disabledSystemPackageStates) {
+ if (
+ packageName !in externalState.packageStates &&
+ packageName !in externalState.disabledSystemPackageStates
+ ) {
Slog.w(
- LOG_TAG, "Dropping permission ${permission.name} from unknown package" +
+ LOG_TAG,
+ "Dropping permission ${permission.name} from unknown package" +
" $packageName when parsing permissions"
)
permissions.removeAt(permissionIndex)
@@ -88,11 +92,12 @@
) {
val name = getAttributeValueOrThrow(ATTR_NAME).intern()
@Suppress("DEPRECATION")
- val permissionInfo = PermissionInfo().apply {
- this.name = name
- packageName = getAttributeValueOrThrow(ATTR_PACKAGE_NAME).intern()
- protectionLevel = getAttributeIntHexOrThrow(ATTR_PROTECTION_LEVEL)
- }
+ val permissionInfo =
+ PermissionInfo().apply {
+ this.name = name
+ packageName = getAttributeValueOrThrow(ATTR_PACKAGE_NAME).intern()
+ protectionLevel = getAttributeIntHexOrThrow(ATTR_PROTECTION_LEVEL)
+ }
val type = getAttributeIntOrThrow(ATTR_TYPE)
when (type) {
Permission.TYPE_MANIFEST -> {}
@@ -125,15 +130,14 @@
tagName: String,
permissions: IndexedMap<String, Permission>
) {
- tag(tagName) {
- permissions.forEachIndexed { _, _, it -> serializePermission(it) }
- }
+ tag(tagName) { permissions.forEachIndexed { _, _, it -> serializePermission(it) } }
}
private fun BinaryXmlSerializer.serializePermission(permission: Permission) {
val type = permission.type
when (type) {
- Permission.TYPE_MANIFEST, Permission.TYPE_DYNAMIC -> {}
+ Permission.TYPE_MANIFEST,
+ Permission.TYPE_DYNAMIC -> {}
Permission.TYPE_CONFIG -> return
else -> {
Slog.w(LOG_TAG, "Skipping serializing permission $name with unknown type $type")
@@ -228,11 +232,12 @@
tag(TAG_PERMISSION) {
attributeInterned(ATTR_NAME, name)
// Never serialize one-time permissions as granted.
- val serializedFlags = if (flags.hasBits(PermissionFlags.ONE_TIME)) {
- flags andInv PermissionFlags.RUNTIME_GRANTED
- } else {
- flags
- }
+ val serializedFlags =
+ if (flags.hasBits(PermissionFlags.ONE_TIME)) {
+ flags andInv PermissionFlags.RUNTIME_GRANTED
+ } else {
+ flags
+ }
attributeInt(ATTR_FLAGS, serializedFlags)
}
}
diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
index 345f101..08ba753 100644
--- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
@@ -55,7 +55,8 @@
@Volatile
private var onPermissionFlagsChangedListeners:
- IndexedListSet<OnPermissionFlagsChangedListener> = MutableIndexedListSet()
+ IndexedListSet<OnPermissionFlagsChangedListener> =
+ MutableIndexedListSet()
private val onPermissionFlagsChangedListenersLock = Any()
private val privilegedPermissionAllowlistViolations = MutableIndexedSet<String>()
@@ -73,30 +74,37 @@
override fun MutateStateScope.onInitialized() {
newState.externalState.configPermissions.forEach { (permissionName, permissionEntry) ->
val oldPermission = newState.systemState.permissions[permissionName]
- val newPermission = if (oldPermission != null) {
- if (permissionEntry.gids != null) {
- oldPermission.copy(
- gids = permissionEntry.gids, areGidsPerUser = permissionEntry.perUser
- )
+ val newPermission =
+ if (oldPermission != null) {
+ if (permissionEntry.gids != null) {
+ oldPermission.copy(
+ gids = permissionEntry.gids,
+ areGidsPerUser = permissionEntry.perUser
+ )
+ } else {
+ return@forEach
+ }
} else {
- return@forEach
+ @Suppress("DEPRECATION")
+ val permissionInfo =
+ PermissionInfo().apply {
+ name = permissionName
+ packageName = PLATFORM_PACKAGE_NAME
+ protectionLevel = PermissionInfo.PROTECTION_SIGNATURE
+ }
+ if (permissionEntry.gids != null) {
+ Permission(
+ permissionInfo,
+ false,
+ Permission.TYPE_CONFIG,
+ 0,
+ permissionEntry.gids,
+ permissionEntry.perUser
+ )
+ } else {
+ Permission(permissionInfo, false, Permission.TYPE_CONFIG, 0)
+ }
}
- } else {
- @Suppress("DEPRECATION")
- val permissionInfo = PermissionInfo().apply {
- name = permissionName
- packageName = PLATFORM_PACKAGE_NAME
- protectionLevel = PermissionInfo.PROTECTION_SIGNATURE
- }
- if (permissionEntry.gids != null) {
- Permission(
- permissionInfo, false, Permission.TYPE_CONFIG, 0, permissionEntry.gids,
- permissionEntry.perUser
- )
- } else {
- Permission(permissionInfo, false, Permission.TYPE_CONFIG, 0)
- }
- }
newState.mutateSystemState().mutatePermissions()[permissionName] = newPermission
}
}
@@ -200,30 +208,32 @@
val androidPackage = packageState.androidPackage ?: return
val appId = packageState.appId
androidPackage.requestedPermissions.forEach { permissionName ->
- val permission = newState.systemState.permissions[permissionName]
- ?: return@forEach
+ val permission = newState.systemState.permissions[permissionName] ?: return@forEach
if (!permission.isHardOrSoftRestricted) {
return@forEach
}
- val isRequestedBySystemPackage = anyPackageInAppId(appId) {
- it.isSystem && permissionName in it.androidPackage!!.requestedPermissions
- }
+ val isRequestedBySystemPackage =
+ anyPackageInAppId(appId) {
+ it.isSystem && permissionName in it.androidPackage!!.requestedPermissions
+ }
if (isRequestedBySystemPackage) {
return@forEach
}
val oldFlags = getPermissionFlags(appId, userId, permissionName)
var newFlags = oldFlags andInv PermissionFlags.UPGRADE_EXEMPT
val isExempt = newFlags.hasAnyBit(PermissionFlags.MASK_EXEMPT)
- newFlags = if (permission.isHardRestricted && !isExempt) {
- newFlags or PermissionFlags.RESTRICTION_REVOKED
- } else {
- newFlags andInv PermissionFlags.RESTRICTION_REVOKED
- }
- newFlags = if (permission.isSoftRestricted && !isExempt) {
- newFlags or PermissionFlags.SOFT_RESTRICTED
- } else {
- newFlags andInv PermissionFlags.SOFT_RESTRICTED
- }
+ newFlags =
+ if (permission.isHardRestricted && !isExempt) {
+ newFlags or PermissionFlags.RESTRICTION_REVOKED
+ } else {
+ newFlags andInv PermissionFlags.RESTRICTION_REVOKED
+ }
+ newFlags =
+ if (permission.isSoftRestricted && !isExempt) {
+ newFlags or PermissionFlags.SOFT_RESTRICTED
+ } else {
+ newFlags andInv PermissionFlags.SOFT_RESTRICTED
+ }
setPermissionFlags(appId, userId, permissionName, newFlags)
}
}
@@ -243,15 +253,15 @@
val androidPackage = packageState.androidPackage ?: return
val appId = packageState.appId
androidPackage.requestedPermissions.forEach { permissionName ->
- val permission = newState.systemState.permissions[permissionName]
- ?: return@forEach
+ val permission = newState.systemState.permissions[permissionName] ?: return@forEach
if (!permission.isRuntime || permission.isRemoved) {
return@forEach
}
- val isRequestedByOtherPackages = anyPackageInAppId(appId) {
- it.packageName != packageName &&
- permissionName in it.androidPackage!!.requestedPermissions
- }
+ val isRequestedByOtherPackages =
+ anyPackageInAppId(appId) {
+ it.packageName != packageName &&
+ permissionName in it.androidPackage!!.requestedPermissions
+ }
if (isRequestedByOtherPackages) {
return@forEach
}
@@ -260,13 +270,15 @@
return@forEach
}
var newFlags = oldFlags
- newFlags = if (
- newFlags.hasBits(PermissionFlags.ROLE) || newFlags.hasBits(PermissionFlags.PREGRANT)
- ) {
- newFlags or PermissionFlags.RUNTIME_GRANTED
- } else {
- newFlags andInv PermissionFlags.RUNTIME_GRANTED
- }
+ newFlags =
+ if (
+ newFlags.hasBits(PermissionFlags.ROLE) ||
+ newFlags.hasBits(PermissionFlags.PREGRANT)
+ ) {
+ newFlags or PermissionFlags.RUNTIME_GRANTED
+ } else {
+ newFlags andInv PermissionFlags.RUNTIME_GRANTED
+ }
newFlags = newFlags andInv USER_SETTABLE_MASK
if (newFlags.hasBits(PermissionFlags.LEGACY_GRANTED)) {
newFlags = newFlags or PermissionFlags.IMPLICIT
@@ -285,24 +297,32 @@
if (!canAdoptPermissions(packageName, originalPackageName)) {
return@forEachIndexed
}
- newState.systemState.permissions.forEachIndexed permissions@ {
- permissionIndex, permissionName, oldPermission ->
+ newState.systemState.permissions.forEachIndexed permissions@{
+ permissionIndex,
+ permissionName,
+ oldPermission ->
if (oldPermission.packageName != originalPackageName) {
return@permissions
}
@Suppress("DEPRECATION")
- val newPermissionInfo = PermissionInfo().apply {
- name = oldPermission.permissionInfo.name
- this.packageName = packageName
- protectionLevel = oldPermission.permissionInfo.protectionLevel
- }
+ val newPermissionInfo =
+ PermissionInfo().apply {
+ name = oldPermission.permissionInfo.name
+ this.packageName = packageName
+ protectionLevel = oldPermission.permissionInfo.protectionLevel
+ }
// Different from the old implementation, which removes the GIDs upon permission
// adoption, but adds them back on the next boot, we now just consistently keep the
// GIDs.
- val newPermission = oldPermission.copy(
- permissionInfo = newPermissionInfo, isReconciled = false, appId = 0
- )
- newState.mutateSystemState().mutatePermissions()
+ val newPermission =
+ oldPermission.copy(
+ permissionInfo = newPermissionInfo,
+ isReconciled = false,
+ appId = 0
+ )
+ newState
+ .mutateSystemState()
+ .mutatePermissions()
.putAt(permissionIndex, newPermission)
changedPermissionNames += permissionName
}
@@ -313,18 +333,20 @@
packageName: String,
originalPackageName: String
): Boolean {
- val originalPackageState = newState.externalState.packageStates[originalPackageName]
- ?: return false
+ val originalPackageState =
+ newState.externalState.packageStates[originalPackageName] ?: return false
if (!originalPackageState.isSystem) {
Slog.w(
- LOG_TAG, "Unable to adopt permissions from $originalPackageName to $packageName:" +
+ LOG_TAG,
+ "Unable to adopt permissions from $originalPackageName to $packageName:" +
" original package not in system partition"
)
return false
}
if (originalPackageState.androidPackage != null) {
Slog.w(
- LOG_TAG, "Unable to adopt permissions from $originalPackageName to $packageName:" +
+ LOG_TAG,
+ "Unable to adopt permissions from $originalPackageName to $packageName:" +
" original package still exists"
)
return false
@@ -339,20 +361,25 @@
val isInstantApp = packageState.userStates.allIndexed { _, _, it -> it.isInstantApp }
if (isInstantApp) {
Slog.w(
- LOG_TAG, "Ignoring permission groups declared in package" +
+ LOG_TAG,
+ "Ignoring permission groups declared in package" +
" ${packageState.packageName}: instant apps cannot declare permission groups"
)
return
}
packageState.androidPackage!!.permissionGroups.forEachIndexed { _, parsedPermissionGroup ->
- val newPermissionGroup = PackageInfoUtils.generatePermissionGroupInfo(
- parsedPermissionGroup, PackageManager.GET_META_DATA.toLong()
- )!!
+ val newPermissionGroup =
+ PackageInfoUtils.generatePermissionGroupInfo(
+ parsedPermissionGroup,
+ PackageManager.GET_META_DATA.toLong()
+ )!!
// TODO: Clear permission state on group take-over?
val permissionGroupName = newPermissionGroup.name
val oldPermissionGroup = newState.systemState.permissionGroups[permissionGroupName]
- if (oldPermissionGroup != null &&
- newPermissionGroup.packageName != oldPermissionGroup.packageName) {
+ if (
+ oldPermissionGroup != null &&
+ newPermissionGroup.packageName != oldPermissionGroup.packageName
+ ) {
val newPackageName = newPermissionGroup.packageName
val oldPackageName = oldPermissionGroup.packageName
// Different from the old implementation, which defines permission group on
@@ -361,7 +388,8 @@
// to permissions so that we no longer need to rely on the scan order.
if (!packageState.isSystem) {
Slog.w(
- LOG_TAG, "Ignoring permission group $permissionGroupName declared in" +
+ LOG_TAG,
+ "Ignoring permission group $permissionGroupName declared in" +
" package $newPackageName: already declared in another" +
" package $oldPackageName"
)
@@ -369,14 +397,16 @@
}
if (newState.externalState.packageStates[oldPackageName]?.isSystem == true) {
Slog.w(
- LOG_TAG, "Ignoring permission group $permissionGroupName declared in" +
+ LOG_TAG,
+ "Ignoring permission group $permissionGroupName declared in" +
" system package $newPackageName: already declared in another" +
" system package $oldPackageName"
)
return@forEachIndexed
}
Slog.w(
- LOG_TAG, "Overriding permission group $permissionGroupName with" +
+ LOG_TAG,
+ "Overriding permission group $permissionGroupName with" +
" new declaration in system package $newPackageName: originally" +
" declared in another package $oldPackageName"
)
@@ -393,20 +423,23 @@
val androidPackage = packageState.androidPackage!!
// This may not be the same package as the old permission because the old permission owner
// can be different, hence using this somewhat strange name to prevent misuse.
- val oldNewPackage = oldState.externalState.packageStates[packageState.packageName]
- ?.androidPackage
- val isPackageSigningChanged = oldNewPackage != null &&
- androidPackage.signingDetails != oldNewPackage.signingDetails
+ val oldNewPackage =
+ oldState.externalState.packageStates[packageState.packageName]?.androidPackage
+ val isPackageSigningChanged =
+ oldNewPackage != null && androidPackage.signingDetails != oldNewPackage.signingDetails
androidPackage.permissions.forEachIndexed { _, parsedPermission ->
- val newPermissionInfo = PackageInfoUtils.generatePermissionInfo(
- parsedPermission, PackageManager.GET_META_DATA.toLong()
- )!!
+ val newPermissionInfo =
+ PackageInfoUtils.generatePermissionInfo(
+ parsedPermission,
+ PackageManager.GET_META_DATA.toLong()
+ )!!
val permissionName = newPermissionInfo.name
- val oldPermission = if (parsedPermission.isTree) {
- newState.systemState.permissionTrees[permissionName]
- } else {
- newState.systemState.permissions[permissionName]
- }
+ val oldPermission =
+ if (parsedPermission.isTree) {
+ newState.systemState.permissionTrees[permissionName]
+ } else {
+ newState.systemState.permissions[permissionName]
+ }
// Different from the old implementation, which may add an (incomplete) signature
// permission inside another package's permission tree, we now consistently ignore such
// permissions.
@@ -414,128 +447,152 @@
val newPackageName = newPermissionInfo.packageName
if (permissionTree != null && newPackageName != permissionTree.packageName) {
Slog.w(
- LOG_TAG, "Ignoring permission $permissionName declared in package" +
+ LOG_TAG,
+ "Ignoring permission $permissionName declared in package" +
" $newPackageName: base permission tree ${permissionTree.name} is" +
" declared in another package ${permissionTree.packageName}"
)
return@forEachIndexed
}
- val newPermission = if (oldPermission != null &&
- newPackageName != oldPermission.packageName) {
- val oldPackageName = oldPermission.packageName
- // Only allow system apps to redefine non-system permissions.
- if (!packageState.isSystem) {
- Slog.w(
- LOG_TAG, "Ignoring permission $permissionName declared in package" +
- " $newPackageName: already declared in another package" +
- " $oldPackageName"
- )
- return@forEachIndexed
- }
- if (oldPermission.type == Permission.TYPE_CONFIG && !oldPermission.isReconciled) {
- // It's a config permission and has no owner, take ownership now.
- oldPermission.copy(
- permissionInfo = newPermissionInfo, isReconciled = true,
- type = Permission.TYPE_MANIFEST, appId = packageState.appId
- )
- } else if (newState.externalState.packageStates[oldPackageName]?.isSystem != true) {
- Slog.w(
- LOG_TAG, "Overriding permission $permissionName with new declaration in" +
- " system package $newPackageName: originally declared in another" +
- " package $oldPackageName"
- )
- // Remove permission state on owner change.
- newState.externalState.userIds.forEachIndexed { _, userId ->
- newState.externalState.appIdPackageNames.forEachIndexed { _, appId, _ ->
- setPermissionFlags(appId, userId, permissionName, 0)
- }
- }
- // Different from the old implementation, which removes the GIDs upon permission
- // override, but adds them back on the next boot, we now just consistently keep
- // the GIDs.
- Permission(
- newPermissionInfo, true, Permission.TYPE_MANIFEST, packageState.appId,
- oldPermission.gids, oldPermission.areGidsPerUser
- )
- } else {
- Slog.w(
- LOG_TAG, "Ignoring permission $permissionName declared in system package" +
- " $newPackageName: already declared in another system package" +
- " $oldPackageName"
- )
- return@forEachIndexed
- }
- } else {
- if (oldPermission != null && oldPermission.isReconciled) {
- val isPermissionGroupChanged = newPermissionInfo.isRuntime &&
- newPermissionInfo.group != null &&
- newPermissionInfo.group != oldPermission.groupName
- val isPermissionProtectionChanged =
- oldPermission.type != Permission.TYPE_CONFIG && (
- (newPermissionInfo.isRuntime && !oldPermission.isRuntime) ||
- (newPermissionInfo.isInternal && !oldPermission.isInternal)
+ val newPermission =
+ if (oldPermission != null && newPackageName != oldPermission.packageName) {
+ val oldPackageName = oldPermission.packageName
+ // Only allow system apps to redefine non-system permissions.
+ if (!packageState.isSystem) {
+ Slog.w(
+ LOG_TAG,
+ "Ignoring permission $permissionName declared in package" +
+ " $newPackageName: already declared in another package" +
+ " $oldPackageName"
)
- if (isPermissionGroupChanged || isPermissionProtectionChanged) {
+ return@forEachIndexed
+ }
+ if (
+ oldPermission.type == Permission.TYPE_CONFIG && !oldPermission.isReconciled
+ ) {
+ // It's a config permission and has no owner, take ownership now.
+ oldPermission.copy(
+ permissionInfo = newPermissionInfo,
+ isReconciled = true,
+ type = Permission.TYPE_MANIFEST,
+ appId = packageState.appId
+ )
+ } else if (
+ newState.externalState.packageStates[oldPackageName]?.isSystem != true
+ ) {
+ Slog.w(
+ LOG_TAG,
+ "Overriding permission $permissionName with new declaration in" +
+ " system package $newPackageName: originally declared in another" +
+ " package $oldPackageName"
+ )
+ // Remove permission state on owner change.
newState.externalState.userIds.forEachIndexed { _, userId ->
newState.externalState.appIdPackageNames.forEachIndexed { _, appId, _ ->
- if (isPermissionGroupChanged) {
- // We might auto-grant permissions if any permission of
- // the group is already granted. Hence if the group of
- // a granted permission changes we need to revoke it to
- // avoid having permissions of the new group auto-granted.
- Slog.w(
- LOG_TAG, "Revoking runtime permission $permissionName for" +
- " appId $appId and userId $userId as the permission" +
- " group changed from ${oldPermission.groupName}" +
- " to ${newPermissionInfo.group}"
- )
- }
- if (isPermissionProtectionChanged) {
- Slog.w(
- LOG_TAG, "Revoking permission $permissionName for" +
- " appId $appId and userId $userId as the permission" +
- " protection changed."
- )
- }
setPermissionFlags(appId, userId, permissionName, 0)
}
}
+ // Different from the old implementation, which removes the GIDs upon
+ // permission
+ // override, but adds them back on the next boot, we now just consistently
+ // keep
+ // the GIDs.
+ Permission(
+ newPermissionInfo,
+ true,
+ Permission.TYPE_MANIFEST,
+ packageState.appId,
+ oldPermission.gids,
+ oldPermission.areGidsPerUser
+ )
+ } else {
+ Slog.w(
+ LOG_TAG,
+ "Ignoring permission $permissionName declared in system package" +
+ " $newPackageName: already declared in another system package" +
+ " $oldPackageName"
+ )
+ return@forEachIndexed
+ }
+ } else {
+ if (oldPermission != null && oldPermission.isReconciled) {
+ val isPermissionGroupChanged =
+ newPermissionInfo.isRuntime &&
+ newPermissionInfo.group != null &&
+ newPermissionInfo.group != oldPermission.groupName
+ val isPermissionProtectionChanged =
+ oldPermission.type != Permission.TYPE_CONFIG &&
+ ((newPermissionInfo.isRuntime && !oldPermission.isRuntime) ||
+ (newPermissionInfo.isInternal && !oldPermission.isInternal))
+ if (isPermissionGroupChanged || isPermissionProtectionChanged) {
+ newState.externalState.userIds.forEachIndexed { _, userId ->
+ newState.externalState.appIdPackageNames.forEachIndexed {
+ _,
+ appId,
+ _ ->
+ if (isPermissionGroupChanged) {
+ // We might auto-grant permissions if any permission of
+ // the group is already granted. Hence if the group of
+ // a granted permission changes we need to revoke it to
+ // avoid having permissions of the new group auto-granted.
+ Slog.w(
+ LOG_TAG,
+ "Revoking runtime permission $permissionName for" +
+ " appId $appId and userId $userId as the permission" +
+ " group changed from ${oldPermission.groupName}" +
+ " to ${newPermissionInfo.group}"
+ )
+ }
+ if (isPermissionProtectionChanged) {
+ Slog.w(
+ LOG_TAG,
+ "Revoking permission $permissionName for" +
+ " appId $appId and userId $userId as the permission" +
+ " protection changed."
+ )
+ }
+ setPermissionFlags(appId, userId, permissionName, 0)
+ }
+ }
+ }
+ }
+
+ // Different from the old implementation, which doesn't update the permission
+ // definition upon app update, but does update it on the next boot, we now
+ // consistently update the permission definition upon app update.
+ @Suppress("IfThenToElvis")
+ if (oldPermission != null) {
+ oldPermission.copy(
+ permissionInfo = newPermissionInfo,
+ isReconciled = true,
+ type = Permission.TYPE_MANIFEST,
+ appId = packageState.appId
+ )
+ } else {
+ Permission(
+ newPermissionInfo,
+ true,
+ Permission.TYPE_MANIFEST,
+ packageState.appId
+ )
}
}
- // Different from the old implementation, which doesn't update the permission
- // definition upon app update, but does update it on the next boot, we now
- // consistently update the permission definition upon app update.
- @Suppress("IfThenToElvis")
- if (oldPermission != null) {
- oldPermission.copy(
- permissionInfo = newPermissionInfo, isReconciled = true,
- type = Permission.TYPE_MANIFEST, appId = packageState.appId
- )
- } else {
- Permission(
- newPermissionInfo, true, Permission.TYPE_MANIFEST, packageState.appId
- )
- }
- }
-
if (parsedPermission.isTree) {
newState.mutateSystemState().mutatePermissionTrees()[permissionName] = newPermission
} else {
newState.mutateSystemState().mutatePermissions()[permissionName] = newPermission
- val isPermissionChanged = oldPermission == null ||
- newPackageName != oldPermission.packageName ||
- newPermission.protectionLevel != oldPermission.protectionLevel || (
- oldPermission.isReconciled && (
- (newPermission.isSignature && isPackageSigningChanged) || (
- newPermission.isKnownSigner &&
- newPermission.knownCerts != oldPermission.knownCerts
- ) || (
- newPermission.isRuntime && newPermission.groupName != null &&
- newPermission.groupName != oldPermission.groupName
- )
- )
- )
+ val isPermissionChanged =
+ oldPermission == null ||
+ newPackageName != oldPermission.packageName ||
+ newPermission.protectionLevel != oldPermission.protectionLevel ||
+ (oldPermission.isReconciled &&
+ ((newPermission.isSignature && isPackageSigningChanged) ||
+ (newPermission.isKnownSigner &&
+ newPermission.knownCerts != oldPermission.knownCerts) ||
+ (newPermission.isRuntime &&
+ newPermission.groupName != null &&
+ newPermission.groupName != oldPermission.groupName)))
if (isPermissionChanged) {
changedPermissionNames += permissionName
}
@@ -552,39 +609,47 @@
if (packageState != null && androidPackage == null) {
return
}
- val disabledSystemPackage = newState.externalState.disabledSystemPackageStates[packageName]
- ?.androidPackage
+ val disabledSystemPackage =
+ newState.externalState.disabledSystemPackageStates[packageName]?.androidPackage
// Unlike in the previous implementation, we now also retain permission trees defined by
// disabled system packages for consistency with permissions.
newState.systemState.permissionTrees.forEachReversedIndexed {
- permissionTreeIndex, permissionTreeName, permissionTree ->
- if (permissionTree.packageName == packageName && (
- packageState == null || androidPackage!!.permissions.noneIndexed { _, it ->
- it.isTree && it.name == permissionTreeName
- }
- ) && (
- disabledSystemPackage?.permissions?.anyIndexed { _, it ->
- it.isTree && it.name == permissionTreeName
- } != true
- )) {
+ permissionTreeIndex,
+ permissionTreeName,
+ permissionTree ->
+ if (
+ permissionTree.packageName == packageName &&
+ (packageState == null ||
+ androidPackage!!.permissions.noneIndexed { _, it ->
+ it.isTree && it.name == permissionTreeName
+ }) &&
+ (disabledSystemPackage?.permissions?.anyIndexed { _, it ->
+ it.isTree && it.name == permissionTreeName
+ } != true)
+ ) {
newState.mutateSystemState().mutatePermissionTrees().removeAt(permissionTreeIndex)
}
}
newState.systemState.permissions.forEachReversedIndexed {
- permissionIndex, permissionName, permission ->
+ permissionIndex,
+ permissionName,
+ permission ->
val updatedPermission = updatePermissionIfDynamic(permission)
- newState.mutateSystemState().mutatePermissions()
+ newState
+ .mutateSystemState()
+ .mutatePermissions()
.putAt(permissionIndex, updatedPermission)
- if (updatedPermission.packageName == packageName && (
- packageState == null || androidPackage!!.permissions.noneIndexed { _, it ->
- !it.isTree && it.name == permissionName
- }
- ) && (
- disabledSystemPackage?.permissions?.anyIndexed { _, it ->
- !it.isTree && it.name == permissionName
- } != true
- )) {
+ if (
+ updatedPermission.packageName == packageName &&
+ (packageState == null ||
+ androidPackage!!.permissions.noneIndexed { _, it ->
+ !it.isTree && it.name == permissionName
+ }) &&
+ (disabledSystemPackage?.permissions?.anyIndexed { _, it ->
+ !it.isTree && it.name == permissionName
+ } != true)
+ ) {
// Different from the old implementation where we keep the permission state if the
// permission is declared by a disabled system package (ag/15189282), we now
// shouldn't be notified when the updated system package is removed but the disabled
@@ -608,9 +673,12 @@
val permissionTree = findPermissionTree(permission.name) ?: return permission
@Suppress("DEPRECATION")
return permission.copy(
- permissionInfo = PermissionInfo(permission.permissionInfo).apply {
- packageName = permissionTree.packageName
- }, appId = permissionTree.appId, isReconciled = true
+ permissionInfo =
+ PermissionInfo(permission.permissionInfo).apply {
+ packageName = permissionTree.packageName
+ },
+ appId = permissionTree.appId,
+ isReconciled = true
)
}
@@ -636,8 +704,9 @@
}
private fun MutateStateScope.revokePermissionsOnPackageUpdate(appId: Int) {
- val hasOldPackage = appId in oldState.externalState.appIdPackageNames &&
- anyPackageInAppId(appId, oldState) { true }
+ val hasOldPackage =
+ appId in oldState.externalState.appIdPackageNames &&
+ anyPackageInAppId(appId, oldState) { true }
if (!hasOldPackage) {
// Don't revoke anything if this isn't a package update, i.e. if information about the
// old package isn't available. Notably, this also means skipping packages changed via
@@ -650,46 +719,58 @@
// app updated in an attempt to get unscoped storage. If so, revoke all storage permissions.
val oldTargetSdkVersion =
reducePackageInAppId(appId, Build.VERSION_CODES.CUR_DEVELOPMENT, oldState) {
- targetSdkVersion, packageState ->
+ targetSdkVersion,
+ packageState ->
targetSdkVersion.coerceAtMost(packageState.androidPackage!!.targetSdkVersion)
}
val newTargetSdkVersion =
reducePackageInAppId(appId, Build.VERSION_CODES.CUR_DEVELOPMENT, newState) {
- targetSdkVersion, packageState ->
+ targetSdkVersion,
+ packageState ->
targetSdkVersion.coerceAtMost(packageState.androidPackage!!.targetSdkVersion)
}
@Suppress("ConvertTwoComparisonsToRangeCheck")
- val isTargetSdkVersionDowngraded = oldTargetSdkVersion >= Build.VERSION_CODES.Q &&
- newTargetSdkVersion < Build.VERSION_CODES.Q
+ val isTargetSdkVersionDowngraded =
+ oldTargetSdkVersion >= Build.VERSION_CODES.Q &&
+ newTargetSdkVersion < Build.VERSION_CODES.Q
@Suppress("ConvertTwoComparisonsToRangeCheck")
- val isTargetSdkVersionUpgraded = oldTargetSdkVersion < Build.VERSION_CODES.Q &&
- newTargetSdkVersion >= Build.VERSION_CODES.Q
- val oldIsRequestLegacyExternalStorage = anyPackageInAppId(appId, oldState) {
- it.androidPackage!!.isRequestLegacyExternalStorage
- }
- val newIsRequestLegacyExternalStorage = anyPackageInAppId(appId, newState) {
- it.androidPackage!!.isRequestLegacyExternalStorage
- }
- val isNewlyRequestingLegacyExternalStorage = !isTargetSdkVersionUpgraded &&
- !oldIsRequestLegacyExternalStorage && newIsRequestLegacyExternalStorage
- val shouldRevokeStorageAndMediaPermissions = isNewlyRequestingLegacyExternalStorage ||
- isTargetSdkVersionDowngraded
+ val isTargetSdkVersionUpgraded =
+ oldTargetSdkVersion < Build.VERSION_CODES.Q &&
+ newTargetSdkVersion >= Build.VERSION_CODES.Q
+ val oldIsRequestLegacyExternalStorage =
+ anyPackageInAppId(appId, oldState) {
+ it.androidPackage!!.isRequestLegacyExternalStorage
+ }
+ val newIsRequestLegacyExternalStorage =
+ anyPackageInAppId(appId, newState) {
+ it.androidPackage!!.isRequestLegacyExternalStorage
+ }
+ val isNewlyRequestingLegacyExternalStorage =
+ !isTargetSdkVersionUpgraded &&
+ !oldIsRequestLegacyExternalStorage &&
+ newIsRequestLegacyExternalStorage
+ val shouldRevokeStorageAndMediaPermissions =
+ isNewlyRequestingLegacyExternalStorage || isTargetSdkVersionDowngraded
if (shouldRevokeStorageAndMediaPermissions) {
newState.userStates.forEachIndexed { _, userId, userState ->
userState.appIdPermissionFlags[appId]?.forEachReversedIndexed {
- _, permissionName, oldFlags ->
+ _,
+ permissionName,
+ oldFlags ->
// Do not revoke the permission during an upgrade if it's POLICY_FIXED or
// SYSTEM_FIXED. Otherwise the user cannot grant back the permission.
- if (permissionName in STORAGE_AND_MEDIA_PERMISSIONS &&
- oldFlags.hasBits(PermissionFlags.RUNTIME_GRANTED) &&
- !oldFlags.hasAnyBit(SYSTEM_OR_POLICY_FIXED_MASK)) {
+ if (
+ permissionName in STORAGE_AND_MEDIA_PERMISSIONS &&
+ oldFlags.hasBits(PermissionFlags.RUNTIME_GRANTED) &&
+ !oldFlags.hasAnyBit(SYSTEM_OR_POLICY_FIXED_MASK)
+ ) {
Slog.v(
- LOG_TAG, "Revoking storage permission: $permissionName for appId: " +
+ LOG_TAG,
+ "Revoking storage permission: $permissionName for appId: " +
" $appId and user: $userId"
)
- val newFlags = oldFlags andInv (
- PermissionFlags.RUNTIME_GRANTED or USER_SETTABLE_MASK
- )
+ val newFlags =
+ oldFlags andInv (PermissionFlags.RUNTIME_GRANTED or USER_SETTABLE_MASK)
setPermissionFlags(appId, userId, permissionName, newFlags)
}
}
@@ -704,9 +785,10 @@
val externalState = newState.externalState
externalState.userIds.forEachIndexed { _, userId ->
externalState.appIdPackageNames.forEachIndexed { _, appId, _ ->
- val isPermissionRequested = anyPackageInAppId(appId) {
- permissionName in it.androidPackage!!.requestedPermissions
- }
+ val isPermissionRequested =
+ anyPackageInAppId(appId) {
+ permissionName in it.androidPackage!!.requestedPermissions
+ }
if (isPermissionRequested) {
evaluatePermissionState(appId, userId, permissionName, installedPackageState)
}
@@ -720,7 +802,9 @@
) {
newState.externalState.userIds.forEachIndexed { _, userId ->
evaluateAllPermissionStatesForPackageAndUser(
- packageState, userId, installedPackageState
+ packageState,
+ userId,
+ installedPackageState
)
}
}
@@ -732,7 +816,10 @@
) {
packageState.androidPackage?.requestedPermissions?.forEach { permissionName ->
evaluatePermissionState(
- packageState.appId, userId, permissionName, installedPackageState
+ packageState.appId,
+ userId,
+ permissionName,
+ installedPackageState
)
}
}
@@ -779,57 +866,71 @@
val wasGranted = oldFlags.hasBits(PermissionFlags.INSTALL_GRANTED)
if (!wasGranted) {
val wasRevoked = oldFlags.hasBits(PermissionFlags.INSTALL_REVOKED)
- val isRequestedByInstalledPackage = installedPackageState != null &&
- permissionName in installedPackageState.androidPackage!!.requestedPermissions
+ val isRequestedByInstalledPackage =
+ installedPackageState != null &&
+ permissionName in
+ installedPackageState.androidPackage!!.requestedPermissions
val isRequestedBySystemPackage =
requestingPackageStates.anyIndexed { _, it -> it.isSystem }
- val isCompatibilityPermission = requestingPackageStates.anyIndexed { _, it ->
- isCompatibilityPermissionForPackage(it.androidPackage!!, permissionName)
- }
+ val isCompatibilityPermission =
+ requestingPackageStates.anyIndexed { _, it ->
+ isCompatibilityPermissionForPackage(it.androidPackage!!, permissionName)
+ }
// If this is an existing, non-system package,
// then we can't add any new permissions to it.
// Except if this is a permission that was added to the platform
- var newFlags = if (!wasRevoked || isRequestedByInstalledPackage ||
- isRequestedBySystemPackage || isCompatibilityPermission) {
- PermissionFlags.INSTALL_GRANTED
- } else {
- PermissionFlags.INSTALL_REVOKED
- }
+ var newFlags =
+ if (
+ !wasRevoked ||
+ isRequestedByInstalledPackage ||
+ isRequestedBySystemPackage ||
+ isCompatibilityPermission
+ ) {
+ PermissionFlags.INSTALL_GRANTED
+ } else {
+ PermissionFlags.INSTALL_REVOKED
+ }
if (permission.isAppOp) {
- newFlags = newFlags or (
- oldFlags and (PermissionFlags.ROLE or PermissionFlags.USER_SET)
- )
+ newFlags =
+ newFlags or
+ (oldFlags and (PermissionFlags.ROLE or PermissionFlags.USER_SET))
}
setPermissionFlags(appId, userId, permissionName, newFlags)
}
} else if (permission.isSignature || permission.isInternal) {
val wasProtectionGranted = oldFlags.hasBits(PermissionFlags.PROTECTION_GRANTED)
- var newFlags = if (hasMissingPackage && wasProtectionGranted) {
- // Keep the non-runtime permission grants for shared UID with missing androidPackage
- PermissionFlags.PROTECTION_GRANTED
- } else {
- val mayGrantByPrivileged = !permission.isPrivileged ||
- requestingPackageStates.anyIndexed { _, it ->
- checkPrivilegedPermissionAllowlist(it, permission)
- }
- val shouldGrantBySignature = permission.isSignature &&
- requestingPackageStates.anyIndexed { _, it ->
- shouldGrantPermissionBySignature(it, permission)
- }
- val shouldGrantByProtectionFlags = requestingPackageStates.anyIndexed { _, it ->
- shouldGrantPermissionByProtectionFlags(it, permission)
- }
- if (mayGrantByPrivileged &&
- (shouldGrantBySignature || shouldGrantByProtectionFlags)) {
+ var newFlags =
+ if (hasMissingPackage && wasProtectionGranted) {
+ // Keep the non-runtime permission grants for shared UID with missing
+ // androidPackage
PermissionFlags.PROTECTION_GRANTED
} else {
- 0
+ val mayGrantByPrivileged =
+ !permission.isPrivileged ||
+ requestingPackageStates.anyIndexed { _, it ->
+ checkPrivilegedPermissionAllowlist(it, permission)
+ }
+ val shouldGrantBySignature =
+ permission.isSignature &&
+ requestingPackageStates.anyIndexed { _, it ->
+ shouldGrantPermissionBySignature(it, permission)
+ }
+ val shouldGrantByProtectionFlags =
+ requestingPackageStates.anyIndexed { _, it ->
+ shouldGrantPermissionByProtectionFlags(it, permission)
+ }
+ if (
+ mayGrantByPrivileged &&
+ (shouldGrantBySignature || shouldGrantByProtectionFlags)
+ ) {
+ PermissionFlags.PROTECTION_GRANTED
+ } else {
+ 0
+ }
}
- }
if (permission.isAppOp) {
- newFlags = newFlags or (
- oldFlags and (PermissionFlags.ROLE or PermissionFlags.USER_SET)
- )
+ newFlags =
+ newFlags or (oldFlags and (PermissionFlags.ROLE or PermissionFlags.USER_SET))
}
// Different from the old implementation, which seemingly allows granting an
// unallowlisted privileged permission via development or role but revokes it upon next
@@ -840,9 +941,9 @@
newFlags = newFlags or (oldFlags and PermissionFlags.RUNTIME_GRANTED)
}
if (permission.isRole) {
- newFlags = newFlags or (
- oldFlags and (PermissionFlags.ROLE or PermissionFlags.RUNTIME_GRANTED)
- )
+ newFlags =
+ newFlags or
+ (oldFlags and (PermissionFlags.ROLE or PermissionFlags.RUNTIME_GRANTED))
}
setPermissionFlags(appId, userId, permissionName, newFlags)
} else if (permission.isRuntime) {
@@ -850,7 +951,9 @@
val wasRevoked = newFlags != 0 && !PermissionFlags.isPermissionGranted(newFlags)
val targetSdkVersion =
requestingPackageStates.reduceIndexed(Build.VERSION_CODES.CUR_DEVELOPMENT) {
- targetSdkVersion, _, packageState ->
+ targetSdkVersion,
+ _,
+ packageState ->
targetSdkVersion.coerceAtMost(packageState.androidPackage!!.targetSdkVersion)
}
if (targetSdkVersion < Build.VERSION_CODES.M) {
@@ -883,23 +986,27 @@
}
}
val wasGrantedByImplicit = newFlags.hasBits(PermissionFlags.IMPLICIT_GRANTED)
- val isLeanbackNotificationsPermission = newState.externalState.isLeanback &&
- permissionName in NOTIFICATIONS_PERMISSIONS
- val isImplicitPermission = requestingPackageStates.anyIndexed { _, it ->
- permissionName in it.androidPackage!!.implicitPermissions
- }
- val sourcePermissions = newState.externalState
- .implicitToSourcePermissions[permissionName]
- val isAnySourcePermissionNonRuntime = sourcePermissions?.anyIndexed {
- _, sourcePermissionName ->
- val sourcePermission = newState.systemState.permissions[sourcePermissionName]
- checkNotNull(sourcePermission) {
- "Unknown source permission $sourcePermissionName in split permissions"
+ val isLeanbackNotificationsPermission =
+ newState.externalState.isLeanback && permissionName in NOTIFICATIONS_PERMISSIONS
+ val isImplicitPermission =
+ requestingPackageStates.anyIndexed { _, it ->
+ permissionName in it.androidPackage!!.implicitPermissions
}
- !sourcePermission.isRuntime
- } ?: false
- val shouldGrantByImplicit = isLeanbackNotificationsPermission ||
- (isImplicitPermission && isAnySourcePermissionNonRuntime)
+ val sourcePermissions =
+ newState.externalState.implicitToSourcePermissions[permissionName]
+ val isAnySourcePermissionNonRuntime =
+ sourcePermissions?.anyIndexed { _, sourcePermissionName ->
+ val sourcePermission =
+ newState.systemState.permissions[sourcePermissionName]
+ checkNotNull(sourcePermission) {
+ "Unknown source permission $sourcePermissionName in split permissions"
+ }
+ !sourcePermission.isRuntime
+ }
+ ?: false
+ val shouldGrantByImplicit =
+ isLeanbackNotificationsPermission ||
+ (isImplicitPermission && isAnySourcePermissionNonRuntime)
if (shouldGrantByImplicit) {
newFlags = newFlags or PermissionFlags.IMPLICIT_GRANTED
if (wasRevoked) {
@@ -907,26 +1014,31 @@
}
} else {
newFlags = newFlags andInv PermissionFlags.IMPLICIT_GRANTED
- if ((wasGrantedByLegacy || wasGrantedByImplicit) &&
- newFlags.hasBits(PermissionFlags.APP_OP_REVOKED)) {
+ if (
+ (wasGrantedByLegacy || wasGrantedByImplicit) &&
+ newFlags.hasBits(PermissionFlags.APP_OP_REVOKED)
+ ) {
// The permission was granted from a compatibility grant or an implicit
// grant, however this flag might still be set if the user denied this
// permission in the settings. Hence upon app upgrade and when this
// permission is no longer LEGACY_GRANTED or IMPLICIT_GRANTED and we revoke
// the permission, we want to remove this flag so that the app can request
// the permission again.
- newFlags = newFlags andInv (
- PermissionFlags.RUNTIME_GRANTED or PermissionFlags.APP_OP_REVOKED
- )
+ newFlags =
+ newFlags andInv
+ (PermissionFlags.RUNTIME_GRANTED or PermissionFlags.APP_OP_REVOKED)
}
}
if (!isImplicitPermission && hasImplicitFlag) {
newFlags = newFlags andInv PermissionFlags.IMPLICIT
var shouldRetainAsNearbyDevices = false
if (permissionName in NEARBY_DEVICES_PERMISSIONS) {
- val accessBackgroundLocationFlags = getPermissionFlags(
- appId, userId, Manifest.permission.ACCESS_BACKGROUND_LOCATION
- )
+ val accessBackgroundLocationFlags =
+ getPermissionFlags(
+ appId,
+ userId,
+ Manifest.permission.ACCESS_BACKGROUND_LOCATION
+ )
shouldRetainAsNearbyDevices =
PermissionFlags.isAppOpGranted(accessBackgroundLocationFlags) &&
!accessBackgroundLocationFlags.hasBits(PermissionFlags.IMPLICIT)
@@ -937,46 +1049,57 @@
newFlags = newFlags or PermissionFlags.RUNTIME_GRANTED
}
} else {
- newFlags = newFlags andInv (
- PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET or
- PermissionFlags.USER_FIXED
- )
+ newFlags =
+ newFlags andInv
+ (PermissionFlags.RUNTIME_GRANTED or
+ PermissionFlags.USER_SET or
+ PermissionFlags.USER_FIXED)
}
}
}
val wasExempt = newFlags.hasAnyBit(PermissionFlags.MASK_EXEMPT)
val wasRestricted = newFlags.hasAnyBit(PermissionFlags.MASK_RESTRICTED)
- val isExempt = if (permission.isHardOrSoftRestricted && !wasExempt && !wasRestricted) {
- // All restricted permissions start as exempt. If there's an installer for the
- // package, we will drop this UPGRADE_EXEMPT flag when we receive the
- // onPackageInstalled() callback and set up the INSTALLER_EXEMPT flags.
- // UPGRADE_EXEMPT is chosen instead of other flags because it is the same flag that
- // was assigned to pre-installed apps in RuntimePermissionsUpgradeController, and to
- // apps with missing permission state.
- // This way we make sure both pre-installed apps, and apps updated/installed after
- // a rollback snapshot is taken, can get the allowlist for permissions that won't be
- // allowlisted otherwise.
- newFlags = newFlags or PermissionFlags.UPGRADE_EXEMPT
- true
- } else {
- wasExempt
- }
- newFlags = if (permission.isHardRestricted && !isExempt) {
- newFlags or PermissionFlags.RESTRICTION_REVOKED
- } else {
- newFlags andInv PermissionFlags.RESTRICTION_REVOKED
- }
- newFlags = if (permission.isSoftRestricted && !isExempt) {
- newFlags or PermissionFlags.SOFT_RESTRICTED
- } else {
- newFlags andInv PermissionFlags.SOFT_RESTRICTED
- }
+ val isExempt =
+ if (permission.isHardOrSoftRestricted && !wasExempt && !wasRestricted) {
+ // All restricted permissions start as exempt. If there's an installer for the
+ // package, we will drop this UPGRADE_EXEMPT flag when we receive the
+ // onPackageInstalled() callback and set up the INSTALLER_EXEMPT flags.
+ // UPGRADE_EXEMPT is chosen instead of other flags because it is the same flag
+ // that
+ // was assigned to pre-installed apps in RuntimePermissionsUpgradeController,
+ // and to
+ // apps with missing permission state.
+ // This way we make sure both pre-installed apps, and apps updated/installed
+ // after
+ // a rollback snapshot is taken, can get the allowlist for permissions that
+ // won't be
+ // allowlisted otherwise.
+ newFlags = newFlags or PermissionFlags.UPGRADE_EXEMPT
+ true
+ } else {
+ wasExempt
+ }
+ newFlags =
+ if (permission.isHardRestricted && !isExempt) {
+ newFlags or PermissionFlags.RESTRICTION_REVOKED
+ } else {
+ newFlags andInv PermissionFlags.RESTRICTION_REVOKED
+ }
+ newFlags =
+ if (permission.isSoftRestricted && !isExempt) {
+ newFlags or PermissionFlags.SOFT_RESTRICTED
+ } else {
+ newFlags andInv PermissionFlags.SOFT_RESTRICTED
+ }
setPermissionFlags(appId, userId, permissionName, newFlags)
} else {
- Slog.e(LOG_TAG, "Unknown protection level ${permission.protectionLevel}" +
- "for permission ${permission.name} while evaluating permission state" +
- "for appId $appId and userId $userId")
+ Slog.e(
+ LOG_TAG,
+ "Unknown protection level ${permission.protectionLevel}" +
+ "for permission ${permission.name} while evaluating permission state" +
+ "for appId $appId and userId $userId"
+ )
}
}
@@ -985,7 +1108,7 @@
forEachPackageInAppId(appId) {
implicitPermissions += it.androidPackage!!.implicitPermissions
}
- implicitPermissions.forEachIndexed implicitPermissions@ { _, implicitPermissionName ->
+ implicitPermissions.forEachIndexed implicitPermissions@{ _, implicitPermissionName ->
val implicitPermission = newState.systemState.permissions[implicitPermissionName]
checkNotNull(implicitPermission) {
"Unknown implicit permission $implicitPermissionName in split permissions"
@@ -999,10 +1122,11 @@
if (!isNewPermission) {
return@implicitPermissions
}
- val sourcePermissions = newState.externalState
- .implicitToSourcePermissions[implicitPermissionName] ?: return@implicitPermissions
+ val sourcePermissions =
+ newState.externalState.implicitToSourcePermissions[implicitPermissionName]
+ ?: return@implicitPermissions
var newFlags = getPermissionFlags(appId, userId, implicitPermissionName)
- sourcePermissions.forEachIndexed sourcePermissions@ { _, sourcePermissionName ->
+ sourcePermissions.forEachIndexed sourcePermissions@{ _, sourcePermissionName ->
val sourcePermission = newState.systemState.permissions[sourcePermissionName]
checkNotNull(sourcePermission) {
"Unknown source permission $sourcePermissionName in split permissions"
@@ -1032,11 +1156,14 @@
permissionName: String
): Boolean {
for (compatibilityPermission in CompatibilityPermissionInfo.COMPAT_PERMS) {
- if (compatibilityPermission.name == permissionName &&
- androidPackage.targetSdkVersion < compatibilityPermission.sdkVersion) {
+ if (
+ compatibilityPermission.name == permissionName &&
+ androidPackage.targetSdkVersion < compatibilityPermission.sdkVersion
+ ) {
Slog.i(
- LOG_TAG, "Auto-granting $permissionName to old package" +
- " ${androidPackage.packageName}"
+ LOG_TAG,
+ "Auto-granting $permissionName to old package" +
+ " ${androidPackage.packageName}"
)
return true
}
@@ -1058,15 +1185,23 @@
// and the defining package still trusts the old certificate for permissions
// - or it shares the above relationships with the system package
val packageSigningDetails = packageState.androidPackage!!.signingDetails
- val sourceSigningDetails = newState.externalState
- .packageStates[permission.packageName]?.androidPackage?.signingDetails
- val platformSigningDetails = newState.externalState
- .packageStates[PLATFORM_PACKAGE_NAME]!!.androidPackage!!.signingDetails
- return sourceSigningDetails?.hasCommonSignerWithCapability(packageSigningDetails,
- SigningDetails.CertCapabilities.PERMISSION) == true ||
+ val sourceSigningDetails =
+ newState.externalState.packageStates[permission.packageName]
+ ?.androidPackage
+ ?.signingDetails
+ val platformSigningDetails =
+ newState.externalState.packageStates[PLATFORM_PACKAGE_NAME]!!
+ .androidPackage!!
+ .signingDetails
+ return sourceSigningDetails?.hasCommonSignerWithCapability(
+ packageSigningDetails,
+ SigningDetails.CertCapabilities.PERMISSION
+ ) == true ||
packageSigningDetails.hasAncestorOrSelf(platformSigningDetails) ||
- platformSigningDetails.checkCapability(packageSigningDetails,
- SigningDetails.CertCapabilities.PERMISSION)
+ platformSigningDetails.checkCapability(
+ packageSigningDetails,
+ SigningDetails.CertCapabilities.PERMISSION
+ )
}
private fun MutateStateScope.checkPrivilegedPermissionAllowlist(
@@ -1082,8 +1217,9 @@
if (!(packageState.isSystem && packageState.isPrivileged)) {
return true
}
- if (permission.packageName !in
- newState.externalState.privilegedPermissionAllowlistPackages) {
+ if (
+ permission.packageName !in newState.externalState.privilegedPermissionAllowlistPackages
+ ) {
return true
}
val allowlistState = getPrivilegedPermissionAllowlistState(packageState, permission.name)
@@ -1099,13 +1235,15 @@
// Apps that are in updated apex's do not need to be allowlisted
if (!packageState.isApkInUpdatedApex) {
Slog.w(
- LOG_TAG, "Privileged permission ${permission.name} for package" +
- " ${packageState.packageName} (${packageState.path}) not in" +
- " privileged permission allowlist"
+ LOG_TAG,
+ "Privileged permission ${permission.name} for package" +
+ " ${packageState.packageName} (${packageState.path}) not in" +
+ " privileged permission allowlist"
)
if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE) {
- privilegedPermissionAllowlistViolations += "${packageState.packageName}" +
- " (${packageState.path}): ${permission.name}"
+ privilegedPermissionAllowlistViolations +=
+ "${packageState.packageName}" +
+ " (${packageState.path}): ${permission.name}"
}
}
}
@@ -1124,32 +1262,40 @@
val apexModuleName = packageState.apexModuleName
val packageName = packageState.packageName
return when {
- packageState.isVendor -> permissionAllowlist.getVendorPrivilegedAppAllowlistState(
- packageName, permissionName
- )
- packageState.isProduct -> permissionAllowlist.getProductPrivilegedAppAllowlistState(
- packageName, permissionName
- )
+ packageState.isVendor ->
+ permissionAllowlist.getVendorPrivilegedAppAllowlistState(
+ packageName,
+ permissionName
+ )
+ packageState.isProduct ->
+ permissionAllowlist.getProductPrivilegedAppAllowlistState(
+ packageName,
+ permissionName
+ )
packageState.isSystemExt ->
permissionAllowlist.getSystemExtPrivilegedAppAllowlistState(
- packageName, permissionName
+ packageName,
+ permissionName
)
apexModuleName != null -> {
- val nonApexAllowlistState = permissionAllowlist.getPrivilegedAppAllowlistState(
- packageName, permissionName
- )
+ val nonApexAllowlistState =
+ permissionAllowlist.getPrivilegedAppAllowlistState(packageName, permissionName)
if (nonApexAllowlistState != null) {
// TODO(andreionea): Remove check as soon as all apk-in-apex
// permission allowlists are migrated.
Slog.w(
- LOG_TAG, "Package $packageName is an APK in APEX but has permission" +
+ LOG_TAG,
+ "Package $packageName is an APK in APEX but has permission" +
" allowlist on the system image, please bundle the allowlist in the" +
" $apexModuleName APEX instead"
)
}
- val apexAllowlistState = permissionAllowlist.getApexPrivilegedAppAllowlistState(
- apexModuleName, packageName, permissionName
- )
+ val apexAllowlistState =
+ permissionAllowlist.getApexPrivilegedAppAllowlistState(
+ apexModuleName,
+ packageName,
+ permissionName
+ )
apexAllowlistState ?: nonApexAllowlistState
}
else -> permissionAllowlist.getPrivilegedAppAllowlistState(packageName, permissionName)
@@ -1208,18 +1354,19 @@
val knownPackages = newState.externalState.knownPackages
val packageName = packageState.packageName
if ((permission.isPrivileged || permission.isOem) && packageState.isSystem) {
- val shouldGrant = if (packageState.isUpdatedSystemApp) {
- // For updated system applications, a privileged/oem permission
- // is granted only if it had been defined by the original application.
- val disabledSystemPackageState = newState.externalState
- .disabledSystemPackageStates[packageState.packageName]
- val disabledSystemPackage = disabledSystemPackageState?.androidPackage
- disabledSystemPackage != null &&
- permission.name in disabledSystemPackage.requestedPermissions &&
- shouldGrantPrivilegedOrOemPermission(disabledSystemPackageState, permission)
- } else {
- shouldGrantPrivilegedOrOemPermission(packageState, permission)
- }
+ val shouldGrant =
+ if (packageState.isUpdatedSystemApp) {
+ // For updated system applications, a privileged/oem permission
+ // is granted only if it had been defined by the original application.
+ val disabledSystemPackageState =
+ newState.externalState.disabledSystemPackageStates[packageState.packageName]
+ val disabledSystemPackage = disabledSystemPackageState?.androidPackage
+ disabledSystemPackage != null &&
+ permission.name in disabledSystemPackage.requestedPermissions &&
+ shouldGrantPrivilegedOrOemPermission(disabledSystemPackageState, permission)
+ } else {
+ shouldGrantPrivilegedOrOemPermission(packageState, permission)
+ }
if (shouldGrant) {
return true
}
@@ -1230,16 +1377,18 @@
// we still want to blindly grant it to old apps.
return true
}
- if (permission.isInstaller && (
- packageName in knownPackages[KnownPackages.PACKAGE_INSTALLER]!! ||
- packageName in knownPackages[KnownPackages.PACKAGE_PERMISSION_CONTROLLER]!!
- )) {
+ if (
+ permission.isInstaller &&
+ (packageName in knownPackages[KnownPackages.PACKAGE_INSTALLER]!! ||
+ packageName in knownPackages[KnownPackages.PACKAGE_PERMISSION_CONTROLLER]!!)
+ ) {
// If this permission is to be granted to the system installer and
// this app is an installer or permission controller, then it gets the permission.
return true
}
- if (permission.isVerifier &&
- packageName in knownPackages[KnownPackages.PACKAGE_VERIFIER]!!) {
+ if (
+ permission.isVerifier && packageName in knownPackages[KnownPackages.PACKAGE_VERIFIER]!!
+ ) {
// If this permission is to be granted to the system verifier and
// this app is a verifier, then it gets the permission.
return true
@@ -1248,53 +1397,67 @@
// Any pre-installed system app is allowed to get this permission.
return true
}
- if (permission.isKnownSigner &&
- androidPackage.signingDetails.hasAncestorOrSelfWithDigest(permission.knownCerts)) {
+ if (
+ permission.isKnownSigner &&
+ androidPackage.signingDetails.hasAncestorOrSelfWithDigest(permission.knownCerts)
+ ) {
// If the permission is to be granted to a known signer then check if any of this
// app's signing certificates are in the trusted certificate digest Set.
return true
}
- if (permission.isSetup &&
- packageName in knownPackages[KnownPackages.PACKAGE_SETUP_WIZARD]!!) {
+ if (
+ permission.isSetup && packageName in knownPackages[KnownPackages.PACKAGE_SETUP_WIZARD]!!
+ ) {
// If this permission is to be granted to the system setup wizard and
// this app is a setup wizard, then it gets the permission.
return true
}
- if (permission.isSystemTextClassifier &&
- packageName in knownPackages[KnownPackages.PACKAGE_SYSTEM_TEXT_CLASSIFIER]!!) {
+ if (
+ permission.isSystemTextClassifier &&
+ packageName in knownPackages[KnownPackages.PACKAGE_SYSTEM_TEXT_CLASSIFIER]!!
+ ) {
// Special permissions for the system default text classifier.
return true
}
- if (permission.isConfigurator &&
- packageName in knownPackages[KnownPackages.PACKAGE_CONFIGURATOR]!!) {
+ if (
+ permission.isConfigurator &&
+ packageName in knownPackages[KnownPackages.PACKAGE_CONFIGURATOR]!!
+ ) {
// Special permissions for the device configurator.
return true
}
- if (permission.isIncidentReportApprover &&
- packageName in knownPackages[KnownPackages.PACKAGE_INCIDENT_REPORT_APPROVER]!!) {
+ if (
+ permission.isIncidentReportApprover &&
+ packageName in knownPackages[KnownPackages.PACKAGE_INCIDENT_REPORT_APPROVER]!!
+ ) {
// If this permission is to be granted to the incident report approver and
// this app is the incident report approver, then it gets the permission.
return true
}
- if (permission.isAppPredictor &&
- packageName in knownPackages[KnownPackages.PACKAGE_APP_PREDICTOR]!!) {
+ if (
+ permission.isAppPredictor &&
+ packageName in knownPackages[KnownPackages.PACKAGE_APP_PREDICTOR]!!
+ ) {
// Special permissions for the system app predictor.
return true
}
- if (permission.isCompanion &&
- packageName in knownPackages[KnownPackages.PACKAGE_COMPANION]!!) {
+ if (
+ permission.isCompanion &&
+ packageName in knownPackages[KnownPackages.PACKAGE_COMPANION]!!
+ ) {
// Special permissions for the system companion device manager.
return true
}
- if (permission.isRetailDemo &&
- packageName in knownPackages[KnownPackages.PACKAGE_RETAIL_DEMO]!!) {
+ if (
+ permission.isRetailDemo &&
+ packageName in knownPackages[KnownPackages.PACKAGE_RETAIL_DEMO]!!
+ ) {
// Special permission granted only to the OEM specified retail demo app.
// Note that the original code was passing app ID as UID, so this behavior is kept
// unchanged.
return true
}
- if (permission.isRecents &&
- packageName in knownPackages[KnownPackages.PACKAGE_RECENTS]!!) {
+ if (permission.isRecents && packageName in knownPackages[KnownPackages.PACKAGE_RECENTS]!!) {
// Special permission for the recents app.
return true
}
@@ -1319,9 +1482,10 @@
// flag.
if (packageState.isVendor && !permission.isVendorPrivileged) {
Slog.w(
- LOG_TAG, "Permission $permissionName cannot be granted to privileged" +
- " vendor app $packageName because it isn't a vendorPrivileged" +
- " permission"
+ LOG_TAG,
+ "Permission $permissionName cannot be granted to privileged" +
+ " vendor app $packageName because it isn't a vendorPrivileged" +
+ " permission"
)
return false
}
@@ -1330,8 +1494,11 @@
}
permission.isOem -> {
if (packageState.isOem) {
- val allowlistState = newState.externalState.permissionAllowlist
- .getOemAppAllowlistState(packageName, permissionName)
+ val allowlistState =
+ newState.externalState.permissionAllowlist.getOemAppAllowlistState(
+ packageName,
+ permissionName
+ )
checkNotNull(allowlistState) {
"OEM permission $permissionName requested by package" +
" $packageName must be explicitly declared granted or not"
@@ -1358,13 +1525,18 @@
val appId = externalState.packageStates[packageName]?.appId ?: continue
newState.userStates.forEachIndexed { _, userId, _ ->
evaluatePermissionState(
- appId, userId, Manifest.permission.PACKAGE_USAGE_STATS, null
+ appId,
+ userId,
+ Manifest.permission.PACKAGE_USAGE_STATS,
+ null
)
}
}
if (!privilegedPermissionAllowlistViolations.isEmpty()) {
- throw IllegalStateException("Signature|privileged permissions not in privileged" +
- " permission allowlist: $privilegedPermissionAllowlistViolations")
+ throw IllegalStateException(
+ "Signature|privileged permissions not in privileged" +
+ " permission allowlist: $privilegedPermissionAllowlistViolations"
+ )
}
}
@@ -1389,10 +1561,14 @@
fun GetStateScope.findPermissionTree(permissionName: String): Permission? =
state.systemState.permissionTrees.firstNotNullOfOrNullIndexed {
- _, permissionTreeName, permissionTree ->
- if (permissionName.startsWith(permissionTreeName) &&
- permissionName.length > permissionTreeName.length &&
- permissionName[permissionTreeName.length] == '.') {
+ _,
+ permissionTreeName,
+ permissionTree ->
+ if (
+ permissionName.startsWith(permissionTreeName) &&
+ permissionName.length > permissionTreeName.length &&
+ permissionName[permissionTreeName.length] == '.'
+ ) {
permissionTree
} else {
null
@@ -1403,15 +1579,11 @@
newState.mutateSystemState().mutatePermissionTrees()[permission.name] = permission
}
- /**
- * returns all permission group definitions available in the system
- */
+ /** returns all permission group definitions available in the system */
fun GetStateScope.getPermissionGroups(): IndexedMap<String, PermissionGroupInfo> =
state.systemState.permissionGroups
- /**
- * returns all permission definitions available in the system
- */
+ /** returns all permission definitions available in the system */
fun GetStateScope.getPermissions(): IndexedMap<String, Permission> =
state.systemState.permissions
@@ -1430,11 +1602,8 @@
fun GetStateScope.getUidPermissionFlags(appId: Int, userId: Int): IndexedMap<String, Int>? =
state.userStates[userId]?.appIdPermissionFlags?.get(appId)
- fun GetStateScope.getPermissionFlags(
- appId: Int,
- userId: Int,
- permissionName: String
- ): Int = getPermissionFlags(state, appId, userId, permissionName)
+ fun GetStateScope.getPermissionFlags(appId: Int, userId: Int, permissionName: String): Int =
+ getPermissionFlags(state, appId, userId, permissionName)
private fun MutateStateScope.getOldStatePermissionFlags(
appId: Int,
@@ -1465,8 +1634,10 @@
flagMask: Int,
flagValues: Int
): Boolean {
- val oldFlags = newState.userStates[userId]!!.appIdPermissionFlags[appId]
- .getWithDefault(permissionName, 0)
+ val oldFlags =
+ newState.userStates[userId]!!
+ .appIdPermissionFlags[appId]
+ .getWithDefault(permissionName, 0)
val newFlags = (oldFlags andInv flagMask) or (flagValues and flagMask)
if (oldFlags == newFlags) {
return false
@@ -1517,38 +1688,37 @@
private const val PLATFORM_PACKAGE_NAME = "android"
// A set of permissions that we don't want to revoke when they are no longer implicit.
- private val RETAIN_IMPLICIT_FLAGS_PERMISSIONS = indexedSetOf(
- Manifest.permission.ACCESS_MEDIA_LOCATION,
- Manifest.permission.ACTIVITY_RECOGNITION,
- Manifest.permission.READ_MEDIA_AUDIO,
- Manifest.permission.READ_MEDIA_IMAGES,
- Manifest.permission.READ_MEDIA_VIDEO,
- )
+ private val RETAIN_IMPLICIT_FLAGS_PERMISSIONS =
+ indexedSetOf(
+ Manifest.permission.ACCESS_MEDIA_LOCATION,
+ Manifest.permission.ACTIVITY_RECOGNITION,
+ Manifest.permission.READ_MEDIA_AUDIO,
+ Manifest.permission.READ_MEDIA_IMAGES,
+ Manifest.permission.READ_MEDIA_VIDEO,
+ )
- private val NEARBY_DEVICES_PERMISSIONS = indexedSetOf(
- Manifest.permission.BLUETOOTH_ADVERTISE,
- Manifest.permission.BLUETOOTH_CONNECT,
- Manifest.permission.BLUETOOTH_SCAN,
- Manifest.permission.NEARBY_WIFI_DEVICES
- )
+ private val NEARBY_DEVICES_PERMISSIONS =
+ indexedSetOf(
+ Manifest.permission.BLUETOOTH_ADVERTISE,
+ Manifest.permission.BLUETOOTH_CONNECT,
+ Manifest.permission.BLUETOOTH_SCAN,
+ Manifest.permission.NEARBY_WIFI_DEVICES
+ )
- private val NOTIFICATIONS_PERMISSIONS = indexedSetOf(
- Manifest.permission.POST_NOTIFICATIONS
- )
+ private val NOTIFICATIONS_PERMISSIONS = indexedSetOf(Manifest.permission.POST_NOTIFICATIONS)
- private val STORAGE_AND_MEDIA_PERMISSIONS = indexedSetOf(
- Manifest.permission.READ_EXTERNAL_STORAGE,
- Manifest.permission.WRITE_EXTERNAL_STORAGE,
- Manifest.permission.READ_MEDIA_AUDIO,
- Manifest.permission.READ_MEDIA_VIDEO,
- Manifest.permission.READ_MEDIA_IMAGES,
- Manifest.permission.ACCESS_MEDIA_LOCATION,
- Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED
- )
+ private val STORAGE_AND_MEDIA_PERMISSIONS =
+ indexedSetOf(
+ Manifest.permission.READ_EXTERNAL_STORAGE,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE,
+ Manifest.permission.READ_MEDIA_AUDIO,
+ Manifest.permission.READ_MEDIA_VIDEO,
+ Manifest.permission.READ_MEDIA_IMAGES,
+ Manifest.permission.ACCESS_MEDIA_LOCATION,
+ Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED
+ )
- /**
- * Mask for all permission flags that can be set by the user
- */
+ /** Mask for all permission flags that can be set by the user */
private const val USER_SETTABLE_MASK =
PermissionFlags.USER_SET or
PermissionFlags.USER_FIXED or
@@ -1558,16 +1728,14 @@
PermissionFlags.USER_SELECTED
/**
- * Mask for all permission flags that imply we shouldn't automatically modify the
- * permission grant state.
+ * Mask for all permission flags that imply we shouldn't automatically modify the permission
+ * grant state.
*/
private const val SYSTEM_OR_POLICY_FIXED_MASK =
PermissionFlags.SYSTEM_FIXED or PermissionFlags.POLICY_FIXED
}
- /**
- * Listener for permission flags changes.
- */
+ /** Listener for permission flags changes. */
abstract class OnPermissionFlagsChangedListener {
/**
* Called when a permission flags change has been made to the upcoming new state.
diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionUpgrade.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionUpgrade.kt
index b644d8f..edacda0 100644
--- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionUpgrade.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionUpgrade.kt
@@ -32,7 +32,6 @@
* Upgrade the package permissions, if needed.
*
* @param version package version
- *
* @see [com.android.server.permission.access.util.PackageVersionMigration.getVersion]
*/
fun MutateStateScope.upgradePackageState(
@@ -43,7 +42,8 @@
val packageName = packageState.packageName
if (version <= 3) {
Slog.v(
- LOG_TAG, "Allowlisting and upgrading background location permission for " +
+ LOG_TAG,
+ "Allowlisting and upgrading background location permission for " +
"package: $packageName, version: $version, user:$userId"
)
allowlistRestrictedPermissions(packageState, userId)
@@ -51,7 +51,8 @@
}
if (version <= 10) {
Slog.v(
- LOG_TAG, "Upgrading access media location permission for package: $packageName" +
+ LOG_TAG,
+ "Upgrading access media location permission for package: $packageName" +
", version: $version, user: $userId"
)
upgradeAccessMediaLocationPermission(packageState, userId)
@@ -59,7 +60,8 @@
// TODO Enable isAtLeastT check, when moving subsystem to mainline.
if (version <= 12 /*&& SdkLevel.isAtLeastT()*/) {
Slog.v(
- LOG_TAG, "Upgrading scoped permissions for package: $packageName" +
+ LOG_TAG,
+ "Upgrading scoped permissions for package: $packageName" +
", version: $version, user: $userId"
)
upgradeAuralVisualMediaPermissions(packageState, userId)
@@ -67,7 +69,8 @@
// TODO Enable isAtLeastU check, when moving subsystem to mainline.
if (version <= 14 /*&& SdkLevel.isAtLeastU()*/) {
Slog.v(
- LOG_TAG, "Upgrading visual media permission for package: $packageName" +
+ LOG_TAG,
+ "Upgrading visual media permission for package: $packageName" +
", version: $version, user: $userId"
)
upgradeUserSelectedVisualMediaPermission(packageState, userId)
@@ -84,8 +87,11 @@
if (permissionName in LEGACY_RESTRICTED_PERMISSIONS) {
with(policy) {
updatePermissionFlags(
- packageState.appId, userId, permissionName,
- PermissionFlags.UPGRADE_EXEMPT, PermissionFlags.UPGRADE_EXEMPT
+ packageState.appId,
+ userId,
+ permissionName,
+ PermissionFlags.UPGRADE_EXEMPT,
+ PermissionFlags.UPGRADE_EXEMPT
)
}
}
@@ -96,21 +102,27 @@
packageState: PackageState,
userId: Int
) {
- if (Manifest.permission.ACCESS_BACKGROUND_LOCATION in
- packageState.androidPackage!!.requestedPermissions) {
+ if (
+ Manifest.permission.ACCESS_BACKGROUND_LOCATION in
+ packageState.androidPackage!!.requestedPermissions
+ ) {
val appId = packageState.appId
- val accessFineLocationFlags = with(policy) {
- getPermissionFlags(appId, userId, Manifest.permission.ACCESS_FINE_LOCATION)
- }
- val accessCoarseLocationFlags = with(policy) {
- getPermissionFlags(appId, userId, Manifest.permission.ACCESS_COARSE_LOCATION)
- }
+ val accessFineLocationFlags =
+ with(policy) {
+ getPermissionFlags(appId, userId, Manifest.permission.ACCESS_FINE_LOCATION)
+ }
+ val accessCoarseLocationFlags =
+ with(policy) {
+ getPermissionFlags(appId, userId, Manifest.permission.ACCESS_COARSE_LOCATION)
+ }
val isForegroundLocationGranted =
PermissionFlags.isAppOpGranted(accessFineLocationFlags) ||
PermissionFlags.isAppOpGranted(accessCoarseLocationFlags)
if (isForegroundLocationGranted) {
grantRuntimePermission(
- packageState, userId, Manifest.permission.ACCESS_BACKGROUND_LOCATION
+ packageState,
+ userId,
+ Manifest.permission.ACCESS_BACKGROUND_LOCATION
)
}
}
@@ -120,24 +132,29 @@
packageState: PackageState,
userId: Int
) {
- if (Manifest.permission.ACCESS_MEDIA_LOCATION in
- packageState.androidPackage!!.requestedPermissions) {
- val flags = with(policy) {
- getPermissionFlags(
- packageState.appId, userId, Manifest.permission.READ_EXTERNAL_STORAGE
- )
- }
+ if (
+ Manifest.permission.ACCESS_MEDIA_LOCATION in
+ packageState.androidPackage!!.requestedPermissions
+ ) {
+ val flags =
+ with(policy) {
+ getPermissionFlags(
+ packageState.appId,
+ userId,
+ Manifest.permission.READ_EXTERNAL_STORAGE
+ )
+ }
if (PermissionFlags.isAppOpGranted(flags)) {
grantRuntimePermission(
- packageState, userId, Manifest.permission.ACCESS_MEDIA_LOCATION
+ packageState,
+ userId,
+ Manifest.permission.ACCESS_MEDIA_LOCATION
)
}
}
}
- /**
- * Upgrade permissions based on storage permissions grant
- */
+ /** Upgrade permissions based on storage permissions grant */
private fun MutateStateScope.upgradeAuralVisualMediaPermissions(
packageState: PackageState,
userId: Int
@@ -147,15 +164,15 @@
return
}
val requestedPermissionNames = androidPackage.requestedPermissions
- val isStorageUserGranted = STORAGE_PERMISSIONS.anyIndexed { _, permissionName ->
- if (permissionName !in requestedPermissionNames) {
- return@anyIndexed false
+ val isStorageUserGranted =
+ STORAGE_PERMISSIONS.anyIndexed { _, permissionName ->
+ if (permissionName !in requestedPermissionNames) {
+ return@anyIndexed false
+ }
+ val flags =
+ with(policy) { getPermissionFlags(packageState.appId, userId, permissionName) }
+ PermissionFlags.isAppOpGranted(flags) && flags.hasBits(PermissionFlags.USER_SET)
}
- val flags = with(policy) {
- getPermissionFlags(packageState.appId, userId, permissionName)
- }
- PermissionFlags.isAppOpGranted(flags) && flags.hasBits(PermissionFlags.USER_SET)
- }
if (isStorageUserGranted) {
AURAL_VISUAL_MEDIA_PERMISSIONS.forEachIndexed { _, permissionName ->
if (permissionName in requestedPermissionNames) {
@@ -165,9 +182,7 @@
}
}
- /**
- * Upgrade permission based on the grant in [Manifest.permission_group.READ_MEDIA_VISUAL]
- */
+ /** Upgrade permission based on the grant in [Manifest.permission_group.READ_MEDIA_VISUAL] */
private fun MutateStateScope.upgradeUserSelectedVisualMediaPermission(
packageState: PackageState,
userId: Int
@@ -177,19 +192,21 @@
return
}
val requestedPermissionNames = androidPackage.requestedPermissions
- val isVisualMediaUserGranted = VISUAL_MEDIA_PERMISSIONS.anyIndexed { _, permissionName ->
- if (permissionName !in requestedPermissionNames) {
- return@anyIndexed false
+ val isVisualMediaUserGranted =
+ VISUAL_MEDIA_PERMISSIONS.anyIndexed { _, permissionName ->
+ if (permissionName !in requestedPermissionNames) {
+ return@anyIndexed false
+ }
+ val flags =
+ with(policy) { getPermissionFlags(packageState.appId, userId, permissionName) }
+ PermissionFlags.isAppOpGranted(flags) && flags.hasBits(PermissionFlags.USER_SET)
}
- val flags = with(policy) {
- getPermissionFlags(packageState.appId, userId, permissionName)
- }
- PermissionFlags.isAppOpGranted(flags) && flags.hasBits(PermissionFlags.USER_SET)
- }
if (isVisualMediaUserGranted) {
if (Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED in requestedPermissionNames) {
grantRuntimePermission(
- packageState, userId, Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED
+ packageState,
+ userId,
+ Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED
)
}
}
@@ -201,7 +218,8 @@
permissionName: String
) {
Slog.v(
- LOG_TAG, "Granting runtime permission for package: ${packageState.packageName}, " +
+ LOG_TAG,
+ "Granting runtime permission for package: ${packageState.packageName}, " +
"permission: $permissionName, userId: $userId"
)
val permission = newState.systemState.permissions[permissionName]!!
@@ -220,13 +238,13 @@
}
flags = flags or PermissionFlags.RUNTIME_GRANTED
- flags = flags andInv (
- PermissionFlags.APP_OP_REVOKED or
- PermissionFlags.IMPLICIT or
- PermissionFlags.LEGACY_GRANTED or
- PermissionFlags.HIBERNATION or
- PermissionFlags.ONE_TIME
- )
+ flags =
+ flags andInv
+ (PermissionFlags.APP_OP_REVOKED or
+ PermissionFlags.IMPLICIT or
+ PermissionFlags.LEGACY_GRANTED or
+ PermissionFlags.HIBERNATION or
+ PermissionFlags.ONE_TIME)
with(policy) { setPermissionFlags(appId, userId, permissionName, flags) }
}
@@ -234,39 +252,45 @@
private val LOG_TAG = AppIdPermissionUpgrade::class.java.simpleName
private const val MASK_ANY_FIXED =
- PermissionFlags.USER_SET or PermissionFlags.USER_FIXED or
- PermissionFlags.POLICY_FIXED or PermissionFlags.SYSTEM_FIXED
+ PermissionFlags.USER_SET or
+ PermissionFlags.USER_FIXED or
+ PermissionFlags.POLICY_FIXED or
+ PermissionFlags.SYSTEM_FIXED
- private val LEGACY_RESTRICTED_PERMISSIONS = indexedSetOf(
- Manifest.permission.ACCESS_BACKGROUND_LOCATION,
- Manifest.permission.READ_EXTERNAL_STORAGE,
- Manifest.permission.WRITE_EXTERNAL_STORAGE,
- Manifest.permission.SEND_SMS,
- Manifest.permission.RECEIVE_SMS,
- Manifest.permission.RECEIVE_WAP_PUSH,
- Manifest.permission.RECEIVE_MMS,
- Manifest.permission.READ_CELL_BROADCASTS,
- Manifest.permission.READ_CALL_LOG,
- Manifest.permission.WRITE_CALL_LOG,
- Manifest.permission.PROCESS_OUTGOING_CALLS
- )
+ private val LEGACY_RESTRICTED_PERMISSIONS =
+ indexedSetOf(
+ Manifest.permission.ACCESS_BACKGROUND_LOCATION,
+ Manifest.permission.READ_EXTERNAL_STORAGE,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE,
+ Manifest.permission.SEND_SMS,
+ Manifest.permission.RECEIVE_SMS,
+ Manifest.permission.RECEIVE_WAP_PUSH,
+ Manifest.permission.RECEIVE_MMS,
+ Manifest.permission.READ_CELL_BROADCASTS,
+ Manifest.permission.READ_CALL_LOG,
+ Manifest.permission.WRITE_CALL_LOG,
+ Manifest.permission.PROCESS_OUTGOING_CALLS
+ )
- private val STORAGE_PERMISSIONS = indexedSetOf(
- Manifest.permission.READ_EXTERNAL_STORAGE,
- Manifest.permission.WRITE_EXTERNAL_STORAGE
- )
- private val AURAL_VISUAL_MEDIA_PERMISSIONS = indexedSetOf(
- Manifest.permission.READ_MEDIA_AUDIO,
- Manifest.permission.READ_MEDIA_IMAGES,
- Manifest.permission.READ_MEDIA_VIDEO,
- Manifest.permission.ACCESS_MEDIA_LOCATION,
- Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED
- )
+ private val STORAGE_PERMISSIONS =
+ indexedSetOf(
+ Manifest.permission.READ_EXTERNAL_STORAGE,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE
+ )
+ private val AURAL_VISUAL_MEDIA_PERMISSIONS =
+ indexedSetOf(
+ Manifest.permission.READ_MEDIA_AUDIO,
+ Manifest.permission.READ_MEDIA_IMAGES,
+ Manifest.permission.READ_MEDIA_VIDEO,
+ Manifest.permission.ACCESS_MEDIA_LOCATION,
+ Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED
+ )
// Visual media permissions in T
- private val VISUAL_MEDIA_PERMISSIONS = indexedSetOf(
- Manifest.permission.READ_MEDIA_IMAGES,
- Manifest.permission.READ_MEDIA_VIDEO,
- Manifest.permission.ACCESS_MEDIA_LOCATION
- )
+ private val VISUAL_MEDIA_PERMISSIONS =
+ indexedSetOf(
+ Manifest.permission.READ_MEDIA_IMAGES,
+ Manifest.permission.READ_MEDIA_VIDEO,
+ Manifest.permission.ACCESS_MEDIA_LOCATION
+ )
}
}
diff --git a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPersistence.kt b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPersistence.kt
index 37a4a90..1bee356 100644
--- a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPersistence.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPersistence.kt
@@ -135,9 +135,7 @@
) {
tag(TAG_DEVICE) {
attributeInterned(ATTR_ID, deviceId)
- permissionFlags.forEachIndexed { _, name, flags ->
- serializePermission(name, flags)
- }
+ permissionFlags.forEachIndexed { _, name, flags -> serializePermission(name, flags) }
}
}
@@ -145,11 +143,12 @@
tag(TAG_PERMISSION) {
attributeInterned(ATTR_NAME, name)
// Never serialize one-time permissions as granted.
- val serializedFlags = if (flags.hasBits(PermissionFlags.ONE_TIME)) {
- flags andInv PermissionFlags.RUNTIME_GRANTED
- } else {
- flags
- }
+ val serializedFlags =
+ if (flags.hasBits(PermissionFlags.ONE_TIME)) {
+ flags andInv PermissionFlags.RUNTIME_GRANTED
+ } else {
+ flags
+ }
attributeInt(ATTR_FLAGS, serializedFlags)
}
}
@@ -166,4 +165,4 @@
private const val ATTR_NAME = "name"
private const val ATTR_FLAGS = "flags"
}
-}
\ No newline at end of file
+}
diff --git a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
index 240585c..15a5859 100644
--- a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
@@ -53,8 +53,8 @@
override fun MutateStateScope.onAppIdRemoved(appId: Int) {
newState.userStates.forEachIndexed { userStateIndex, _, userState ->
if (appId in userState.appIdDevicePermissionFlags) {
- newState.mutateUserStateAt(userStateIndex)
- .mutateAppIdDevicePermissionFlags() -= appId
+ newState.mutateUserStateAt(userStateIndex).mutateAppIdDevicePermissionFlags() -=
+ appId
}
}
}
@@ -85,10 +85,10 @@
appId: Int,
userId: Int
) {
- resetPermissionStates(packageName, userId)
+ resetRuntimePermissions(packageName, userId)
}
- private fun MutateStateScope.resetPermissionStates(packageName: String, userId: Int) {
+ fun MutateStateScope.resetRuntimePermissions(packageName: String, userId: Int) {
// It's okay to skip resetting permissions for packages that are removed,
// because their states will be trimmed in onPackageRemoved()/onAppIdRemoved()
val packageState = newState.externalState.packageStates[packageName] ?: return
@@ -96,10 +96,11 @@
val appId = packageState.appId
val appIdPermissionFlags = newState.userStates[userId]!!.appIdDevicePermissionFlags
androidPackage.requestedPermissions.forEach { permissionName ->
- val isRequestedByOtherPackages = anyPackageInAppId(appId) {
- it.packageName != packageName &&
- permissionName in it.androidPackage!!.requestedPermissions
- }
+ val isRequestedByOtherPackages =
+ anyPackageInAppId(appId) {
+ it.packageName != packageName &&
+ permissionName in it.androidPackage!!.requestedPermissions
+ }
if (isRequestedByOtherPackages) {
return@forEach
}
@@ -116,7 +117,9 @@
}
newState.userStates.forEachIndexed { _, userId, userState ->
userState.appIdDevicePermissionFlags[appId]?.forEachReversedIndexed {
- _, deviceId, permissionFlags ->
+ _,
+ deviceId,
+ permissionFlags ->
permissionFlags.forEachReversedIndexed { _, permissionName, _ ->
if (permissionName !in requestedPermissions) {
setPermissionFlags(appId, deviceId, userId, permissionName, 0)
@@ -166,11 +169,17 @@
userId: Int,
permissionName: String
): Int {
- val flags = state.userStates[userId]?.appIdDevicePermissionFlags?.get(appId)?.get(deviceId)
- ?.getWithDefault(permissionName, 0) ?: 0
+ val flags =
+ state.userStates[userId]
+ ?.appIdDevicePermissionFlags
+ ?.get(appId)
+ ?.get(deviceId)
+ ?.getWithDefault(permissionName, 0)
+ ?: 0
if (PermissionManager.DEBUG_DEVICE_PERMISSIONS) {
Slog.i(
- LOG_TAG, "getPermissionFlags: appId=$appId, userId=$userId," +
+ LOG_TAG,
+ "getPermissionFlags: appId=$appId, userId=$userId," +
" deviceId=$deviceId, permissionName=$permissionName," +
" flags=${PermissionFlags.toString(flags)}"
)
@@ -186,7 +195,12 @@
flags: Int
): Boolean =
updatePermissionFlags(
- appId, deviceId, userId, permissionName, PermissionFlags.MASK_ALL, flags
+ appId,
+ deviceId,
+ userId,
+ permissionName,
+ PermissionFlags.MASK_ALL,
+ flags
)
private fun MutateStateScope.updatePermissionFlags(
@@ -201,20 +215,23 @@
Slog.w(LOG_TAG, "$permissionName is not a device aware permission.")
return false
}
- val oldFlags = newState.userStates[userId]!!.appIdDevicePermissionFlags[appId]
- ?.get(deviceId).getWithDefault(permissionName, 0)
+ val oldFlags =
+ newState.userStates[userId]!!
+ .appIdDevicePermissionFlags[appId]
+ ?.get(deviceId)
+ .getWithDefault(permissionName, 0)
val newFlags = (oldFlags andInv flagMask) or (flagValues and flagMask)
if (oldFlags == newFlags) {
return false
}
val appIdDevicePermissionFlags =
newState.mutateUserState(userId)!!.mutateAppIdDevicePermissionFlags()
- val devicePermissionFlags = appIdDevicePermissionFlags.mutateOrPut(appId) {
- MutableIndexedReferenceMap()
- }
+ val devicePermissionFlags =
+ appIdDevicePermissionFlags.mutateOrPut(appId) { MutableIndexedReferenceMap() }
if (PermissionManager.DEBUG_DEVICE_PERMISSIONS) {
Slog.i(
- LOG_TAG, "setPermissionFlags(): appId=$appId, userId=$userId," +
+ LOG_TAG,
+ "setPermissionFlags(): appId=$appId, userId=$userId," +
" deviceId=$deviceId, permissionName=$permissionName," +
" newFlags=${PermissionFlags.toString(newFlags)}"
)
@@ -229,40 +246,39 @@
}
listeners.forEachIndexed { _, it ->
it.onDevicePermissionFlagsChanged(
- appId, userId, deviceId, permissionName, oldFlags, newFlags
+ appId,
+ userId,
+ deviceId,
+ permissionName,
+ oldFlags,
+ newFlags
)
}
return true
}
fun addOnPermissionFlagsChangedListener(listener: OnDevicePermissionFlagsChangedListener) {
- synchronized(listenersLock) {
- listeners = listeners + listener
- }
+ synchronized(listenersLock) { listeners = listeners + listener }
}
fun removeOnPermissionFlagsChangedListener(listener: OnDevicePermissionFlagsChangedListener) {
- synchronized(listenersLock) {
- listeners = listeners - listener
- }
+ synchronized(listenersLock) { listeners = listeners - listener }
}
private fun isDeviceAwarePermission(permissionName: String): Boolean =
- DEVICE_AWARE_PERMISSIONS.contains(permissionName)
+ DEVICE_AWARE_PERMISSIONS.contains(permissionName)
companion object {
private val LOG_TAG = DevicePermissionPolicy::class.java.simpleName
- /**
- * These permissions are supported for virtual devices.
- */
+ /** These permissions are supported for virtual devices. */
// TODO: b/298661870 - Use new API to get the list of device aware permissions.
val DEVICE_AWARE_PERMISSIONS = emptySet<String>()
}
/**
- * TODO: b/289355341 - implement listener for permission changes
- * Listener for permission flags changes.
+ * TODO: b/289355341 - implement listener for permission changes Listener for permission flags
+ * changes.
*/
abstract class OnDevicePermissionFlagsChangedListener {
/**
@@ -288,4 +304,4 @@
*/
abstract fun onStateMutated()
}
-}
\ No newline at end of file
+}
diff --git a/services/permission/java/com/android/server/permission/access/permission/Permission.kt b/services/permission/java/com/android/server/permission/access/permission/Permission.kt
index c7fe1a9..aa56928 100644
--- a/services/permission/java/com/android/server/permission/access/permission/Permission.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/Permission.kt
@@ -26,8 +26,7 @@
val isReconciled: Boolean,
val type: Int,
val appId: Int,
- @Suppress("ArrayInDataClass")
- val gids: IntArray = EmptyArray.INT,
+ @Suppress("ArrayInDataClass") val gids: IntArray = EmptyArray.INT,
val areGidsPerUser: Boolean = false
) {
inline val name: String
@@ -43,8 +42,7 @@
get() = type == TYPE_DYNAMIC
inline val protectionLevel: Int
- @Suppress("DEPRECATION")
- get() = permissionInfo.protectionLevel
+ @Suppress("DEPRECATION") get() = permissionInfo.protectionLevel
inline val protection: Int
get() = permissionInfo.protection
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt
index 550d148..b9d89c2 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt
@@ -32,15 +32,12 @@
*
* The old binary permission state is now tracked by multiple `*_GRANTED` and `*_REVOKED` flags, so
* that:
- *
* - With [INSTALL_GRANTED] and [INSTALL_REVOKED], we can now get rid of the old per-package
* `areInstallPermissionsFixed` attribute and correctly track it per-permission, finally fixing
* edge cases during module rollbacks.
- *
* - With [LEGACY_GRANTED] and [IMPLICIT_GRANTED], we can now ensure that legacy permissions and
* implicit permissions split from non-runtime permissions are never revoked, without checking
* split permissions and package state everywhere slowly and in slightly different ways.
- *
* - With [RESTRICTION_REVOKED], we can now get rid of the error-prone logic about revoking and
* potentially re-granting permissions upon restriction state changes.
*
@@ -55,9 +52,7 @@
* don't have any effect on the binary permission state.
*/
object PermissionFlags {
- /**
- * Permission flag for a normal permission that is granted at package installation.
- */
+ /** Permission flag for a normal permission that is granted at package installation. */
const val INSTALL_GRANTED = 1 shl 0
/**
@@ -97,8 +92,8 @@
/**
* Permission flag for a runtime permission whose state is set by the user.
*
- * For example, this flag may be set when the permission is allowed by the user in the
- * request permission dialog, or managed in the permission settings.
+ * For example, this flag may be set when the permission is allowed by the user in the request
+ * permission dialog, or managed in the permission settings.
*
* @see PackageManager.FLAG_PERMISSION_USER_SET
*/
@@ -290,8 +285,8 @@
/**
* Permission flag for a runtime permission that is selected by the user.
*
- * For example, this flag may be set when one of the coarse/fine location accuracies is
- * selected by the user.
+ * For example, this flag may be set when one of the coarse/fine location accuracies is selected
+ * by the user.
*
* This flag is informational and managed by PermissionController.
*
@@ -299,28 +294,37 @@
*/
const val USER_SELECTED = 1 shl 23
- /**
- * Mask for all permission flags.
- */
+ /** Mask for all permission flags. */
const val MASK_ALL = 0.inv()
- /**
- * Mask for all permission flags that may be applied to a runtime permission.
- */
- const val MASK_RUNTIME = ROLE or RUNTIME_GRANTED or USER_SET or USER_FIXED or POLICY_FIXED or
- SYSTEM_FIXED or PREGRANT or LEGACY_GRANTED or IMPLICIT_GRANTED or IMPLICIT or
- USER_SENSITIVE_WHEN_GRANTED or USER_SENSITIVE_WHEN_REVOKED or INSTALLER_EXEMPT or
- SYSTEM_EXEMPT or UPGRADE_EXEMPT or RESTRICTION_REVOKED or SOFT_RESTRICTED or
- APP_OP_REVOKED or ONE_TIME or HIBERNATION or USER_SELECTED
+ /** Mask for all permission flags that may be applied to a runtime permission. */
+ const val MASK_RUNTIME =
+ ROLE or
+ RUNTIME_GRANTED or
+ USER_SET or
+ USER_FIXED or
+ POLICY_FIXED or
+ SYSTEM_FIXED or
+ PREGRANT or
+ LEGACY_GRANTED or
+ IMPLICIT_GRANTED or
+ IMPLICIT or
+ USER_SENSITIVE_WHEN_GRANTED or
+ USER_SENSITIVE_WHEN_REVOKED or
+ INSTALLER_EXEMPT or
+ SYSTEM_EXEMPT or
+ UPGRADE_EXEMPT or
+ RESTRICTION_REVOKED or
+ SOFT_RESTRICTED or
+ APP_OP_REVOKED or
+ ONE_TIME or
+ HIBERNATION or
+ USER_SELECTED
- /**
- * Mask for all permission flags about permission exemption.
- */
+ /** Mask for all permission flags about permission exemption. */
const val MASK_EXEMPT = INSTALLER_EXEMPT or SYSTEM_EXEMPT or UPGRADE_EXEMPT
- /**
- * Mask for all permission flags about permission restriction.
- */
+ /** Mask for all permission flags about permission restriction. */
const val MASK_RESTRICTED = RESTRICTION_REVOKED or SOFT_RESTRICTED
fun isPermissionGranted(flags: Int): Boolean {
@@ -363,11 +367,13 @@
apiFlags = apiFlags or PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT
}
if (flags.hasBits(IMPLICIT)) {
- apiFlags = apiFlags or if (flags.hasBits(LEGACY_GRANTED)) {
- PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED
- } else {
- PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED
- }
+ apiFlags =
+ apiFlags or
+ if (flags.hasBits(LEGACY_GRANTED)) {
+ PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED
+ } else {
+ PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED
+ }
}
if (flags.hasBits(USER_SENSITIVE_WHEN_GRANTED)) {
apiFlags = apiFlags or PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED
@@ -440,8 +446,10 @@
}
flags = flags or (oldFlags and LEGACY_GRANTED)
flags = flags or (oldFlags and IMPLICIT_GRANTED)
- if (apiFlags.hasBits(PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) ||
- apiFlags.hasBits(PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED)) {
+ if (
+ apiFlags.hasBits(PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) ||
+ apiFlags.hasBits(PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED)
+ ) {
flags = flags or IMPLICIT
}
if (apiFlags.hasBits(PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED)) {
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index bb24d51..ab3d78c 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -41,10 +41,10 @@
import android.os.ServiceManager
import android.os.UserHandle
import android.os.UserManager
-import android.permission.flags.Flags
import android.permission.IOnPermissionsChangeListener
import android.permission.PermissionControllerManager
import android.permission.PermissionManager
+import android.permission.flags.Flags
import android.provider.Settings
import android.util.ArrayMap
import android.util.ArraySet
@@ -88,28 +88,25 @@
import com.android.server.pm.UserManagerService
import com.android.server.pm.parsing.pkg.AndroidPackageUtils
import com.android.server.pm.permission.LegacyPermission
-import com.android.server.pm.permission.Permission as LegacyPermission2
import com.android.server.pm.permission.LegacyPermissionSettings
import com.android.server.pm.permission.LegacyPermissionState
+import com.android.server.pm.permission.Permission as LegacyPermission2
import com.android.server.pm.permission.PermissionManagerServiceInterface
import com.android.server.pm.permission.PermissionManagerServiceInternal
import com.android.server.pm.pkg.AndroidPackage
import com.android.server.pm.pkg.PackageState
import com.android.server.policy.SoftRestrictedPermissionPolicy
-import libcore.util.EmptyArray
import java.io.FileDescriptor
import java.io.PrintWriter
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ExecutionException
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
+import libcore.util.EmptyArray
-/**
- * Modern implementation of [PermissionManagerServiceInterface].
- */
-class PermissionService(
- private val service: AccessCheckingService
-) : PermissionManagerServiceInterface {
+/** Modern implementation of [PermissionManagerServiceInterface]. */
+class PermissionService(private val service: AccessCheckingService) :
+ PermissionManagerServiceInterface {
private val policy =
service.getSchemePolicy(UidUri.SCHEME, PermissionUri.SCHEME) as AppIdPermissionPolicy
@@ -131,8 +128,7 @@
private lateinit var onPermissionFlagsChangedListener: OnPermissionFlagsChangedListener
private val storageVolumeLock = Any()
- @GuardedBy("storageVolumeLock")
- private val mountedStorageVolumes = ArraySet<String?>()
+ @GuardedBy("storageVolumeLock") private val mountedStorageVolumes = ArraySet<String?>()
@GuardedBy("storageVolumeLock")
private val storageVolumePackageNames = ArrayMap<String?, MutableList<String>>()
@@ -144,8 +140,8 @@
* A permission backup might contain apps that are not installed. In this case we delay the
* restoration until the app is installed.
*
- * This array (`userId -> noDelayedBackupLeft`) is `true` for all the users where
- * there is **no more** delayed backup left.
+ * This array (`userId -> noDelayedBackupLeft`) is `true` for all the users where there is **no
+ * more** delayed backup left.
*/
private val isDelayedPermissionBackupFinished = SparseBooleanArray()
@@ -154,9 +150,10 @@
packageManagerInternal = LocalServices.getService(PackageManagerInternal::class.java)
packageManagerLocal =
LocalManagerRegistry.getManagerOrThrow(PackageManagerLocal::class.java)
- platformCompat = IPlatformCompat.Stub.asInterface(
- ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)
- )
+ platformCompat =
+ IPlatformCompat.Stub.asInterface(
+ ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)
+ )
systemConfig = SystemConfig.getInstance()
userManagerInternal = LocalServices.getService(UserManagerInternal::class.java)
userManagerService = UserManagerService.getInstance()
@@ -166,8 +163,8 @@
PackageManager.invalidatePackageInfoCache()
PermissionManager.disablePackageNamePermissionCache()
- handlerThread = ServiceThread(LOG_TAG, Process.THREAD_PRIORITY_BACKGROUND, true)
- .apply { start() }
+ handlerThread =
+ ServiceThread(LOG_TAG, Process.THREAD_PRIORITY_BACKGROUND, true).apply { start() }
handler = Handler(handlerThread.looper)
onPermissionsChangeListeners = OnPermissionsChangeListeners(FgThread.get().looper)
onPermissionFlagsChangedListener = OnPermissionFlagsChangedListener()
@@ -181,9 +178,7 @@
return emptyList()
}
- val permissionGroups = service.getState {
- with(policy) { getPermissionGroups() }
- }
+ val permissionGroups = service.getState { with(policy) { getPermissionGroups() } }
return permissionGroups.mapNotNullIndexedTo(ArrayList()) { _, _, permissionGroup ->
if (snapshot.isPackageVisibleToUid(permissionGroup.packageName, callingUid)) {
@@ -206,9 +201,9 @@
return null
}
- permissionGroup = service.getState {
- with(policy) { getPermissionGroups()[permissionGroupName] }
- } ?: return null
+ permissionGroup =
+ service.getState { with(policy) { getPermissionGroups()[permissionGroupName] } }
+ ?: return null
if (!snapshot.isPackageVisibleToUid(permissionGroup.packageName, callingUid)) {
return null
@@ -242,29 +237,28 @@
return null
}
- permission = service.getState {
- with(policy) { getPermissions()[permissionName] }
- } ?: return null
+ permission =
+ service.getState { with(policy) { getPermissions()[permissionName] } }
+ ?: return null
if (!snapshot.isPackageVisibleToUid(permission.packageName, callingUid)) {
return null
}
val opPackage = snapshot.getPackageState(opPackageName)?.androidPackage
- targetSdkVersion = when {
- // System sees all flags.
- isRootOrSystemOrShellUid(callingUid) -> Build.VERSION_CODES.CUR_DEVELOPMENT
- opPackage != null -> opPackage.targetSdkVersion
- else -> Build.VERSION_CODES.CUR_DEVELOPMENT
- }
+ targetSdkVersion =
+ when {
+ // System sees all flags.
+ isRootOrSystemOrShellUid(callingUid) -> Build.VERSION_CODES.CUR_DEVELOPMENT
+ opPackage != null -> opPackage.targetSdkVersion
+ else -> Build.VERSION_CODES.CUR_DEVELOPMENT
+ }
}
return permission.generatePermissionInfo(flags, targetSdkVersion)
}
- /**
- * Generate a new [PermissionInfo] from [Permission] and adjust it accordingly.
- */
+ /** Generate a new [PermissionInfo] from [Permission] and adjust it accordingly. */
private fun Permission.generatePermissionInfo(
flags: Int,
targetSdkVersion: Int = Build.VERSION_CODES.CUR_DEVELOPMENT
@@ -296,22 +290,27 @@
return null
}
- val permissions = service.getState {
- if (permissionGroupName != null) {
- val permissionGroup =
- with(policy) { getPermissionGroups()[permissionGroupName] } ?: return null
+ val permissions =
+ service.getState {
+ if (permissionGroupName != null) {
+ val permissionGroup =
+ with(policy) { getPermissionGroups()[permissionGroupName] }
+ ?: return null
- if (!snapshot.isPackageVisibleToUid(permissionGroup.packageName, callingUid)) {
- return null
+ if (
+ !snapshot.isPackageVisibleToUid(permissionGroup.packageName, callingUid)
+ ) {
+ return null
+ }
}
+
+ with(policy) { getPermissions() }
}
- with(policy) { getPermissions() }
- }
-
return permissions.mapNotNullIndexedTo(ArrayList()) { _, _, permission ->
- if (permission.groupName == permissionGroupName &&
- snapshot.isPackageVisibleToUid(permission.packageName, callingUid)
+ if (
+ permission.groupName == permissionGroupName &&
+ snapshot.isPackageVisibleToUid(permission.packageName, callingUid)
) {
permission.generatePermissionInfo(flags)
} else {
@@ -334,9 +333,7 @@
private inline fun getPermissionsWithProtectionOrProtectionFlags(
predicate: (Permission) -> Boolean
): List<PermissionInfo> {
- val permissions = service.getState {
- with(policy) { getPermissions() }
- }
+ val permissions = service.getState { with(policy) { getPermissions() } }
return permissions.mapNotNullIndexedTo(ArrayList()) { _, _, permission ->
if (predicate(permission)) {
@@ -348,18 +345,16 @@
}
override fun getPermissionGids(permissionName: String, userId: Int): IntArray {
- val permission = service.getState {
- with(policy) { getPermissions()[permissionName] }
- } ?: return EmptyArray.INT
+ val permission =
+ service.getState { with(policy) { getPermissions()[permissionName] } }
+ ?: return EmptyArray.INT
return permission.getGidsForUser(userId)
}
override fun getInstalledPermissions(packageName: String): Set<String> {
requireNotNull(packageName) { "packageName cannot be null" }
- val permissions = service.getState {
- with(policy) { getPermissions() }
- }
+ val permissions = service.getState { with(policy) { getPermissions() } }
return permissions.mapNotNullIndexedTo(ArraySet()) { _, _, permission ->
if (permission.packageName == packageName) {
@@ -398,9 +393,8 @@
permissionInfo.protectionLevel =
PermissionInfo.fixProtectionLevel(permissionInfo.protectionLevel)
- val newPermission = Permission(
- permissionInfo, true, Permission.TYPE_DYNAMIC, permissionTree.appId
- )
+ val newPermission =
+ Permission(permissionInfo, true, Permission.TYPE_DYNAMIC, permissionTree.appId)
with(policy) { addPermission(newPermission, !async) }
}
@@ -431,7 +425,7 @@
val callingUid = Binder.getCallingUid()
val permissionTree = with(policy) { findPermissionTree(permissionName) }
if (permissionTree != null && permissionTree.appId == UserHandle.getAppId(callingUid)) {
- return permissionTree
+ return permissionTree
}
throw SecurityException(
@@ -447,8 +441,9 @@
// if that plus the size of 'info' would exceed our stated maximum.
if (permissionTree.appId != Process.SYSTEM_UID) {
val permissionTreeFootprint = calculatePermissionTreeFootprint(permissionTree)
- if (permissionTreeFootprint + permissionInfo.calculateFootprint() >
- MAX_PERMISSION_TREE_FOOTPRINT
+ if (
+ permissionTreeFootprint + permissionInfo.calculateFootprint() >
+ MAX_PERMISSION_TREE_FOOTPRINT
) {
throw SecurityException("Permission tree size cap exceeded")
}
@@ -483,14 +478,16 @@
packageManagerInternal.getPackageStateInternal(androidPackage.packageName)
if (packageState == null) {
Slog.e(
- LOG_TAG, "checkUidPermission: PackageState not found for AndroidPackage" +
+ LOG_TAG,
+ "checkUidPermission: PackageState not found for AndroidPackage" +
" $androidPackage"
)
return PackageManager.PERMISSION_DENIED
}
- val isPermissionGranted = service.getState {
- isPermissionGranted(packageState, userId, permissionName, deviceId)
- }
+ val isPermissionGranted =
+ service.getState {
+ isPermissionGranted(packageState, userId, permissionName, deviceId)
+ }
return if (isPermissionGranted) {
PackageManager.PERMISSION_GRANTED
} else {
@@ -505,9 +502,7 @@
}
}
- /**
- * Internal implementation that should only be called by [checkUidPermission].
- */
+ /** Internal implementation that should only be called by [checkUidPermission]. */
private fun isSystemUidPermissionGranted(uid: Int, permissionName: String): Boolean {
val uidPermissions = systemConfig.systemPermissions[uid] ?: return false
if (permissionName in uidPermissions) {
@@ -532,12 +527,14 @@
return PackageManager.PERMISSION_DENIED
}
- val packageState = packageManagerLocal.withFilteredSnapshot(Binder.getCallingUid(), userId)
- .use { it.getPackageState(packageName) } ?: return PackageManager.PERMISSION_DENIED
+ val packageState =
+ packageManagerLocal.withFilteredSnapshot(Binder.getCallingUid(), userId).use {
+ it.getPackageState(packageName)
+ }
+ ?: return PackageManager.PERMISSION_DENIED
- val isPermissionGranted = service.getState {
- isPermissionGranted(packageState, userId, permissionName, deviceId)
- }
+ val isPermissionGranted =
+ service.getState { isPermissionGranted(packageState, userId, permissionName, deviceId) }
return if (isPermissionGranted) {
PackageManager.PERMISSION_GRANTED
} else {
@@ -566,8 +563,15 @@
}
val fullerPermissionName = FULLER_PERMISSIONS[permissionName]
- if (fullerPermissionName != null &&
- isSinglePermissionGranted(appId, userId, isInstantApp, fullerPermissionName, deviceId)
+ if (
+ fullerPermissionName != null &&
+ isSinglePermissionGranted(
+ appId,
+ userId,
+ isInstantApp,
+ fullerPermissionName,
+ deviceId
+ )
) {
return true
}
@@ -575,9 +579,7 @@
return false
}
- /**
- * Internal implementation that should only be called by [isPermissionGranted].
- */
+ /** Internal implementation that should only be called by [isPermissionGranted]. */
private fun GetStateScope.isSinglePermissionGranted(
appId: Int,
userId: Int,
@@ -604,20 +606,27 @@
requireNotNull(packageName) { "packageName cannot be null" }
Preconditions.checkArgumentNonnegative(userId, "userId")
- val packageState = packageManagerLocal.withUnfilteredSnapshot()
- .use { it.getPackageState(packageName) }
+ val packageState =
+ packageManagerLocal.withUnfilteredSnapshot().use { it.getPackageState(packageName) }
if (packageState == null) {
Slog.w(LOG_TAG, "getGrantedPermissions: Unknown package $packageName")
return emptySet()
}
service.getState {
- val permissionFlags = with(policy) { getUidPermissionFlags(packageState.appId, userId) }
- ?: return emptySet()
+ val permissionFlags =
+ with(policy) { getUidPermissionFlags(packageState.appId, userId) }
+ ?: return emptySet()
return permissionFlags.mapNotNullIndexedTo(ArraySet()) { _, permissionName, _ ->
- if (isPermissionGranted(
- packageState, userId, permissionName, Context.DEVICE_ID_DEFAULT)) {
+ if (
+ isPermissionGranted(
+ packageState,
+ userId,
+ permissionName,
+ Context.DEVICE_ID_DEFAULT
+ )
+ ) {
permissionName
} else {
null
@@ -635,8 +644,8 @@
// permission state is not found, now we always return at least global GIDs. This is
// more consistent with the pre-S-refactor behavior. This is also because we are now
// actively trimming the per-UID objects when empty.
- val permissionFlags = with(policy) { getUidPermissionFlags(appId, userId) }
- ?: return globalGids.copyOf()
+ val permissionFlags =
+ with(policy) { getUidPermissionFlags(appId, userId) } ?: return globalGids.copyOf()
val gids = GrowingIntArray.wrap(globalGids)
permissionFlags.forEachIndexed { _, permissionName, flags ->
@@ -644,8 +653,8 @@
return@forEachIndexed
}
- val permission = with(policy) { getPermissions()[permissionName] }
- ?: return@forEachIndexed
+ val permission =
+ with(policy) { getPermissions()[permissionName] } ?: return@forEachIndexed
val permissionGids = permission.getGidsForUser(userId)
if (permissionGids.isEmpty()) {
return@forEachIndexed
@@ -662,9 +671,7 @@
deviceId: Int,
userId: Int
) {
- setRuntimePermissionGranted(
- packageName, userId, permissionName, deviceId, isGranted = true
- )
+ setRuntimePermissionGranted(packageName, userId, permissionName, deviceId, isGranted = true)
}
override fun revokeRuntimePermission(
@@ -675,7 +682,12 @@
reason: String?
) {
setRuntimePermissionGranted(
- packageName, userId, permissionName, deviceId, isGranted = false, revokeReason = reason
+ packageName,
+ userId,
+ permissionName,
+ deviceId,
+ isGranted = false,
+ revokeReason = reason
)
}
@@ -684,8 +696,12 @@
userId: Int
) {
setRuntimePermissionGranted(
- packageName, userId, Manifest.permission.POST_NOTIFICATIONS, Context.DEVICE_ID_DEFAULT,
- isGranted = false, skipKillUid = true
+ packageName,
+ userId,
+ Manifest.permission.POST_NOTIFICATIONS,
+ Context.DEVICE_ID_DEFAULT,
+ isGranted = false,
+ skipKillUid = true
)
}
@@ -704,19 +720,24 @@
) {
val methodName = if (isGranted) "grantRuntimePermission" else "revokeRuntimePermission"
val callingUid = Binder.getCallingUid()
- val isDebugEnabled = if (isGranted) {
- PermissionManager.DEBUG_TRACE_GRANTS
- } else {
- PermissionManager.DEBUG_TRACE_PERMISSION_UPDATES
- }
- if (isDebugEnabled &&
- PermissionManager.shouldTraceGrant(packageName, permissionName, userId)) {
+ val isDebugEnabled =
+ if (isGranted) {
+ PermissionManager.DEBUG_TRACE_GRANTS
+ } else {
+ PermissionManager.DEBUG_TRACE_PERMISSION_UPDATES
+ }
+ if (
+ isDebugEnabled &&
+ PermissionManager.shouldTraceGrant(packageName, permissionName, userId)
+ ) {
val callingUidName = packageManagerInternal.getNameForUid(callingUid)
Slog.i(
- LOG_TAG, "$methodName(packageName = $packageName," +
+ LOG_TAG,
+ "$methodName(packageName = $packageName," +
" permissionName = $permissionName" +
(if (isGranted) "" else "skipKillUid = $skipKillUid, reason = $revokeReason") +
- ", userId = $userId," + " callingUid = $callingUidName ($callingUid))",
+ ", userId = $userId," +
+ " callingUid = $callingUidName ($callingUid))",
RuntimeException()
)
}
@@ -727,23 +748,31 @@
}
enforceCallingOrSelfCrossUserPermission(
- userId, enforceFullPermission = true, enforceShellRestriction = true, methodName
+ userId,
+ enforceFullPermission = true,
+ enforceShellRestriction = true,
+ methodName
)
- val enforcedPermissionName = if (isGranted) {
- Manifest.permission.GRANT_RUNTIME_PERMISSIONS
- } else {
- Manifest.permission.REVOKE_RUNTIME_PERMISSIONS
- }
+ val enforcedPermissionName =
+ if (isGranted) {
+ Manifest.permission.GRANT_RUNTIME_PERMISSIONS
+ } else {
+ Manifest.permission.REVOKE_RUNTIME_PERMISSIONS
+ }
context.enforceCallingOrSelfPermission(enforcedPermissionName, methodName)
val packageState: PackageState?
- val permissionControllerPackageName = packageManagerInternal.getKnownPackageNames(
- KnownPackages.PACKAGE_PERMISSION_CONTROLLER, UserHandle.USER_SYSTEM
- ).first()
+ val permissionControllerPackageName =
+ packageManagerInternal
+ .getKnownPackageNames(
+ KnownPackages.PACKAGE_PERMISSION_CONTROLLER,
+ UserHandle.USER_SYSTEM
+ )
+ .first()
val permissionControllerPackageState: PackageState?
packageManagerLocal.withUnfilteredSnapshot().use { snapshot ->
- packageState = snapshot.filtered(callingUid, userId)
- .use { it.getPackageState(packageName) }
+ packageState =
+ snapshot.filtered(callingUid, userId).use { it.getPackageState(packageName) }
permissionControllerPackageState =
snapshot.getPackageState(permissionControllerPackageName)
}
@@ -756,11 +785,13 @@
return
}
- val canManageRolePermission = isRootOrSystemUid(callingUid) ||
- UserHandle.getAppId(callingUid) == permissionControllerPackageState!!.appId
- val overridePolicyFixed = context.checkCallingOrSelfPermission(
- Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY
- ) == PackageManager.PERMISSION_GRANTED
+ val canManageRolePermission =
+ isRootOrSystemUid(callingUid) ||
+ UserHandle.getAppId(callingUid) == permissionControllerPackageState!!.appId
+ val overridePolicyFixed =
+ context.checkCallingOrSelfPermission(
+ Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY
+ ) == PackageManager.PERMISSION_GRANTED
service.mutateState {
with(onPermissionFlagsChangedListener) {
@@ -773,8 +804,15 @@
}
setRuntimePermissionGranted(
- packageState, userId, permissionName, deviceId, isGranted, canManageRolePermission,
- overridePolicyFixed, reportError = true, methodName
+ packageState,
+ userId,
+ permissionName,
+ deviceId,
+ isGranted,
+ canManageRolePermission,
+ overridePolicyFixed,
+ reportError = true,
+ methodName
)
}
}
@@ -791,8 +829,9 @@
PackageInstaller.SessionParams.PERMISSION_STATE_DENIED -> {}
else -> {
Slog.w(
- LOG_TAG, "setRequestedPermissionStates: Unknown permission state" +
- " $permissionState for permission $permissionName"
+ LOG_TAG,
+ "setRequestedPermissionStates: Unknown permission state" +
+ " $permissionState for permission $permissionName"
)
return@forEachIndexed
}
@@ -800,35 +839,50 @@
if (permissionName !in packageState.androidPackage!!.requestedPermissions) {
return@forEachIndexed
}
- val permission = with(policy) { getPermissions()[permissionName] }
- ?: return@forEachIndexed
+ val permission =
+ with(policy) { getPermissions()[permissionName] } ?: return@forEachIndexed
when {
permission.isDevelopment || permission.isRuntime -> {
- if (permissionState ==
- PackageInstaller.SessionParams.PERMISSION_STATE_GRANTED) {
+ if (
+ permissionState ==
+ PackageInstaller.SessionParams.PERMISSION_STATE_GRANTED
+ ) {
setRuntimePermissionGranted(
- packageState, userId, permissionName, Context.DEVICE_ID_DEFAULT,
- isGranted = true, canManageRolePermission = false,
- overridePolicyFixed = false, reportError = false,
+ packageState,
+ userId,
+ permissionName,
+ Context.DEVICE_ID_DEFAULT,
+ isGranted = true,
+ canManageRolePermission = false,
+ overridePolicyFixed = false,
+ reportError = false,
"setRequestedPermissionStates"
)
updatePermissionFlags(
- packageState.appId, userId, permissionName,
+ packageState.appId,
+ userId,
+ permissionName,
Context.DEVICE_ID_DEFAULT,
PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED or
- PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, 0,
+ PackageManager.FLAG_PERMISSION_REVOKED_COMPAT,
+ 0,
canUpdateSystemFlags = false,
reportErrorForUnknownPermission = false,
- isPermissionRequested = true, "setRequestedPermissionStates",
+ isPermissionRequested = true,
+ "setRequestedPermissionStates",
packageState.packageName
)
}
}
- permission.isAppOp && permissionName in
+ permission.isAppOp &&
+ permissionName in
PackageInstallerService.INSTALLER_CHANGEABLE_APP_OP_PERMISSIONS ->
setAppOpPermissionGranted(
- packageState, userId, permissionName, permissionState ==
- PackageInstaller.SessionParams.PERMISSION_STATE_GRANTED
+ packageState,
+ userId,
+ permissionName,
+ permissionState ==
+ PackageInstaller.SessionParams.PERMISSION_STATE_GRANTED
)
else -> {}
}
@@ -836,9 +890,7 @@
}
}
- /**
- * Set whether a runtime permission is granted, without any validation on caller.
- */
+ /** Set whether a runtime permission is granted, without any validation on caller. */
private fun MutateStateScope.setRuntimePermissionGranted(
packageState: PackageState,
userId: Int,
@@ -876,8 +928,11 @@
// their permissions as always granted
return
}
- if (isGranted && packageState.getUserStateOrDefault(userId).isInstantApp &&
- !permission.isInstant) {
+ if (
+ isGranted &&
+ packageState.getUserStateOrDefault(userId).isInstantApp &&
+ !permission.isInstant
+ ) {
if (reportError) {
throw SecurityException(
"Cannot grant non-instant permission $permissionName to package" +
@@ -913,7 +968,8 @@
if (oldFlags.hasBits(PermissionFlags.SYSTEM_FIXED)) {
if (reportError) {
Slog.e(
- LOG_TAG, "$methodName: Cannot change system fixed permission $permissionName" +
+ LOG_TAG,
+ "$methodName: Cannot change system fixed permission $permissionName" +
" for package $packageName"
)
}
@@ -923,7 +979,8 @@
if (oldFlags.hasBits(PermissionFlags.POLICY_FIXED) && !overridePolicyFixed) {
if (reportError) {
Slog.e(
- LOG_TAG, "$methodName: Cannot change policy fixed permission $permissionName" +
+ LOG_TAG,
+ "$methodName: Cannot change policy fixed permission $permissionName" +
" for package $packageName"
)
}
@@ -933,7 +990,8 @@
if (isGranted && oldFlags.hasBits(PermissionFlags.RESTRICTION_REVOKED)) {
if (reportError) {
Slog.e(
- LOG_TAG, "$methodName: Cannot grant hard-restricted non-exempt permission" +
+ LOG_TAG,
+ "$methodName: Cannot grant hard-restricted non-exempt permission" +
" $permissionName to package $packageName"
)
}
@@ -942,14 +1000,19 @@
if (isGranted && oldFlags.hasBits(PermissionFlags.SOFT_RESTRICTED)) {
// TODO: Refactor SoftRestrictedPermissionPolicy.
- val softRestrictedPermissionPolicy = SoftRestrictedPermissionPolicy.forPermission(
- context, AndroidPackageUtils.generateAppInfoWithoutState(androidPackage),
- androidPackage, UserHandle.of(userId), permissionName
- )
+ val softRestrictedPermissionPolicy =
+ SoftRestrictedPermissionPolicy.forPermission(
+ context,
+ AndroidPackageUtils.generateAppInfoWithoutState(androidPackage),
+ androidPackage,
+ UserHandle.of(userId),
+ permissionName
+ )
if (!softRestrictedPermissionPolicy.mayGrantPermission()) {
if (reportError) {
Slog.e(
- LOG_TAG, "$methodName: Cannot grant soft-restricted non-exempt permission" +
+ LOG_TAG,
+ "$methodName: Cannot grant soft-restricted non-exempt permission" +
" $permissionName to package $packageName"
)
}
@@ -965,15 +1028,17 @@
setPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId, newFlags)
if (permission.isRuntime) {
- val action = if (isGranted) {
- MetricsProto.MetricsEvent.ACTION_PERMISSION_GRANTED
- } else {
- MetricsProto.MetricsEvent.ACTION_PERMISSION_REVOKED
- }
- val log = LogMaker(action).apply {
- setPackageName(packageName)
- addTaggedData(MetricsProto.MetricsEvent.FIELD_PERMISSION, permissionName)
- }
+ val action =
+ if (isGranted) {
+ MetricsProto.MetricsEvent.ACTION_PERMISSION_GRANTED
+ } else {
+ MetricsProto.MetricsEvent.ACTION_PERMISSION_REVOKED
+ }
+ val log =
+ LogMaker(action).apply {
+ setPackageName(packageName)
+ addTaggedData(MetricsProto.MetricsEvent.FIELD_PERMISSION, permissionName)
+ }
metricsLogger.write(log)
}
}
@@ -984,8 +1049,8 @@
permissionName: String,
isGranted: Boolean
) {
- val appOpPolicy = service.getSchemePolicy(UidUri.SCHEME, AppOpUri.SCHEME) as
- AppIdAppOpPolicy
+ val appOpPolicy =
+ service.getSchemePolicy(UidUri.SCHEME, AppOpUri.SCHEME) as AppIdAppOpPolicy
val appOpName = AppOpsManager.permissionToOp(permissionName)!!
val mode = if (isGranted) AppOpsManager.MODE_ALLOWED else AppOpsManager.MODE_ERRORED
with(appOpPolicy) { setAppOpMode(packageState.appId, userId, appOpName, mode) }
@@ -1003,17 +1068,20 @@
}
enforceCallingOrSelfCrossUserPermission(
- userId, enforceFullPermission = true, enforceShellRestriction = false,
+ userId,
+ enforceFullPermission = true,
+ enforceShellRestriction = false,
"getPermissionFlags"
)
enforceCallingOrSelfAnyPermission(
- "getPermissionFlags", Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
+ "getPermissionFlags",
+ Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
Manifest.permission.GET_RUNTIME_PERMISSIONS
)
- val packageState = packageManagerLocal.withFilteredSnapshot()
- .use { it.getPackageState(packageName) }
+ val packageState =
+ packageManagerLocal.withFilteredSnapshot().use { it.getPackageState(packageName) }
if (packageState == null) {
Slog.w(LOG_TAG, "getPermissionFlags: Unknown package $packageName")
return 0
@@ -1045,12 +1113,17 @@
}
enforceCallingOrSelfCrossUserPermission(
- userId, enforceFullPermission = true, enforceShellRestriction = false,
+ userId,
+ enforceFullPermission = true,
+ enforceShellRestriction = false,
"isPermissionRevokedByPolicy"
)
- val packageState = packageManagerLocal.withFilteredSnapshot(Binder.getCallingUid(), userId)
- .use { it.getPackageState(packageName) } ?: return false
+ val packageState =
+ packageManagerLocal.withFilteredSnapshot(Binder.getCallingUid(), userId).use {
+ it.getPackageState(packageName)
+ }
+ ?: return false
service.getState {
if (isPermissionGranted(packageState, userId, permissionName, deviceId)) {
@@ -1069,12 +1142,13 @@
// TODO(b/173235285): Some caller may pass USER_ALL as userId.
// Preconditions.checkArgumentNonnegative(userId, "userId")
- val packageState = packageManagerLocal.withUnfilteredSnapshot()
- .use { it.getPackageState(packageName) } ?: return false
+ val packageState =
+ packageManagerLocal.withUnfilteredSnapshot().use { it.getPackageState(packageName) }
+ ?: return false
- val permissionFlags = service.getState {
- with(policy) { getUidPermissionFlags(packageState.appId, userId) }
- } ?: return false
+ val permissionFlags =
+ service.getState { with(policy) { getUidPermissionFlags(packageState.appId, userId) } }
+ ?: return false
return permissionFlags.anyIndexed { _, _, it -> it.hasBits(REVIEW_REQUIRED_FLAGS) }
}
@@ -1090,13 +1164,18 @@
}
enforceCallingOrSelfCrossUserPermission(
- userId, enforceFullPermission = true, enforceShellRestriction = false,
+ userId,
+ enforceFullPermission = true,
+ enforceShellRestriction = false,
"shouldShowRequestPermissionRationale"
)
val callingUid = Binder.getCallingUid()
- val packageState = packageManagerLocal.withFilteredSnapshot(callingUid, userId)
- .use { it.getPackageState(packageName) } ?: return false
+ val packageState =
+ packageManagerLocal.withFilteredSnapshot(callingUid, userId).use {
+ it.getPackageState(packageName)
+ }
+ ?: return false
val appId = packageState.appId
if (UserHandle.getAppId(callingUid) != appId) {
return false
@@ -1115,17 +1194,24 @@
}
if (permissionName == Manifest.permission.ACCESS_BACKGROUND_LOCATION) {
- val isBackgroundRationaleChangeEnabled = Binder::class.withClearedCallingIdentity {
- try {
- platformCompat.isChangeEnabledByPackageName(
- BACKGROUND_RATIONALE_CHANGE_ID, packageName, userId
- )
- } catch (e: RemoteException) {
- Slog.e(LOG_TAG, "shouldShowRequestPermissionRationale: Unable to check if" +
- " compatibility change is enabled", e)
- false
+ val isBackgroundRationaleChangeEnabled =
+ Binder::class.withClearedCallingIdentity {
+ try {
+ platformCompat.isChangeEnabledByPackageName(
+ BACKGROUND_RATIONALE_CHANGE_ID,
+ packageName,
+ userId
+ )
+ } catch (e: RemoteException) {
+ Slog.e(
+ LOG_TAG,
+ "shouldShowRequestPermissionRationale: Unable to check if" +
+ " compatibility change is enabled",
+ e
+ )
+ false
+ }
}
- }
if (isBackgroundRationaleChangeEnabled) {
return true
}
@@ -1144,20 +1230,30 @@
userId: Int
) {
val callingUid = Binder.getCallingUid()
- if (PermissionManager.DEBUG_TRACE_PERMISSION_UPDATES &&
- PermissionManager.shouldTraceGrant(packageName, permissionName, userId)) {
- val flagMaskString = DebugUtils.flagsToString(
- PackageManager::class.java, "FLAG_PERMISSION_", flagMask.toLong()
- )
- val flagValuesString = DebugUtils.flagsToString(
- PackageManager::class.java, "FLAG_PERMISSION_", flagValues.toLong()
- )
+ if (
+ PermissionManager.DEBUG_TRACE_PERMISSION_UPDATES &&
+ PermissionManager.shouldTraceGrant(packageName, permissionName, userId)
+ ) {
+ val flagMaskString =
+ DebugUtils.flagsToString(
+ PackageManager::class.java,
+ "FLAG_PERMISSION_",
+ flagMask.toLong()
+ )
+ val flagValuesString =
+ DebugUtils.flagsToString(
+ PackageManager::class.java,
+ "FLAG_PERMISSION_",
+ flagValues.toLong()
+ )
val callingUidName = packageManagerInternal.getNameForUid(callingUid)
Slog.i(
- LOG_TAG, "updatePermissionFlags(packageName = $packageName," +
+ LOG_TAG,
+ "updatePermissionFlags(packageName = $packageName," +
" permissionName = $permissionName, flagMask = $flagMaskString," +
" flagValues = $flagValuesString, userId = $userId," +
- " callingUid = $callingUidName ($callingUid))", RuntimeException()
+ " callingUid = $callingUidName ($callingUid))",
+ RuntimeException()
)
}
@@ -1167,11 +1263,14 @@
}
enforceCallingOrSelfCrossUserPermission(
- userId, enforceFullPermission = true, enforceShellRestriction = true,
+ userId,
+ enforceFullPermission = true,
+ enforceShellRestriction = true,
"updatePermissionFlags"
)
enforceCallingOrSelfAnyPermission(
- "updatePermissionFlags", Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
+ "updatePermissionFlags",
+ Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
Manifest.permission.REVOKE_RUNTIME_PERMISSIONS
)
@@ -1208,8 +1307,10 @@
// Different from the old implementation, which returns when package doesn't exist but
// throws when package exists but isn't visible, we now return in both cases to avoid
// leaking the package existence.
- if (androidPackage == null ||
- packageManagerInternal.filterAppAccess(packageName, callingUid, userId, false)) {
+ if (
+ androidPackage == null ||
+ packageManagerInternal.filterAppAccess(packageName, callingUid, userId, false)
+ ) {
Slog.w(LOG_TAG, "updatePermissionFlags: Unknown package $packageName")
return
}
@@ -1219,26 +1320,35 @@
// permissions.
val canUpdateSystemFlags = isRootOrSystemUid(callingUid)
- val isPermissionRequested = if (permissionName in androidPackage.requestedPermissions) {
- // Fast path, the current package has requested the permission.
- true
- } else {
- // Slow path, go through all shared user packages.
- val sharedUserPackageNames =
- packageManagerInternal.getSharedUserPackagesForPackage(packageName, userId)
- sharedUserPackageNames.any { sharedUserPackageName ->
- val sharedUserPackage = packageManagerInternal.getPackage(sharedUserPackageName)
- sharedUserPackage != null &&
- permissionName in sharedUserPackage.requestedPermissions
+ val isPermissionRequested =
+ if (permissionName in androidPackage.requestedPermissions) {
+ // Fast path, the current package has requested the permission.
+ true
+ } else {
+ // Slow path, go through all shared user packages.
+ val sharedUserPackageNames =
+ packageManagerInternal.getSharedUserPackagesForPackage(packageName, userId)
+ sharedUserPackageNames.any { sharedUserPackageName ->
+ val sharedUserPackage = packageManagerInternal.getPackage(sharedUserPackageName)
+ sharedUserPackage != null &&
+ permissionName in sharedUserPackage.requestedPermissions
+ }
}
- }
val appId = packageState.appId
service.mutateState {
updatePermissionFlags(
- appId, userId, permissionName, deviceId, flagMask, flagValues, canUpdateSystemFlags,
- reportErrorForUnknownPermission = true, isPermissionRequested,
- "updatePermissionFlags", packageName
+ appId,
+ userId,
+ permissionName,
+ deviceId,
+ flagMask,
+ flagValues,
+ canUpdateSystemFlags,
+ reportErrorForUnknownPermission = true,
+ isPermissionRequested,
+ "updatePermissionFlags",
+ packageName
)
}
}
@@ -1246,17 +1356,25 @@
override fun updatePermissionFlagsForAllApps(flagMask: Int, flagValues: Int, userId: Int) {
val callingUid = Binder.getCallingUid()
if (PermissionManager.DEBUG_TRACE_PERMISSION_UPDATES) {
- val flagMaskString = DebugUtils.flagsToString(
- PackageManager::class.java, "FLAG_PERMISSION_", flagMask.toLong()
- )
- val flagValuesString = DebugUtils.flagsToString(
- PackageManager::class.java, "FLAG_PERMISSION_", flagValues.toLong()
- )
+ val flagMaskString =
+ DebugUtils.flagsToString(
+ PackageManager::class.java,
+ "FLAG_PERMISSION_",
+ flagMask.toLong()
+ )
+ val flagValuesString =
+ DebugUtils.flagsToString(
+ PackageManager::class.java,
+ "FLAG_PERMISSION_",
+ flagValues.toLong()
+ )
val callingUidName = packageManagerInternal.getNameForUid(callingUid)
Slog.i(
- LOG_TAG, "updatePermissionFlagsForAllApps(flagMask = $flagMaskString," +
+ LOG_TAG,
+ "updatePermissionFlagsForAllApps(flagMask = $flagMaskString," +
" flagValues = $flagValuesString, userId = $userId," +
- " callingUid = $callingUidName ($callingUid))", RuntimeException()
+ " callingUid = $callingUidName ($callingUid))",
+ RuntimeException()
)
}
@@ -1266,11 +1384,14 @@
}
enforceCallingOrSelfCrossUserPermission(
- userId, enforceFullPermission = true, enforceShellRestriction = true,
+ userId,
+ enforceFullPermission = true,
+ enforceShellRestriction = true,
"updatePermissionFlagsForAllApps"
)
enforceCallingOrSelfAnyPermission(
- "updatePermissionFlagsForAllApps", Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
+ "updatePermissionFlagsForAllApps",
+ Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
Manifest.permission.REVOKE_RUNTIME_PERMISSIONS
)
@@ -1278,26 +1399,30 @@
// flag, we now properly sanitize all flags as in updatePermissionFlags().
val canUpdateSystemFlags = isRootOrSystemUid(callingUid)
- val packageStates = packageManagerLocal.withUnfilteredSnapshot()
- .use { it.packageStates }
+ val packageStates = packageManagerLocal.withUnfilteredSnapshot().use { it.packageStates }
service.mutateState {
packageStates.forEach { (packageName, packageState) ->
val androidPackage = packageState.androidPackage ?: return@forEach
androidPackage.requestedPermissions.forEach { permissionName ->
updatePermissionFlags(
- packageState.appId, userId, permissionName, Context.DEVICE_ID_DEFAULT,
- flagMask, flagValues, canUpdateSystemFlags,
+ packageState.appId,
+ userId,
+ permissionName,
+ Context.DEVICE_ID_DEFAULT,
+ flagMask,
+ flagValues,
+ canUpdateSystemFlags,
reportErrorForUnknownPermission = false,
- isPermissionRequested = true, "updatePermissionFlagsForAllApps", packageName
+ isPermissionRequested = true,
+ "updatePermissionFlagsForAllApps",
+ packageName
)
}
}
}
}
- /**
- * Update flags for a permission, without any validation on caller.
- */
+ /** Update flags for a permission, without any validation on caller. */
private fun MutateStateScope.updatePermissionFlags(
appId: Int,
userId: Int,
@@ -1311,20 +1436,19 @@
methodName: String,
packageName: String
) {
- @Suppress("NAME_SHADOWING")
- var flagMask = flagMask
- @Suppress("NAME_SHADOWING")
- var flagValues = flagValues
+ @Suppress("NAME_SHADOWING") var flagMask = flagMask
+ @Suppress("NAME_SHADOWING") var flagValues = flagValues
// Only the system can change these flags and nothing else.
if (!canUpdateSystemFlags) {
// Different from the old implementation, which allowed non-system UIDs to remove (but
// not add) permission restriction flags, we now consistently ignore them altogether.
- val ignoredMask = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED or
- PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT or
- PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT or
- PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT or
- PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT or
- PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION
+ val ignoredMask =
+ PackageManager.FLAG_PERMISSION_SYSTEM_FIXED or
+ PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT or
+ PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT or
+ PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT or
+ PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT or
+ PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION
flagMask = flagMask andInv ignoredMask
flagValues = flagValues andInv ignoredMask
}
@@ -1340,7 +1464,8 @@
val oldFlags = getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId)
if (!isPermissionRequested && oldFlags == 0) {
Slog.w(
- LOG_TAG, "$methodName: Permission $permissionName isn't requested by package" +
+ LOG_TAG,
+ "$methodName: Permission $permissionName isn't requested by package" +
" $packageName"
)
return
@@ -1365,21 +1490,29 @@
}
enforceCallingOrSelfCrossUserPermission(
- userId, enforceFullPermission = false, enforceShellRestriction = false,
+ userId,
+ enforceFullPermission = false,
+ enforceShellRestriction = false,
"getAllowlistedRestrictedPermissions"
)
val callingUid = Binder.getCallingUid()
- val packageState = packageManagerLocal.withFilteredSnapshot(callingUid, userId)
- .use { it.getPackageState(packageName) } ?: return null
+ val packageState =
+ packageManagerLocal.withFilteredSnapshot(callingUid, userId).use {
+ it.getPackageState(packageName)
+ }
+ ?: return null
val androidPackage = packageState.androidPackage ?: return null
- val isCallerPrivileged = context.checkCallingOrSelfPermission(
- Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS
- ) == PackageManager.PERMISSION_GRANTED
+ val isCallerPrivileged =
+ context.checkCallingOrSelfPermission(
+ Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS
+ ) == PackageManager.PERMISSION_GRANTED
- if (allowlistedFlags.hasBits(PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) &&
- !isCallerPrivileged) {
+ if (
+ allowlistedFlags.hasBits(PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) &&
+ !isCallerPrivileged
+ ) {
throw SecurityException(
"Querying system allowlist requires " +
Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS
@@ -1389,8 +1522,12 @@
val isCallerInstallerOnRecord =
packageManagerInternal.isCallerInstallerOfRecord(androidPackage, callingUid)
- if (allowlistedFlags.hasAnyBit(PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE or
- PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER)) {
+ if (
+ allowlistedFlags.hasAnyBit(
+ PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE or
+ PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER
+ )
+ ) {
if (!isCallerPrivileged && !isCallerInstallerOnRecord) {
throw SecurityException(
"Querying upgrade or installer allowlist requires being installer on record" +
@@ -1400,7 +1537,9 @@
}
return getAllowlistedRestrictedPermissionsUnchecked(
- packageState.appId, allowlistedFlags, userId
+ packageState.appId,
+ allowlistedFlags,
+ userId
)
}
@@ -1414,8 +1553,11 @@
with(policy) { getPermissionFlags(appId, userId, permissionName) }
} else {
if (permissionName !in DevicePermissionPolicy.DEVICE_AWARE_PERMISSIONS) {
- Slog.i(LOG_TAG, "$permissionName is not device aware permission, " +
- " get the flags for default device.")
+ Slog.i(
+ LOG_TAG,
+ "$permissionName is not device aware permission, " +
+ " get the flags for default device."
+ )
return with(policy) { getPermissionFlags(appId, userId, permissionName) }
}
val virtualDeviceManagerInternal = virtualDeviceManagerInternal
@@ -1423,8 +1565,7 @@
Slog.e(LOG_TAG, "Virtual device manager service is not available.")
return 0
}
- val persistentDeviceId =
- virtualDeviceManagerInternal.getPersistentIdForDevice(deviceId)
+ val persistentDeviceId = virtualDeviceManagerInternal.getPersistentIdForDevice(deviceId)
if (persistentDeviceId != null) {
with(devicePolicy) {
getPermissionFlags(appId, persistentDeviceId, userId, permissionName)
@@ -1444,13 +1585,14 @@
flags: Int
): Boolean {
return if (!Flags.deviceAwarePermissionApis() || deviceId == Context.DEVICE_ID_DEFAULT) {
- with(policy) {
- setPermissionFlags(appId, userId, permissionName, flags)
- }
+ with(policy) { setPermissionFlags(appId, userId, permissionName, flags) }
} else {
if (permissionName !in DevicePermissionPolicy.DEVICE_AWARE_PERMISSIONS) {
- Slog.i(LOG_TAG, "$permissionName is not device aware permission, " +
- " set the flags for default device.")
+ Slog.i(
+ LOG_TAG,
+ "$permissionName is not device aware permission, " +
+ " set the flags for default device."
+ )
return with(policy) { setPermissionFlags(appId, userId, permissionName, flags) }
}
@@ -1459,8 +1601,7 @@
Slog.e(LOG_TAG, "Virtual device manager service is not available.")
return false
}
- val persistentDeviceId =
- virtualDeviceManagerInternal.getPersistentIdForDevice(deviceId)
+ val persistentDeviceId = virtualDeviceManagerInternal.getPersistentIdForDevice(deviceId)
if (persistentDeviceId != null) {
with(devicePolicy) {
setPermissionFlags(appId, persistentDeviceId, userId, permissionName, flags)
@@ -1473,17 +1614,17 @@
}
/**
- * This method does not enforce checks on the caller, should only be called after
- * required checks.
+ * This method does not enforce checks on the caller, should only be called after required
+ * checks.
*/
private fun getAllowlistedRestrictedPermissionsUnchecked(
appId: Int,
allowlistedFlags: Int,
userId: Int
): ArrayList<String>? {
- val permissionFlags = service.getState {
- with(policy) { getUidPermissionFlags(appId, userId) }
- } ?: return null
+ val permissionFlags =
+ service.getState { with(policy) { getUidPermissionFlags(appId, userId) } }
+ ?: return null
var queryFlags = 0
if (allowlistedFlags.hasBits(PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM)) {
@@ -1512,14 +1653,18 @@
return false
}
- val permissionNames = getAllowlistedRestrictedPermissions(
- packageName, allowlistedFlags, userId
- ) ?: ArrayList(1)
+ val permissionNames =
+ getAllowlistedRestrictedPermissions(packageName, allowlistedFlags, userId)
+ ?: ArrayList(1)
if (permissionName !in permissionNames) {
permissionNames += permissionName
return setAllowlistedRestrictedPermissions(
- packageName, permissionNames, allowlistedFlags, userId, isAddingPermission = true
+ packageName,
+ permissionNames,
+ allowlistedFlags,
+ userId,
+ isAddingPermission = true
)
}
return false
@@ -1531,14 +1676,22 @@
permissionNames: List<String>,
userId: Int
) {
- val newPermissionNames = getAllowlistedRestrictedPermissionsUnchecked(appId,
- PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER, userId
- )?.let {
- ArraySet(permissionNames).apply { this += it }.toList()
- } ?: permissionNames
+ val newPermissionNames =
+ getAllowlistedRestrictedPermissionsUnchecked(
+ appId,
+ PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER,
+ userId
+ )
+ ?.let { ArraySet(permissionNames).apply { this += it }.toList() }
+ ?: permissionNames
- setAllowlistedRestrictedPermissionsUnchecked(androidPackage, appId, newPermissionNames,
- PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER, userId)
+ setAllowlistedRestrictedPermissionsUnchecked(
+ androidPackage,
+ appId,
+ newPermissionNames,
+ PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER,
+ userId
+ )
}
override fun removeAllowlistedRestrictedPermission(
@@ -1552,13 +1705,17 @@
return false
}
- val permissions = getAllowlistedRestrictedPermissions(
- packageName, allowlistedFlags, userId
- ) ?: return false
+ val permissions =
+ getAllowlistedRestrictedPermissions(packageName, allowlistedFlags, userId)
+ ?: return false
if (permissions.remove(permissionName)) {
return setAllowlistedRestrictedPermissions(
- packageName, permissions, allowlistedFlags, userId, isAddingPermission = false
+ packageName,
+ permissions,
+ allowlistedFlags,
+ userId,
+ isAddingPermission = false
)
}
@@ -1572,16 +1729,22 @@
return false
}
- if (packageManagerLocal.withFilteredSnapshot()
- .use { it.getPackageState(permission.packageName) } == null) {
+ if (
+ packageManagerLocal.withFilteredSnapshot().use {
+ it.getPackageState(permission.packageName)
+ } == null
+ ) {
return false
}
val isImmutablyRestrictedPermission =
permission.isHardOrSoftRestricted && permission.isImmutablyRestricted
- if (isImmutablyRestrictedPermission && context.checkCallingOrSelfPermission(
- Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS
- ) != PackageManager.PERMISSION_GRANTED) {
+ if (
+ isImmutablyRestrictedPermission &&
+ context.checkCallingOrSelfPermission(
+ Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS
+ ) != PackageManager.PERMISSION_GRANTED
+ ) {
throw SecurityException(
"Cannot modify allowlist of an immutably restricted permission: ${permission.name}"
)
@@ -1599,13 +1762,16 @@
): Boolean {
Preconditions.checkArgument(allowlistedFlags.countOneBits() == 1)
- val isCallerPrivileged = context.checkCallingOrSelfPermission(
- Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS
- ) == PackageManager.PERMISSION_GRANTED
+ val isCallerPrivileged =
+ context.checkCallingOrSelfPermission(
+ Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS
+ ) == PackageManager.PERMISSION_GRANTED
val callingUid = Binder.getCallingUid()
- val packageState = packageManagerLocal.withFilteredSnapshot(callingUid, userId)
- .use { snapshot -> snapshot.packageStates[packageName] ?: return false }
+ val packageState =
+ packageManagerLocal.withFilteredSnapshot(callingUid, userId).use { snapshot ->
+ snapshot.packageStates[packageName] ?: return false
+ }
val androidPackage = packageState.androidPackage ?: return false
val isCallerInstallerOnRecord =
@@ -1627,15 +1793,19 @@
}
setAllowlistedRestrictedPermissionsUnchecked(
- androidPackage, packageState.appId, permissionNames, allowlistedFlags, userId
+ androidPackage,
+ packageState.appId,
+ permissionNames,
+ allowlistedFlags,
+ userId
)
return true
}
/**
- * This method does not enforce checks on the caller, should only be called after
- * required checks.
+ * This method does not enforce checks on the caller, should only be called after required
+ * checks.
*/
private fun setAllowlistedRestrictedPermissionsUnchecked(
androidPackage: AndroidPackage,
@@ -1712,22 +1882,24 @@
}
}
- newFlags = if (permission.isHardRestricted && !isExempt) {
- newFlags or PermissionFlags.RESTRICTION_REVOKED
- } else {
- newFlags andInv PermissionFlags.RESTRICTION_REVOKED
- }
- newFlags = if (permission.isSoftRestricted && !isExempt) {
- newFlags or PermissionFlags.SOFT_RESTRICTED
- } else {
- newFlags andInv PermissionFlags.SOFT_RESTRICTED
- }
- mask = mask or PermissionFlags.RESTRICTION_REVOKED or
- PermissionFlags.SOFT_RESTRICTED
+ newFlags =
+ if (permission.isHardRestricted && !isExempt) {
+ newFlags or PermissionFlags.RESTRICTION_REVOKED
+ } else {
+ newFlags andInv PermissionFlags.RESTRICTION_REVOKED
+ }
+ newFlags =
+ if (permission.isSoftRestricted && !isExempt) {
+ newFlags or PermissionFlags.SOFT_RESTRICTED
+ } else {
+ newFlags andInv PermissionFlags.SOFT_RESTRICTED
+ }
+ mask =
+ mask or
+ PermissionFlags.RESTRICTION_REVOKED or
+ PermissionFlags.SOFT_RESTRICTED
- updatePermissionFlags(
- appId, userId, requestedPermission, mask, newFlags
- )
+ updatePermissionFlags(appId, userId, requestedPermission, mask, newFlags)
}
}
}
@@ -1735,9 +1907,8 @@
override fun resetRuntimePermissions(androidPackage: AndroidPackage, userId: Int) {
service.mutateState {
- with(policy) {
- resetRuntimePermissions(androidPackage.packageName, userId)
- }
+ with(policy) { resetRuntimePermissions(androidPackage.packageName, userId) }
+ with(devicePolicy) { resetRuntimePermissions(androidPackage.packageName, userId) }
}
}
@@ -1745,9 +1916,8 @@
packageManagerLocal.withUnfilteredSnapshot().use { snapshot ->
service.mutateState {
snapshot.packageStates.forEach { (_, packageState) ->
- with(policy) {
- resetRuntimePermissions(packageState.packageName, userId)
- }
+ with(policy) { resetRuntimePermissions(packageState.packageName, userId) }
+ with(devicePolicy) { resetRuntimePermissions(packageState.packageName, userId) }
}
}
}
@@ -1755,7 +1925,8 @@
override fun addOnPermissionsChangeListener(listener: IOnPermissionsChangeListener) {
context.enforceCallingOrSelfPermission(
- Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS, "addOnPermissionsChangeListener"
+ Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS,
+ "addOnPermissionsChangeListener"
)
onPermissionsChangeListeners.addListener(listener)
@@ -1780,9 +1951,7 @@
requireNotNull(permissionName) { "permissionName cannot be null" }
val packageNames = ArraySet<String>()
- val permission = service.getState {
- with(policy) { getPermissions()[permissionName] }
- }
+ val permission = service.getState { with(policy) { getPermissions()[permissionName] } }
if (permission == null || !permission.isAppOp) {
packageNames.toTypedArray()
}
@@ -1808,8 +1977,8 @@
androidPackage.requestedPermissions.forEach requestedPermissions@{ permissionName ->
val permission = permissions[permissionName] ?: return@requestedPermissions
if (permission.isAppOp) {
- val packageNames = appOpPermissionPackageNames
- .getOrPut(permissionName) { ArraySet() }
+ val packageNames =
+ appOpPermissionPackageNames.getOrPut(permissionName) { ArraySet() }
packageNames += androidPackage.packageName
}
}
@@ -1822,14 +1991,18 @@
Preconditions.checkArgumentNonnegative(userId, "userId cannot be null")
val backup = CompletableFuture<ByteArray>()
permissionControllerManager.getRuntimePermissionBackup(
- UserHandle.of(userId), PermissionThread.getExecutor(), backup::complete
+ UserHandle.of(userId),
+ PermissionThread.getExecutor(),
+ backup::complete
)
return try {
backup.get(BACKUP_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
} catch (e: Exception) {
when (e) {
- is TimeoutException, is InterruptedException, is ExecutionException -> {
+ is TimeoutException,
+ is InterruptedException,
+ is ExecutionException -> {
Slog.e(LOG_TAG, "Cannot create permission backup for user $userId", e)
null
}
@@ -1846,7 +2019,8 @@
isDelayedPermissionBackupFinished -= userId
}
permissionControllerManager.stageAndApplyRuntimePermissionsBackup(
- backup, UserHandle.of(userId)
+ backup,
+ UserHandle.of(userId)
)
}
@@ -1860,7 +2034,9 @@
}
}
permissionControllerManager.applyStagedRuntimePermissionBackup(
- packageName, UserHandle.of(userId), PermissionThread.getExecutor()
+ packageName,
+ UserHandle.of(userId),
+ PermissionThread.getExecutor()
) { hasMoreBackup ->
if (hasMoreBackup) {
return@applyStagedRuntimePermissionBackup
@@ -1907,21 +2083,15 @@
): IndexedMap<Int, MutableIndexedSet<String>> {
val appIds = MutableIndexedSet<Int>()
- val packageStates = packageManagerLocal.withUnfilteredSnapshot().use {
- it.packageStates
- }
+ val packageStates = packageManagerLocal.withUnfilteredSnapshot().use { it.packageStates }
state.userStates.forEachIndexed { _, _, userState ->
- userState.appIdPermissionFlags.forEachIndexed { _, appId, _ ->
- appIds.add(appId)
- }
- userState.appIdAppOpModes.forEachIndexed { _, appId, _ ->
- appIds.add(appId)
- }
- userState.packageVersions.forEachIndexed packageVersions@ { _, packageName, _ ->
+ userState.appIdPermissionFlags.forEachIndexed { _, appId, _ -> appIds.add(appId) }
+ userState.appIdAppOpModes.forEachIndexed { _, appId, _ -> appIds.add(appId) }
+ userState.packageVersions.forEachIndexed packageVersions@{ _, packageName, _ ->
val appId = packageStates[packageName]?.appId ?: return@packageVersions
appIds.add(appId)
}
- userState.packageAppOpModes.forEachIndexed packageAppOpModes@ { _, packageName, _ ->
+ userState.packageAppOpModes.forEachIndexed packageAppOpModes@{ _, packageName, _ ->
val appId = packageStates[packageName]?.appId ?: return@packageAppOpModes
appIds.add(appId)
}
@@ -1929,7 +2099,8 @@
val appIdPackageNames = MutableIndexedMap<Int, MutableIndexedSet<String>>()
packageStates.forEach { (_, packageState) ->
- appIdPackageNames.getOrPut(packageState.appId) { MutableIndexedSet() }
+ appIdPackageNames
+ .getOrPut(packageState.appId) { MutableIndexedSet() }
.add(packageState.packageName)
}
// add non-package app IDs which might not be reported by package manager.
@@ -1960,10 +2131,7 @@
println("Permission groups:")
withIndent {
state.systemState.permissionGroups.forEachIndexed { _, _, permissionGroup ->
- println(
- "${permissionGroup.name}: " +
- "packageName=${permissionGroup.packageName}"
- )
+ println("${permissionGroup.name}: " + "packageName=${permissionGroup.packageName}")
}
}
@@ -1992,7 +2160,9 @@
println("Permissions:")
withIndent {
userState.appIdPermissionFlags[appId]?.forEachIndexed {
- _, permissionName, flags ->
+ _,
+ permissionName,
+ flags ->
val isGranted = PermissionFlags.isPermissionGranted(flags)
println(
"$permissionName: granted=$isGranted, flags=" +
@@ -2002,7 +2172,9 @@
}
userState.appIdDevicePermissionFlags[appId]?.forEachIndexed {
- _, deviceId, devicePermissionFlags ->
+ _,
+ deviceId,
+ devicePermissionFlags ->
println("Permissions (Device $deviceId):")
withIndent {
devicePermissionFlags.forEachIndexed { _, permissionName, flags ->
@@ -2017,7 +2189,8 @@
println("App ops:")
withIndent {
- userState.appIdAppOpModes[appId]?.forEachIndexed {_, appOpName, appOpMode ->
+ userState.appIdAppOpModes[appId]?.forEachIndexed { _, appOpName, appOpMode
+ ->
println("$appOpName: mode=${AppOpsManager.modeToName(appOpMode)}")
}
}
@@ -2029,7 +2202,9 @@
println("App ops:")
withIndent {
userState.packageAppOpModes[packageName]?.forEachIndexed {
- _, appOpName, appOpMode ->
+ _,
+ appOpName,
+ appOpMode ->
val modeName = AppOpsManager.modeToName(appOpMode)
println("$appOpName: mode=$modeName")
}
@@ -2048,24 +2223,30 @@
}
override fun getPermissionTEMP(permissionName: String): LegacyPermission2? {
- val permission = service.getState {
- with(policy) { getPermissions()[permissionName] }
- } ?: return null
+ val permission =
+ service.getState { with(policy) { getPermissions()[permissionName] } } ?: return null
return LegacyPermission2(
- permission.permissionInfo, permission.type, permission.isReconciled, permission.appId,
- permission.gids, permission.areGidsPerUser
+ permission.permissionInfo,
+ permission.type,
+ permission.isReconciled,
+ permission.appId,
+ permission.gids,
+ permission.areGidsPerUser
)
}
override fun getLegacyPermissions(): List<LegacyPermission> =
- service.getState {
- with(policy) { getPermissions() }
- }.mapIndexedTo(ArrayList()) { _, _, permission ->
- LegacyPermission(
- permission.permissionInfo, permission.type, permission.appId, permission.gids
- )
- }
+ service
+ .getState { with(policy) { getPermissions() } }
+ .mapIndexedTo(ArrayList()) { _, _, permission ->
+ LegacyPermission(
+ permission.permissionInfo,
+ permission.type,
+ permission.appId,
+ permission.gids
+ )
+ }
override fun readLegacyPermissionsTEMP(legacyPermissionSettings: LegacyPermissionSettings) {
// Package settings has been read when this method is called.
@@ -2086,9 +2267,7 @@
): List<LegacyPermission> =
permissions.mapIndexedTo(ArrayList()) { _, _, permission ->
// We don't need to provide UID and GIDs, which are only retrieved when dumping.
- LegacyPermission(
- permission.permissionInfo, permission.type, 0, EmptyArray.INT
- )
+ LegacyPermission(permission.permissionInfo, permission.type, 0, EmptyArray.INT)
}
override fun getLegacyPermissionState(appId: Int): LegacyPermissionState {
@@ -2097,17 +2276,18 @@
service.getState {
val permissions = with(policy) { getPermissions() }
userIds.forEachIndexed { _, userId ->
- val permissionFlags = with(policy) { getUidPermissionFlags(appId, userId) }
- ?: return@forEachIndexed
+ val permissionFlags =
+ with(policy) { getUidPermissionFlags(appId, userId) } ?: return@forEachIndexed
permissionFlags.forEachIndexed permissionFlags@{ _, permissionName, flags ->
val permission = permissions[permissionName] ?: return@permissionFlags
- val legacyPermissionState = LegacyPermissionState.PermissionState(
- permissionName,
- permission.isRuntime,
- PermissionFlags.isPermissionGranted(flags),
- PermissionFlags.toApiFlags(flags)
- )
+ val legacyPermissionState =
+ LegacyPermissionState.PermissionState(
+ permissionName,
+ permission.isRuntime,
+ PermissionFlags.isPermissionGranted(flags),
+ PermissionFlags.toApiFlags(flags)
+ )
legacyState.putPermissionState(legacyPermissionState, userId)
}
}
@@ -2131,16 +2311,13 @@
override fun onSystemReady() {
service.onSystemReady()
virtualDeviceManagerInternal =
- LocalServices.getService(VirtualDeviceManagerInternal::class.java)
- permissionControllerManager = PermissionControllerManager(
- context, PermissionThread.getHandler()
- )
+ LocalServices.getService(VirtualDeviceManagerInternal::class.java)
+ permissionControllerManager =
+ PermissionControllerManager(context, PermissionThread.getHandler())
}
override fun onUserCreated(userId: Int) {
- withCorkedPackageInfoCache {
- service.onUserAdded(userId)
- }
+ withCorkedPackageInfoCache { service.onUserAdded(userId) }
}
override fun onUserRemoved(userId: Int) {
@@ -2170,9 +2347,8 @@
// of onPackageAdded() and reuse this order in onStorageVolumeAdded(). We need the
// packages to be iterated in onStorageVolumeAdded() in the same order so that the
// ownership of permissions is consistent.
- storageVolumePackageNames.getOrPut(packageState.volumeUuid) {
- mutableListOf()
- } += packageState.packageName
+ storageVolumePackageNames.getOrPut(packageState.volumeUuid) { mutableListOf() } +=
+ packageState.packageName
if (packageState.volumeUuid !in mountedStorageVolumes) {
// Wait for the storage volume to be mounted and batch the state mutation there.
return
@@ -2212,23 +2388,26 @@
return
}
}
- val userIds = if (userId == UserHandle.USER_ALL) {
- userManagerService.userIdsIncludingPreCreated
- } else {
- intArrayOf(userId)
- }
+ val userIds =
+ if (userId == UserHandle.USER_ALL) {
+ userManagerService.userIdsIncludingPreCreated
+ } else {
+ intArrayOf(userId)
+ }
@Suppress("NAME_SHADOWING")
- userIds.forEach { userId ->
- service.onPackageInstalled(androidPackage.packageName, userId)
- }
+ userIds.forEach { userId -> service.onPackageInstalled(androidPackage.packageName, userId) }
@Suppress("NAME_SHADOWING")
userIds.forEach { userId ->
// TODO: Remove when this callback receives packageState directly.
val packageState =
packageManagerInternal.getPackageStateInternal(androidPackage.packageName)!!
- addAllowlistedRestrictedPermissionsUnchecked(androidPackage, packageState.appId,
- params.allowlistedRestrictedPermissions, userId)
+ addAllowlistedRestrictedPermissionsUnchecked(
+ androidPackage,
+ packageState.appId,
+ params.allowlistedRestrictedPermissions,
+ userId
+ )
setRequestedPermissionStates(packageState, userId, params.permissionStates)
}
}
@@ -2241,11 +2420,12 @@
sharedUserPkgs: List<AndroidPackage>,
userId: Int
) {
- val userIds = if (userId == UserHandle.USER_ALL) {
- userManagerService.userIdsIncludingPreCreated
- } else {
- intArrayOf(userId)
- }
+ val userIds =
+ if (userId == UserHandle.USER_ALL) {
+ userManagerService.userIdsIncludingPreCreated
+ } else {
+ intArrayOf(userId)
+ }
userIds.forEach { service.onPackageUninstalled(packageName, appId, it) }
val packageState = packageManagerInternal.packageStates[packageName]
if (packageState == null) {
@@ -2262,31 +2442,26 @@
}
}
- /**
- * Check whether a UID is root or system UID.
- */
+ /** Check whether a UID is root or system UID. */
private fun isRootOrSystemUid(uid: Int) =
when (UserHandle.getAppId(uid)) {
- Process.ROOT_UID, Process.SYSTEM_UID -> true
+ Process.ROOT_UID,
+ Process.SYSTEM_UID -> true
else -> false
}
- /**
- * Check whether a UID is shell UID.
- */
+ /** Check whether a UID is shell UID. */
private fun isShellUid(uid: Int) = UserHandle.getAppId(uid) == Process.SHELL_UID
- /**
- * Check whether a UID is root, system or shell UID.
- */
+ /** Check whether a UID is root, system or shell UID. */
private fun isRootOrSystemOrShellUid(uid: Int) = isRootOrSystemUid(uid) || isShellUid(uid)
/**
* This method should typically only be used when granting or revoking permissions, since the
* app may immediately restart after this call.
*
- * If you're doing surgery on app code/data, use [PackageFreezer] to guard your work against
- * the app being relaunched.
+ * If you're doing surgery on app code/data, use [PackageFreezer] to guard your work against the
+ * app being relaunched.
*/
private fun killUid(uid: Int, reason: String) {
val activityManager = ActivityManager.getService()
@@ -2303,9 +2478,7 @@
}
}
- /**
- * @see PackageManagerLocal.withFilteredSnapshot
- */
+ /** @see PackageManagerLocal.withFilteredSnapshot */
private fun PackageManagerLocal.withFilteredSnapshot(
callingUid: Int,
userId: Int
@@ -2323,35 +2496,27 @@
packageName: String
): PackageState? = packageStates[packageName]
- /**
- * Check whether a UID belongs to an instant app.
- */
+ /** Check whether a UID belongs to an instant app. */
private fun PackageManagerLocal.UnfilteredSnapshot.isUidInstantApp(uid: Int): Boolean =
// Unfortunately we don't have the API for getting the owner UID of an isolated UID or the
// API for getting the SharedUserApi object for an app ID yet, so for now we just keep
// calling the old API.
packageManagerInternal.getInstantAppPackageName(uid) != null
- /**
- * Check whether a package is visible to a UID within the same user as the UID.
- */
+ /** Check whether a package is visible to a UID within the same user as the UID. */
private fun PackageManagerLocal.UnfilteredSnapshot.isPackageVisibleToUid(
packageName: String,
uid: Int
): Boolean = isPackageVisibleToUid(packageName, UserHandle.getUserId(uid), uid)
- /**
- * Check whether a package in a particular user is visible to a UID.
- */
+ /** Check whether a package in a particular user is visible to a UID. */
private fun PackageManagerLocal.UnfilteredSnapshot.isPackageVisibleToUid(
packageName: String,
userId: Int,
uid: Int
): Boolean = filtered(uid, userId).use { it.getPackageState(packageName) != null }
- /**
- * @see PackageManagerLocal.UnfilteredSnapshot.filtered
- */
+ /** @see PackageManagerLocal.UnfilteredSnapshot.filtered */
private fun PackageManagerLocal.UnfilteredSnapshot.filtered(
callingUid: Int,
userId: Int
@@ -2374,13 +2539,16 @@
val callingUid = Binder.getCallingUid()
val callingUserId = UserHandle.getUserId(callingUid)
if (userId != callingUserId) {
- val permissionName = if (enforceFullPermission) {
- Manifest.permission.INTERACT_ACROSS_USERS_FULL
- } else {
- Manifest.permission.INTERACT_ACROSS_USERS
- }
- if (context.checkCallingOrSelfPermission(permissionName) !=
- PackageManager.PERMISSION_GRANTED) {
+ val permissionName =
+ if (enforceFullPermission) {
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL
+ } else {
+ Manifest.permission.INTERACT_ACROSS_USERS
+ }
+ if (
+ context.checkCallingOrSelfPermission(permissionName) !=
+ PackageManager.PERMISSION_GRANTED
+ ) {
val exceptionMessage = buildString {
if (message != null) {
append(message)
@@ -2397,9 +2565,11 @@
}
}
if (enforceShellRestriction && isShellUid(callingUid)) {
- val isShellRestricted = userManagerInternal.hasUserRestriction(
- UserManager.DISALLOW_DEBUGGING_FEATURES, userId
- )
+ val isShellRestricted =
+ userManagerInternal.hasUserRestriction(
+ UserManager.DISALLOW_DEBUGGING_FEATURES,
+ userId
+ )
if (isShellRestricted) {
val exceptionMessage = buildString {
if (message != null) {
@@ -2424,10 +2594,11 @@
message: String?,
vararg permissionNames: String
) {
- val hasAnyPermission = permissionNames.any { permissionName ->
- context.checkCallingOrSelfPermission(permissionName) ==
- PackageManager.PERMISSION_GRANTED
- }
+ val hasAnyPermission =
+ permissionNames.any { permissionName ->
+ context.checkCallingOrSelfPermission(permissionName) ==
+ PackageManager.PERMISSION_GRANTED
+ }
if (!hasAnyPermission) {
val exceptionMessage = buildString {
if (message != null) {
@@ -2443,9 +2614,7 @@
}
}
- /**
- * Callback invoked when interesting actions have been taken on a permission.
- */
+ /** Callback invoked when interesting actions have been taken on a permission. */
private inner class OnPermissionFlagsChangedListener :
AppIdPermissionPolicy.OnPermissionFlagsChangedListener() {
private var isPermissionFlagsChanged = false
@@ -2476,9 +2645,8 @@
isPermissionFlagsChanged = true
val uid = UserHandle.getUid(userId, appId)
- val permission = service.getState {
- with(policy) { getPermissions()[permissionName] }
- } ?: return
+ val permission =
+ service.getState { with(policy) { getPermissions()[permissionName] } } ?: return
val wasPermissionGranted = PermissionFlags.isPermissionGranted(oldFlags)
val isPermissionGranted = PermissionFlags.isPermissionGranted(newFlags)
@@ -2512,16 +2680,20 @@
runtimePermissionChangedUids.clear()
if (!isKillRuntimePermissionRevokedUidsSkipped) {
- val reason = if (killRuntimePermissionRevokedUidsReasons.isNotEmpty()) {
- killRuntimePermissionRevokedUidsReasons.joinToString(", ")
- } else {
- PermissionManager.KILL_APP_REASON_PERMISSIONS_REVOKED
- }
+ val reason =
+ if (killRuntimePermissionRevokedUidsReasons.isNotEmpty()) {
+ killRuntimePermissionRevokedUidsReasons.joinToString(", ")
+ } else {
+ PermissionManager.KILL_APP_REASON_PERMISSIONS_REVOKED
+ }
runtimePermissionRevokedUids.forEachIndexed {
- _, uid, areOnlyNotificationsPermissionsRevoked ->
+ _,
+ uid,
+ areOnlyNotificationsPermissionsRevoked ->
handler.post {
- if (areOnlyNotificationsPermissionsRevoked &&
- isAppBackupAndRestoreRunning(uid)
+ if (
+ areOnlyNotificationsPermissionsRevoked &&
+ isAppBackupAndRestoreRunning(uid)
) {
return@post
}
@@ -2541,19 +2713,27 @@
}
private fun isAppBackupAndRestoreRunning(uid: Int): Boolean {
- if (checkUidPermission(uid, Manifest.permission.BACKUP, Context.DEVICE_ID_DEFAULT) !=
- PackageManager.PERMISSION_GRANTED) {
+ if (
+ checkUidPermission(uid, Manifest.permission.BACKUP, Context.DEVICE_ID_DEFAULT) !=
+ PackageManager.PERMISSION_GRANTED
+ ) {
return false
}
return try {
val contentResolver = context.contentResolver
val userId = UserHandle.getUserId(uid)
- val isInSetup = Settings.Secure.getIntForUser(
- contentResolver, Settings.Secure.USER_SETUP_COMPLETE, userId
- ) == 0
- val isInDeferredSetup = Settings.Secure.getIntForUser(
- contentResolver, Settings.Secure.USER_SETUP_PERSONALIZATION_STATE, userId
- ) == Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED
+ val isInSetup =
+ Settings.Secure.getIntForUser(
+ contentResolver,
+ Settings.Secure.USER_SETUP_COMPLETE,
+ userId
+ ) == 0
+ val isInDeferredSetup =
+ Settings.Secure.getIntForUser(
+ contentResolver,
+ Settings.Secure.USER_SETUP_PERSONALIZATION_STATE,
+ userId
+ ) == Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED
isInSetup || isInDeferredSetup
} catch (e: Settings.SettingNotFoundException) {
Slog.w(LOG_TAG, "Failed to check if the user is in restore: $e")
@@ -2614,31 +2794,34 @@
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
private val BACKGROUND_RATIONALE_CHANGE_ID = 147316723L
- private val FULLER_PERMISSIONS = ArrayMap<String, String>().apply {
- this[Manifest.permission.ACCESS_COARSE_LOCATION] =
- Manifest.permission.ACCESS_FINE_LOCATION
- this[Manifest.permission.INTERACT_ACROSS_USERS] =
- Manifest.permission.INTERACT_ACROSS_USERS_FULL
- }
+ private val FULLER_PERMISSIONS =
+ ArrayMap<String, String>().apply {
+ this[Manifest.permission.ACCESS_COARSE_LOCATION] =
+ Manifest.permission.ACCESS_FINE_LOCATION
+ this[Manifest.permission.INTERACT_ACROSS_USERS] =
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL
+ }
- private val NOTIFICATIONS_PERMISSIONS = arraySetOf(
- Manifest.permission.POST_NOTIFICATIONS
- )
+ private val NOTIFICATIONS_PERMISSIONS = arraySetOf(Manifest.permission.POST_NOTIFICATIONS)
- private const val REVIEW_REQUIRED_FLAGS = PermissionFlags.LEGACY_GRANTED or
- PermissionFlags.IMPLICIT
- private const val UNREQUESTABLE_MASK = PermissionFlags.RESTRICTION_REVOKED or
- PermissionFlags.SYSTEM_FIXED or PermissionFlags.POLICY_FIXED or
- PermissionFlags.USER_FIXED
+ private const val REVIEW_REQUIRED_FLAGS =
+ PermissionFlags.LEGACY_GRANTED or PermissionFlags.IMPLICIT
+ private const val UNREQUESTABLE_MASK =
+ PermissionFlags.RESTRICTION_REVOKED or
+ PermissionFlags.SYSTEM_FIXED or
+ PermissionFlags.POLICY_FIXED or
+ PermissionFlags.USER_FIXED
private val BACKUP_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(60)
- /** Cap the size of permission trees that 3rd party apps can define; in characters of text */
+ /**
+ * Cap the size of permission trees that 3rd party apps can define; in characters of text
+ */
private const val MAX_PERMISSION_TREE_FOOTPRINT = 32768
private const val PERMISSION_ALLOWLIST_MASK =
PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE or
- PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM or
- PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER
+ PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM or
+ PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER
}
}
diff --git a/services/permission/java/com/android/server/permission/access/util/AtomicFileExtensions.kt b/services/permission/java/com/android/server/permission/access/util/AtomicFileExtensions.kt
index bd82935..6b20ef1 100644
--- a/services/permission/java/com/android/server/permission/access/util/AtomicFileExtensions.kt
+++ b/services/permission/java/com/android/server/permission/access/util/AtomicFileExtensions.kt
@@ -25,9 +25,7 @@
import java.io.FileOutputStream
import java.io.IOException
-/**
- * Read from an [AtomicFile], fallback to reserve file to read the data.
- */
+/** Read from an [AtomicFile], fallback to reserve file to read the data. */
@Throws(Exception::class)
inline fun AtomicFile.readWithReserveCopy(block: (FileInputStream) -> Unit) {
try {
@@ -46,9 +44,7 @@
}
}
-/**
- * Write to actual file and reserve file.
- */
+/** Write to actual file and reserve file. */
@Throws(IOException::class)
inline fun AtomicFile.writeWithReserveCopy(block: (FileOutputStream) -> Unit) {
val reserveFile = File(baseFile.parentFile, baseFile.name + ".reservecopy")
@@ -66,9 +62,7 @@
}
}
-/**
- * Write to an [AtomicFile] and close everything safely when done.
- */
+/** Write to an [AtomicFile] and close everything safely when done. */
@Throws(IOException::class)
// Renamed to writeInlined() to avoid conflict with the hidden AtomicFile.write() that isn't inline.
inline fun AtomicFile.writeInlined(block: (FileOutputStream) -> Unit) {
diff --git a/services/permission/java/com/android/server/permission/access/util/BinaryXmlPullParserExtensions.kt b/services/permission/java/com/android/server/permission/access/util/BinaryXmlPullParserExtensions.kt
index 1d27aef..6ab73c7 100644
--- a/services/permission/java/com/android/server/permission/access/util/BinaryXmlPullParserExtensions.kt
+++ b/services/permission/java/com/android/server/permission/access/util/BinaryXmlPullParserExtensions.kt
@@ -22,9 +22,7 @@
import org.xmlpull.v1.XmlPullParser
import org.xmlpull.v1.XmlPullParserException
-/**
- * Parse content from [InputStream] with [BinaryXmlPullParser].
- */
+/** Parse content from [InputStream] with [BinaryXmlPullParser]. */
@Throws(IOException::class, XmlPullParserException::class)
inline fun InputStream.parseBinaryXml(block: BinaryXmlPullParser.() -> Unit) {
BinaryXmlPullParser().apply {
@@ -35,6 +33,7 @@
/**
* Iterate through child tags of the current tag.
+ *
* <p>
* Attributes for the current tag needs to be accessed before this method is called because this
* method will advance the parser past the start tag of the current tag. The code inspecting each
@@ -50,7 +49,8 @@
inline fun BinaryXmlPullParser.forEachTag(block: BinaryXmlPullParser.() -> Unit) {
when (val eventType = eventType) {
// Document start or start tag of the parent tag.
- XmlPullParser.START_DOCUMENT, XmlPullParser.START_TAG -> nextTagOrEnd()
+ XmlPullParser.START_DOCUMENT,
+ XmlPullParser.START_TAG -> nextTagOrEnd()
else -> throw XmlPullParserException("Unexpected event type $eventType")
}
while (true) {
@@ -90,7 +90,8 @@
nextTagOrEnd()
}
// End tag of the parent tag, or document end.
- XmlPullParser.END_TAG, XmlPullParser.END_DOCUMENT -> break
+ XmlPullParser.END_TAG,
+ XmlPullParser.END_DOCUMENT -> break
else -> throw XmlPullParserException("Unexpected event type $eventType")
}
}
@@ -107,193 +108,146 @@
inline fun BinaryXmlPullParser.nextTagOrEnd(): Int {
while (true) {
when (val eventType = next()) {
- XmlPullParser.START_TAG, XmlPullParser.END_TAG, XmlPullParser.END_DOCUMENT ->
- return eventType
+ XmlPullParser.START_TAG,
+ XmlPullParser.END_TAG,
+ XmlPullParser.END_DOCUMENT -> return eventType
else -> continue
}
}
}
-/**
- * @see BinaryXmlPullParser.getName
- */
+/** @see BinaryXmlPullParser.getName */
inline val BinaryXmlPullParser.tagName: String
get() = name
-/**
- * Check whether an attribute exists for the current tag.
- */
+/** Check whether an attribute exists for the current tag. */
@Suppress("NOTHING_TO_INLINE")
inline fun BinaryXmlPullParser.hasAttribute(name: String): Boolean = getAttributeIndex(name) != -1
-/**
- * @see BinaryXmlPullParser.getAttributeIndex
- */
+/** @see BinaryXmlPullParser.getAttributeIndex */
@Suppress("NOTHING_TO_INLINE")
inline fun BinaryXmlPullParser.getAttributeIndex(name: String): Int = getAttributeIndex(null, name)
-/**
- * @see BinaryXmlPullParser.getAttributeIndexOrThrow
- */
+/** @see BinaryXmlPullParser.getAttributeIndexOrThrow */
@Suppress("NOTHING_TO_INLINE")
@Throws(XmlPullParserException::class)
inline fun BinaryXmlPullParser.getAttributeIndexOrThrow(name: String): Int =
getAttributeIndexOrThrow(null, name)
-/**
- * @see BinaryXmlPullParser.getAttributeValue
- */
+/** @see BinaryXmlPullParser.getAttributeValue */
@Suppress("NOTHING_TO_INLINE")
@Throws(XmlPullParserException::class)
inline fun BinaryXmlPullParser.getAttributeValue(name: String): String? =
getAttributeValue(null, name)
-/**
- * @see BinaryXmlPullParser.getAttributeValue
- */
+/** @see BinaryXmlPullParser.getAttributeValue */
@Suppress("NOTHING_TO_INLINE")
@Throws(XmlPullParserException::class)
inline fun BinaryXmlPullParser.getAttributeValueOrThrow(name: String): String =
getAttributeValue(getAttributeIndexOrThrow(name))
-/**
- * @see BinaryXmlPullParser.getAttributeBytesHex
- */
+/** @see BinaryXmlPullParser.getAttributeBytesHex */
@Suppress("NOTHING_TO_INLINE")
inline fun BinaryXmlPullParser.getAttributeBytesHex(name: String): ByteArray? =
getAttributeBytesHex(null, name, null)
-/**
- * @see BinaryXmlPullParser.getAttributeBytesHex
- */
+/** @see BinaryXmlPullParser.getAttributeBytesHex */
@Suppress("NOTHING_TO_INLINE")
@Throws(XmlPullParserException::class)
inline fun BinaryXmlPullParser.getAttributeBytesHexOrThrow(name: String): ByteArray =
getAttributeBytesHex(null, name)
-/**
- * @see BinaryXmlPullParser.getAttributeBytesBase64
- */
+/** @see BinaryXmlPullParser.getAttributeBytesBase64 */
@Suppress("NOTHING_TO_INLINE")
inline fun BinaryXmlPullParser.getAttributeBytesBase64(name: String): ByteArray? =
getAttributeBytesBase64(null, name, null)
-/**
- * @see BinaryXmlPullParser.getAttributeBytesBase64
- */
+/** @see BinaryXmlPullParser.getAttributeBytesBase64 */
@Suppress("NOTHING_TO_INLINE")
@Throws(XmlPullParserException::class)
inline fun BinaryXmlPullParser.getAttributeBytesBase64OrThrow(name: String): ByteArray =
getAttributeBytesBase64(null, name)
-/**
- * @see BinaryXmlPullParser.getAttributeInt
- */
+/** @see BinaryXmlPullParser.getAttributeInt */
@Suppress("NOTHING_TO_INLINE")
inline fun BinaryXmlPullParser.getAttributeIntOrDefault(name: String, defaultValue: Int): Int =
getAttributeInt(null, name, defaultValue)
-/**
- * @see BinaryXmlPullParser.getAttributeInt
- */
+/** @see BinaryXmlPullParser.getAttributeInt */
@Suppress("NOTHING_TO_INLINE")
@Throws(XmlPullParserException::class)
inline fun BinaryXmlPullParser.getAttributeIntOrThrow(name: String): Int =
getAttributeInt(null, name)
-/**
- * @see BinaryXmlPullParser.getAttributeIntHex
- */
+/** @see BinaryXmlPullParser.getAttributeIntHex */
@Suppress("NOTHING_TO_INLINE")
inline fun BinaryXmlPullParser.getAttributeIntHexOrDefault(name: String, defaultValue: Int): Int =
getAttributeIntHex(null, name, defaultValue)
-/**
- * @see BinaryXmlPullParser.getAttributeIntHex
- */
+/** @see BinaryXmlPullParser.getAttributeIntHex */
@Suppress("NOTHING_TO_INLINE")
@Throws(XmlPullParserException::class)
inline fun BinaryXmlPullParser.getAttributeIntHexOrThrow(name: String): Int =
getAttributeIntHex(null, name)
-/**
- * @see BinaryXmlPullParser.getAttributeLong
- */
+/** @see BinaryXmlPullParser.getAttributeLong */
@Suppress("NOTHING_TO_INLINE")
inline fun BinaryXmlPullParser.getAttributeLongOrDefault(name: String, defaultValue: Long): Long =
getAttributeLong(null, name, defaultValue)
-/**
- * @see BinaryXmlPullParser.getAttributeLong
- */
+/** @see BinaryXmlPullParser.getAttributeLong */
@Suppress("NOTHING_TO_INLINE")
@Throws(XmlPullParserException::class)
inline fun BinaryXmlPullParser.getAttributeLongOrThrow(name: String): Long =
getAttributeLong(null, name)
-/**
- * @see BinaryXmlPullParser.getAttributeLongHex
- */
+/** @see BinaryXmlPullParser.getAttributeLongHex */
@Suppress("NOTHING_TO_INLINE")
inline fun BinaryXmlPullParser.getAttributeLongHexOrDefault(
name: String,
defaultValue: Long
): Long = getAttributeLongHex(null, name, defaultValue)
-/**
- * @see BinaryXmlPullParser.getAttributeLongHex
- */
+/** @see BinaryXmlPullParser.getAttributeLongHex */
@Suppress("NOTHING_TO_INLINE")
@Throws(XmlPullParserException::class)
inline fun BinaryXmlPullParser.getAttributeLongHexOrThrow(name: String): Long =
getAttributeLongHex(null, name)
-/**
- * @see BinaryXmlPullParser.getAttributeFloat
- */
+/** @see BinaryXmlPullParser.getAttributeFloat */
@Suppress("NOTHING_TO_INLINE")
inline fun BinaryXmlPullParser.getAttributeFloatOrDefault(
name: String,
defaultValue: Float
): Float = getAttributeFloat(null, name, defaultValue)
-/**
- * @see BinaryXmlPullParser.getAttributeFloat
- */
+/** @see BinaryXmlPullParser.getAttributeFloat */
@Suppress("NOTHING_TO_INLINE")
@Throws(XmlPullParserException::class)
inline fun BinaryXmlPullParser.getAttributeFloatOrThrow(name: String): Float =
getAttributeFloat(null, name)
-/**
- * @see BinaryXmlPullParser.getAttributeDouble
- */
+/** @see BinaryXmlPullParser.getAttributeDouble */
@Suppress("NOTHING_TO_INLINE")
inline fun BinaryXmlPullParser.getAttributeDoubleOrDefault(
name: String,
defaultValue: Double
): Double = getAttributeDouble(null, name, defaultValue)
-/**
- * @see BinaryXmlPullParser.getAttributeDouble
- */
+/** @see BinaryXmlPullParser.getAttributeDouble */
@Suppress("NOTHING_TO_INLINE")
@Throws(XmlPullParserException::class)
inline fun BinaryXmlPullParser.getAttributeDoubleOrThrow(name: String): Double =
getAttributeDouble(null, name)
-/**
- * @see BinaryXmlPullParser.getAttributeBoolean
- */
+/** @see BinaryXmlPullParser.getAttributeBoolean */
@Suppress("NOTHING_TO_INLINE")
inline fun BinaryXmlPullParser.getAttributeBooleanOrDefault(
name: String,
defaultValue: Boolean
): Boolean = getAttributeBoolean(null, name, defaultValue)
-/**
- * @see BinaryXmlPullParser.getAttributeBoolean
- */
+/** @see BinaryXmlPullParser.getAttributeBoolean */
@Suppress("NOTHING_TO_INLINE")
@Throws(XmlPullParserException::class)
inline fun BinaryXmlPullParser.getAttributeBooleanOrThrow(name: String): Boolean =
diff --git a/services/permission/java/com/android/server/permission/access/util/BinaryXmlSerializerExtensions.kt b/services/permission/java/com/android/server/permission/access/util/BinaryXmlSerializerExtensions.kt
index c8cd586..6500a7d 100644
--- a/services/permission/java/com/android/server/permission/access/util/BinaryXmlSerializerExtensions.kt
+++ b/services/permission/java/com/android/server/permission/access/util/BinaryXmlSerializerExtensions.kt
@@ -20,9 +20,7 @@
import java.io.IOException
import java.io.OutputStream
-/**
- * Serialize content into [OutputStream] with [BinaryXmlSerializer].
- */
+/** Serialize content into [OutputStream] with [BinaryXmlSerializer]. */
@Throws(IOException::class)
inline fun OutputStream.serializeBinaryXml(block: BinaryXmlSerializer.() -> Unit) {
BinaryXmlSerializer().apply {
@@ -57,54 +55,42 @@
endTag(null, name)
}
-/**
- * @see BinaryXmlSerializer.attribute
- */
+/** @see BinaryXmlSerializer.attribute */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attribute(name: String, value: String) {
attribute(null, name, value)
}
-/**
- * @see BinaryXmlSerializer.attributeInterned
- */
+/** @see BinaryXmlSerializer.attributeInterned */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attributeInterned(name: String, value: String) {
attributeInterned(null, name, value)
}
-/**
- * @see BinaryXmlSerializer.attributeBytesHex
- */
+/** @see BinaryXmlSerializer.attributeBytesHex */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attributeBytesHex(name: String, value: ByteArray) {
attributeBytesHex(null, name, value)
}
-/**
- * @see BinaryXmlSerializer.attributeBytesBase64
- */
+/** @see BinaryXmlSerializer.attributeBytesBase64 */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attributeBytesBase64(name: String, value: ByteArray) {
attributeBytesBase64(null, name, value)
}
-/**
- * @see BinaryXmlSerializer.attributeInt
- */
+/** @see BinaryXmlSerializer.attributeInt */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attributeInt(name: String, value: Int) {
attributeInt(null, name, value)
}
-/**
- * @see BinaryXmlSerializer.attributeInt
- */
+/** @see BinaryXmlSerializer.attributeInt */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attributeIntWithDefault(
@@ -117,18 +103,14 @@
}
}
-/**
- * @see BinaryXmlSerializer.attributeIntHex
- */
+/** @see BinaryXmlSerializer.attributeIntHex */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attributeIntHex(name: String, value: Int) {
attributeIntHex(null, name, value)
}
-/**
- * @see BinaryXmlSerializer.attributeIntHex
- */
+/** @see BinaryXmlSerializer.attributeIntHex */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attributeIntHexWithDefault(
@@ -141,18 +123,14 @@
}
}
-/**
- * @see BinaryXmlSerializer.attributeLong
- */
+/** @see BinaryXmlSerializer.attributeLong */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attributeLong(name: String, value: Long) {
attributeLong(null, name, value)
}
-/**
- * @see BinaryXmlSerializer.attributeLong
- */
+/** @see BinaryXmlSerializer.attributeLong */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attributeLongWithDefault(
@@ -165,18 +143,14 @@
}
}
-/**
- * @see BinaryXmlSerializer.attributeLongHex
- */
+/** @see BinaryXmlSerializer.attributeLongHex */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attributeLongHex(name: String, value: Long) {
attributeLongHex(null, name, value)
}
-/**
- * @see BinaryXmlSerializer.attributeLongHex
- */
+/** @see BinaryXmlSerializer.attributeLongHex */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attributeLongHexWithDefault(
@@ -189,18 +163,14 @@
}
}
-/**
- * @see BinaryXmlSerializer.attributeFloat
- */
+/** @see BinaryXmlSerializer.attributeFloat */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attributeFloat(name: String, value: Float) {
attributeFloat(null, name, value)
}
-/**
- * @see BinaryXmlSerializer.attributeFloat
- */
+/** @see BinaryXmlSerializer.attributeFloat */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attributeFloatWithDefault(
@@ -213,18 +183,14 @@
}
}
-/**
- * @see BinaryXmlSerializer.attributeDouble
- */
+/** @see BinaryXmlSerializer.attributeDouble */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attributeDouble(name: String, value: Double) {
attributeDouble(null, name, value)
}
-/**
- * @see BinaryXmlSerializer.attributeDouble
- */
+/** @see BinaryXmlSerializer.attributeDouble */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attributeDoubleWithDefault(
@@ -237,18 +203,14 @@
}
}
-/**
- * @see BinaryXmlSerializer.attributeBoolean
- */
+/** @see BinaryXmlSerializer.attributeBoolean */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attributeBoolean(name: String, value: Boolean) {
attributeBoolean(null, name, value)
}
-/**
- * @see BinaryXmlSerializer.attributeBoolean
- */
+/** @see BinaryXmlSerializer.attributeBoolean */
@Suppress("NOTHING_TO_INLINE")
@Throws(IOException::class)
inline fun BinaryXmlSerializer.attributeBooleanWithDefault(
diff --git a/services/permission/java/com/android/server/permission/access/util/PackageVersionMigration.kt b/services/permission/java/com/android/server/permission/access/util/PackageVersionMigration.kt
index a61489c..3ef284b 100644
--- a/services/permission/java/com/android/server/permission/access/util/PackageVersionMigration.kt
+++ b/services/permission/java/com/android/server/permission/access/util/PackageVersionMigration.kt
@@ -22,13 +22,13 @@
object PackageVersionMigration {
/**
- * Maps existing permission and app-op version to a unified version during OTA upgrade. The
- * new unified version is used in determining the upgrade steps for a package (for both
- * permission and app-ops).
+ * Maps existing permission and app-op version to a unified version during OTA upgrade. The new
+ * unified version is used in determining the upgrade steps for a package (for both permission
+ * and app-ops).
*
* @return unified permission/app-op version
* @throws IllegalStateException if the method is called when there is nothing to migrate i.e.
- * permission and app-op file does not exist.
+ * permission and app-op file does not exist.
*/
internal fun getVersion(userId: Int): Int {
val permissionMigrationHelper =
diff --git a/services/permission/java/com/android/server/permission/access/util/PermissionApex.kt b/services/permission/java/com/android/server/permission/access/util/PermissionApex.kt
index e6b4e3e..3d835e8 100644
--- a/services/permission/java/com/android/server/permission/access/util/PermissionApex.kt
+++ b/services/permission/java/com/android/server/permission/access/util/PermissionApex.kt
@@ -23,15 +23,11 @@
object PermissionApex {
private const val MODULE_NAME = "com.android.permission"
- /**
- * @see ApexEnvironment.getDeviceProtectedDataDir
- */
+ /** @see ApexEnvironment.getDeviceProtectedDataDir */
val systemDataDirectory: File
get() = apexEnvironment.deviceProtectedDataDir
- /**
- * @see ApexEnvironment.getDeviceProtectedDataDirForUser
- */
+ /** @see ApexEnvironment.getDeviceProtectedDataDirForUser */
fun getUserDataDirectory(userId: Int): File =
apexEnvironment.getDeviceProtectedDataDirForUser(UserHandle.of(userId))
diff --git a/services/tests/displayservicetests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java
index c280349..79222c0 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java
@@ -23,6 +23,7 @@
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -37,6 +38,7 @@
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.internal.R;
+import com.android.server.display.feature.DisplayManagerFlags;
import org.junit.After;
import org.junit.Before;
@@ -54,6 +56,8 @@
private Resources mMockedResources;
@Mock
private DisplayManagerInternal mDisplayManagerInternal;
+ @Mock
+ private DisplayManagerFlags mDisplayManagerFlagsMock;
private MockitoSession mSession;
private Resources mResources;
@@ -63,10 +67,6 @@
@Before
public void setUp() {
DisplayManagerInternal displayManagerInternal = mock(DisplayManagerInternal.class);
- mDisplayWhiteBalanceTintController =
- new DisplayWhiteBalanceTintController(displayManagerInternal);
- mDisplayWhiteBalanceTintController.setUp(InstrumentationRegistry.getContext(), true);
- mDisplayWhiteBalanceTintController.setActivated(true);
mSession = ExtendedMockito.mockitoSession()
.initMocks(this)
@@ -74,6 +74,13 @@
.strictness(Strictness.LENIENT)
.startMocking();
+ mDisplayWhiteBalanceTintController =
+ new DisplayWhiteBalanceTintController(displayManagerInternal,
+ mDisplayManagerFlagsMock);
+ mDisplayWhiteBalanceTintController.setUp(InstrumentationRegistry.getContext(), true);
+ mDisplayWhiteBalanceTintController.setActivated(true);
+
+
mResources = InstrumentationRegistry.getContext().getResources();
// These Resources are common to all tests.
doReturn(4000)
@@ -360,9 +367,47 @@
1e-6f /* tolerance */);
}
+ @Test
+ public void testDisplayWhiteBalance_TransitionTimes() {
+ when(mDisplayManagerFlagsMock.isAdaptiveTone2Enabled()).thenReturn(false);
+ setUpTransitionTimes();
+ setUpTintController();
+
+ assertEquals(30L,
+ mDisplayWhiteBalanceTintController.getTransitionDurationMilliseconds(true));
+ assertEquals(30L,
+ mDisplayWhiteBalanceTintController.getTransitionDurationMilliseconds(false));
+ }
+
+ @Test
+ public void testDisplayWhiteBalance_TransitionTimesDirectional() {
+ when(mDisplayManagerFlagsMock.isAdaptiveTone2Enabled()).thenReturn(true);
+ setUpTransitionTimes();
+ setUpTintController();
+
+ assertEquals(400L,
+ mDisplayWhiteBalanceTintController.getTransitionDurationMilliseconds(true));
+ assertEquals(5000L,
+ mDisplayWhiteBalanceTintController.getTransitionDurationMilliseconds(false));
+ }
+
+
+ private void setUpTransitionTimes() {
+ doReturn(mResources.getStringArray(R.array.config_displayWhiteBalanceDisplayPrimaries))
+ .when(mMockedResources)
+ .getStringArray(R.array.config_displayWhiteBalanceDisplayPrimaries);
+ when(mMockedResources.getInteger(
+ R.integer.config_displayWhiteBalanceTransitionTime)).thenReturn(30);
+ when(mMockedResources.getInteger(
+ R.integer.config_displayWhiteBalanceTransitionTimeIncrease)).thenReturn(400);
+ when(mMockedResources.getInteger(
+ R.integer.config_displayWhiteBalanceTransitionTimeDecrease)).thenReturn(5000);
+
+ }
+
private void setUpTintController() {
mDisplayWhiteBalanceTintController = new DisplayWhiteBalanceTintController(
- mDisplayManagerInternal);
+ mDisplayManagerInternal, mDisplayManagerFlagsMock);
mDisplayWhiteBalanceTintController.setUp(mMockedContext, true);
mDisplayWhiteBalanceTintController.setActivated(true);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
index 7cc01e1..4329b6f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
@@ -160,6 +160,8 @@
mPrefetchController = new PrefetchController(mJobSchedulerService);
mPcConstants = mPrefetchController.getPcConstants();
+ setDeviceConfigLong(PcConstants.KEY_LAUNCH_TIME_THRESHOLD_MS, 7 * HOUR_IN_MILLIS);
+
setUidBias(Process.myUid(), JobInfo.BIAS_DEFAULT);
verify(mUsageStatsManagerInternal)
diff --git a/services/tests/servicestests/res/xml/usertypes_test_profile.xml b/services/tests/servicestests/res/xml/usertypes_test_profile.xml
index 0115db6..ef19ba1 100644
--- a/services/tests/servicestests/res/xml/usertypes_test_profile.xml
+++ b/services/tests/servicestests/res/xml/usertypes_test_profile.xml
@@ -40,6 +40,7 @@
mediaSharedWithParent='true'
credentialShareableWithParent='false'
showInSettings='23'
+ hideInSettingsInQuietMode='true'
inheritDevicePolicy='450'
deleteAppWithParent='false'
alwaysVisible='true'
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/FlashNotificationsControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/FlashNotificationsControllerTest.java
index 8a057df..0988eea 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/FlashNotificationsControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/FlashNotificationsControllerTest.java
@@ -366,6 +366,7 @@
private void assumeCameraTorchAvailable() {
assumeTrue(mCameraManager != null);
assumeTrue(!mCameraInfoMap.isEmpty());
+ assumeTrue(mCameraInfoMap.values().stream().anyMatch(info -> info.mIsValid));
}
private void simulateCallSequence() throws InterruptedException {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 430f600..caa9e7c 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -1065,7 +1065,7 @@
mMgh.clearAndTransitionToStateDetecting();
break;
case STATE_ACTIVATED:
- if (mMgh.mDetectTripleTap) {
+ if (mMgh.mDetectSingleFingerTripleTap) {
goFromStateIdleTo(STATE_2TAPS);
tap();
} else {
@@ -1147,7 +1147,7 @@
break;
case STATE_ACTIVATED:
case STATE_ZOOMED_OUT_FROM_SERVICE:
- if (mMgh.mDetectTripleTap) {
+ if (mMgh.mDetectSingleFingerTripleTap) {
tap();
tap();
returnToNormalFrom(STATE_ACTIVATED_2TAPS);
diff --git a/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
index e7777f75..67b70684 100644
--- a/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
@@ -436,6 +436,24 @@
verify(mMockRemoteContentProtectionService).onLoginDetected(PARCELED_EVENTS);
}
+ @Test
+ public void parseContentProtectionGroupsConfig_null() {
+ ContentCaptureManagerService service = new ContentCaptureManagerService(sContext);
+ assertThat(service.parseContentProtectionGroupsConfig(null)).isEmpty();
+ }
+
+ @Test
+ public void parseContentProtectionGroupsConfig_empty() {
+ ContentCaptureManagerService service = new ContentCaptureManagerService(sContext);
+ assertThat(service.parseContentProtectionGroupsConfig("")).isEmpty();
+ }
+
+ @Test
+ public void parseContentProtectionGroupsConfig_notEmpty() {
+ ContentCaptureManagerService service = new ContentCaptureManagerService(sContext);
+ assertThat(service.parseContentProtectionGroupsConfig("a")).isEmpty();
+ }
+
private class TestContentCaptureManagerService extends ContentCaptureManagerService {
TestContentCaptureManagerService() {
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index 7d73563..7dcfc88 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -284,6 +284,7 @@
assertEquals(info.currentState, DEFAULT_DEVICE_STATE.getIdentifier());
}
+ @FlakyTest(bugId = 297949293)
@Test
public void getDeviceStateInfo_baseStateAndCommittedStateNotSet() throws RemoteException {
// Create a provider and a service without an initial base state.
diff --git a/services/tests/servicestests/src/com/android/server/net/LockdownVpnTrackerTest.java b/services/tests/servicestests/src/com/android/server/net/LockdownVpnTrackerTest.java
new file mode 100644
index 0000000..949f8e7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/net/LockdownVpnTrackerTest.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net;
+
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.VpnManager.NOTIFICATION_CHANNEL_VPN;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.text.TextUtils;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.R;
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.internal.net.VpnConfig;
+import com.android.internal.net.VpnProfile;
+import com.android.server.connectivity.Vpn;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class LockdownVpnTrackerTest {
+ private static final NetworkCapabilities TEST_CELL_NC = new NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
+ .build();
+ private static final LinkProperties TEST_CELL_LP = new LinkProperties();
+
+ static {
+ TEST_CELL_LP.setInterfaceName("rmnet0");
+ TEST_CELL_LP.addLinkAddress(new LinkAddress("192.0.2.2/25"));
+ }
+
+ // Use a context wrapper instead of a mock since LockdownVpnTracker builds notifications which
+ // is tedious and currently unnecessary to mock.
+ private final Context mContext = new ContextWrapper(InstrumentationRegistry.getContext()) {
+ @Override
+ public Object getSystemService(String name) {
+ if (Context.CONNECTIVITY_SERVICE.equals(name)) return mCm;
+ if (Context.NOTIFICATION_SERVICE.equals(name)) return mNotificationManager;
+
+ return super.getSystemService(name);
+ }
+ };
+ @Mock private ConnectivityManager mCm;
+ @Mock private Vpn mVpn;
+ @Mock private NotificationManager mNotificationManager;
+ @Mock private NetworkInfo mVpnNetworkInfo;
+ @Mock private VpnConfig mVpnConfig;
+ @Mock private Network mNetwork;
+ @Mock private Network mNetwork2;
+ @Mock private Network mVpnNetwork;
+
+ private HandlerThread mHandlerThread;
+ private Handler mHandler;
+ private VpnProfile mProfile;
+
+ private VpnProfile createTestVpnProfile() {
+ final String profileName = "testVpnProfile";
+ final VpnProfile profile = new VpnProfile(profileName);
+ profile.name = "My VPN";
+ profile.server = "192.0.2.1";
+ profile.dnsServers = "8.8.8.8";
+ profile.ipsecIdentifier = "My ipsecIdentifier";
+ profile.ipsecSecret = "My PSK";
+ profile.type = VpnProfile.TYPE_IKEV2_IPSEC_PSK;
+
+ return profile;
+ }
+
+ private NetworkCallback getDefaultNetworkCallback() {
+ final ArgumentCaptor<NetworkCallback> callbackCaptor =
+ ArgumentCaptor.forClass(NetworkCallback.class);
+ verify(mCm).registerSystemDefaultNetworkCallback(callbackCaptor.capture(), eq(mHandler));
+ return callbackCaptor.getValue();
+ }
+
+ private NetworkCallback getVpnNetworkCallback() {
+ final ArgumentCaptor<NetworkCallback> callbackCaptor =
+ ArgumentCaptor.forClass(NetworkCallback.class);
+ verify(mCm).registerNetworkCallback(any(), callbackCaptor.capture(), eq(mHandler));
+ return callbackCaptor.getValue();
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mHandlerThread = new HandlerThread("LockdownVpnTrackerTest");
+ mHandlerThread.start();
+ mHandler = mHandlerThread.getThreadHandler();
+
+ doReturn(mVpnNetworkInfo).when(mVpn).getNetworkInfo();
+ doReturn(false).when(mVpnNetworkInfo).isConnectedOrConnecting();
+ doReturn(mVpnConfig).when(mVpn).getLegacyVpnConfig();
+ // mVpnConfig is a mock but the production code will try to add addresses in this array
+ // assuming it's non-null, so it needs to be initialized.
+ mVpnConfig.addresses = new ArrayList<>();
+
+ mProfile = createTestVpnProfile();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mHandlerThread != null) {
+ mHandlerThread.quitSafely();
+ mHandlerThread.join();
+ }
+ }
+
+ private LockdownVpnTracker initAndVerifyLockdownVpnTracker() {
+ final LockdownVpnTracker lockdownVpnTracker =
+ new LockdownVpnTracker(mContext, mHandler, mVpn, mProfile);
+ lockdownVpnTracker.init();
+ verify(mVpn).setEnableTeardown(false);
+ verify(mVpn).setLockdown(true);
+ verify(mCm).setLegacyLockdownVpnEnabled(true);
+ verify(mVpn).stopVpnRunnerPrivileged();
+ verify(mNotificationManager).cancel(any(), eq(SystemMessage.NOTE_VPN_STATUS));
+
+ return lockdownVpnTracker;
+ }
+
+ private void callCallbacksForNetworkConnect(NetworkCallback callback, Network network,
+ NetworkCapabilities nc, LinkProperties lp, boolean blocked) {
+ callback.onAvailable(network);
+ callback.onCapabilitiesChanged(network, nc);
+ callback.onLinkPropertiesChanged(network, lp);
+ callback.onBlockedStatusChanged(network, blocked);
+ }
+
+ private void callCallbacksForNetworkConnect(NetworkCallback callback, Network network) {
+ callCallbacksForNetworkConnect(
+ callback, network, TEST_CELL_NC, TEST_CELL_LP, true /* blocked */);
+ }
+
+ private boolean isExpectedNotification(Notification notification, int titleRes, int iconRes) {
+ if (!NOTIFICATION_CHANNEL_VPN.equals(notification.getChannelId())) {
+ return false;
+ }
+ final CharSequence expectedTitle = mContext.getString(titleRes);
+ final CharSequence actualTitle = notification.extras.getCharSequence(
+ Notification.EXTRA_TITLE);
+ if (!TextUtils.equals(expectedTitle, actualTitle)) {
+ return false;
+ }
+ return notification.getSmallIcon().getResId() == iconRes;
+ }
+
+ @Test
+ public void testShutdown() {
+ final LockdownVpnTracker lockdownVpnTracker = initAndVerifyLockdownVpnTracker();
+ final NetworkCallback defaultCallback = getDefaultNetworkCallback();
+ final NetworkCallback vpnCallback = getVpnNetworkCallback();
+ clearInvocations(mVpn, mCm, mNotificationManager);
+
+ lockdownVpnTracker.shutdown();
+ verify(mVpn).stopVpnRunnerPrivileged();
+ verify(mVpn).setLockdown(false);
+ verify(mCm).setLegacyLockdownVpnEnabled(false);
+ verify(mNotificationManager).cancel(any(), eq(SystemMessage.NOTE_VPN_STATUS));
+ verify(mVpn).setEnableTeardown(true);
+ verify(mCm).unregisterNetworkCallback(defaultCallback);
+ verify(mCm).unregisterNetworkCallback(vpnCallback);
+ }
+
+ @Test
+ public void testDefaultNetworkConnected() {
+ initAndVerifyLockdownVpnTracker();
+ final NetworkCallback defaultCallback = getDefaultNetworkCallback();
+ clearInvocations(mVpn, mCm, mNotificationManager);
+
+ // mNetwork connected and available.
+ callCallbacksForNetworkConnect(defaultCallback, mNetwork);
+
+ // Vpn is starting
+ verify(mVpn).startLegacyVpnPrivileged(mProfile, mNetwork, TEST_CELL_LP);
+ verify(mNotificationManager).notify(any(), eq(SystemMessage.NOTE_VPN_STATUS),
+ argThat(notification -> isExpectedNotification(notification,
+ R.string.vpn_lockdown_connecting, R.drawable.vpn_disconnected)));
+ }
+
+ private void doTestDefaultLpChanged(LinkProperties startingLp, LinkProperties newLp) {
+ initAndVerifyLockdownVpnTracker();
+ final NetworkCallback defaultCallback = getDefaultNetworkCallback();
+ callCallbacksForNetworkConnect(
+ defaultCallback, mNetwork, TEST_CELL_NC, startingLp, true /* blocked */);
+ clearInvocations(mVpn, mCm, mNotificationManager);
+
+ // LockdownVpnTracker#handleStateChangedLocked() is not called on the same network even if
+ // the LinkProperties change.
+ defaultCallback.onLinkPropertiesChanged(mNetwork, newLp);
+
+ // Ideally the VPN should start if it hasn't already, but it doesn't because nothing calls
+ // LockdownVpnTracker#handleStateChangedLocked. This is a bug.
+ // TODO: consider fixing this.
+ verify(mVpn, never()).stopVpnRunnerPrivileged();
+ verify(mVpn, never()).startLegacyVpnPrivileged(any(), any(), any());
+ verify(mNotificationManager, never()).cancel(any(), eq(SystemMessage.NOTE_VPN_STATUS));
+ }
+
+ @Test
+ public void testDefaultLPChanged_V4AddLinkAddressV4() {
+ final LinkProperties lp = new LinkProperties(TEST_CELL_LP);
+ lp.setInterfaceName("rmnet0");
+ lp.addLinkAddress(new LinkAddress("192.0.2.3/25"));
+ doTestDefaultLpChanged(TEST_CELL_LP, lp);
+ }
+
+ @Test
+ public void testDefaultLPChanged_V4AddLinkAddressV6() {
+ final LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName("rmnet0");
+ lp.addLinkAddress(new LinkAddress("192.0.2.3/25"));
+ final LinkProperties newLp = new LinkProperties(lp);
+ newLp.addLinkAddress(new LinkAddress("2001:db8::1/64"));
+ doTestDefaultLpChanged(lp, newLp);
+ }
+
+ @Test
+ public void testDefaultLPChanged_V6AddLinkAddressV4() {
+ final LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName("rmnet0");
+ lp.addLinkAddress(new LinkAddress("2001:db8::1/64"));
+ final LinkProperties newLp = new LinkProperties(lp);
+ newLp.addLinkAddress(new LinkAddress("192.0.2.3/25"));
+ doTestDefaultLpChanged(lp, newLp);
+ }
+
+ @Test
+ public void testDefaultLPChanged_AddLinkAddressV4() {
+ final LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName("rmnet0");
+ doTestDefaultLpChanged(lp, TEST_CELL_LP);
+ }
+
+ @Test
+ public void testDefaultNetworkChanged() {
+ initAndVerifyLockdownVpnTracker();
+ final NetworkCallback defaultCallback = getDefaultNetworkCallback();
+ final NetworkCallback vpnCallback = getVpnNetworkCallback();
+ callCallbacksForNetworkConnect(defaultCallback, mNetwork);
+ clearInvocations(mVpn, mCm, mNotificationManager);
+
+ // New network and LinkProperties received
+ final NetworkCapabilities wifiNc = new NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_WIFI)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
+ .build();
+ final LinkProperties wifiLp = new LinkProperties();
+ wifiLp.setInterfaceName("wlan0");
+ callCallbacksForNetworkConnect(
+ defaultCallback, mNetwork2, wifiNc, wifiLp, true /* blocked */);
+
+ // Vpn is restarted.
+ verify(mVpn).stopVpnRunnerPrivileged();
+ verify(mVpn).startLegacyVpnPrivileged(mProfile, mNetwork2, wifiLp);
+ verify(mNotificationManager, never()).cancel(any(), eq(SystemMessage.NOTE_VPN_STATUS));
+ verify(mNotificationManager).notify(any(), eq(SystemMessage.NOTE_VPN_STATUS),
+ argThat(notification -> isExpectedNotification(notification,
+ R.string.vpn_lockdown_connecting, R.drawable.vpn_disconnected)));
+
+ // Vpn is Connected
+ doReturn(true).when(mVpnNetworkInfo).isConnectedOrConnecting();
+ doReturn(true).when(mVpnNetworkInfo).isConnected();
+ vpnCallback.onAvailable(mVpnNetwork);
+ verify(mNotificationManager).notify(any(), eq(SystemMessage.NOTE_VPN_STATUS),
+ argThat(notification -> isExpectedNotification(notification,
+ R.string.vpn_lockdown_connected, R.drawable.vpn_connected)));
+
+ }
+
+ @Test
+ public void testSystemDefaultLost() {
+ initAndVerifyLockdownVpnTracker();
+ final NetworkCallback defaultCallback = getDefaultNetworkCallback();
+ // mNetwork connected
+ callCallbacksForNetworkConnect(defaultCallback, mNetwork);
+ clearInvocations(mVpn, mCm, mNotificationManager);
+
+ defaultCallback.onLost(mNetwork);
+
+ // Vpn is stopped
+ verify(mVpn).stopVpnRunnerPrivileged();
+ verify(mNotificationManager).cancel(any(), eq(SystemMessage.NOTE_VPN_STATUS));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
index a54bc91..c684a7b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
@@ -60,6 +60,7 @@
.setShowInLauncher(21)
.setStartWithParent(false)
.setShowInSettings(45)
+ .setHideInSettingsInQuietMode(false)
.setInheritDevicePolicy(67)
.setUseParentsContacts(false)
.setCrossProfileIntentFilterAccessControl(10)
@@ -72,6 +73,7 @@
final UserProperties actualProps = new UserProperties(defaultProps);
actualProps.setShowInLauncher(14);
actualProps.setShowInSettings(32);
+ actualProps.setHideInSettingsInQuietMode(true);
actualProps.setInheritDevicePolicy(51);
actualProps.setUseParentsContacts(true);
actualProps.setCrossProfileIntentFilterAccessControl(20);
@@ -228,6 +230,8 @@
assertThat(expected.getShowInLauncher()).isEqualTo(actual.getShowInLauncher());
assertThat(expected.getStartWithParent()).isEqualTo(actual.getStartWithParent());
assertThat(expected.getShowInSettings()).isEqualTo(actual.getShowInSettings());
+ assertThat(expected.getHideInSettingsInQuietMode())
+ .isEqualTo(actual.getHideInSettingsInQuietMode());
assertThat(expected.getInheritDevicePolicy()).isEqualTo(actual.getInheritDevicePolicy());
assertThat(expected.getUseParentsContacts()).isEqualTo(actual.getUseParentsContacts());
assertThat(expected.getCrossProfileIntentFilterAccessControl())
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
index e3579b4..20270a8 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
@@ -90,6 +90,7 @@
.setMediaSharedWithParent(true)
.setCredentialShareableWithParent(false)
.setShowInSettings(900)
+ .setHideInSettingsInQuietMode(true)
.setInheritDevicePolicy(340)
.setDeleteAppWithParent(true)
.setAlwaysVisible(true);
@@ -160,6 +161,7 @@
assertTrue(type.getDefaultUserPropertiesReference().isMediaSharedWithParent());
assertFalse(type.getDefaultUserPropertiesReference().isCredentialShareableWithParent());
assertEquals(900, type.getDefaultUserPropertiesReference().getShowInSettings());
+ assertTrue(type.getDefaultUserPropertiesReference().getHideInSettingsInQuietMode());
assertEquals(340, type.getDefaultUserPropertiesReference()
.getInheritDevicePolicy());
assertTrue(type.getDefaultUserPropertiesReference().getDeleteAppWithParent());
@@ -217,6 +219,7 @@
assertFalse(props.isCredentialShareableWithParent());
assertFalse(props.getDeleteAppWithParent());
assertFalse(props.getAlwaysVisible());
+ assertFalse(props.getHideInSettingsInQuietMode());
assertFalse(type.hasBadge());
}
@@ -304,6 +307,7 @@
.setMediaSharedWithParent(false)
.setCredentialShareableWithParent(true)
.setShowInSettings(20)
+ .setHideInSettingsInQuietMode(false)
.setInheritDevicePolicy(21)
.setDeleteAppWithParent(true)
.setAlwaysVisible(false);
@@ -344,6 +348,7 @@
assertTrue(aospType.getDefaultUserPropertiesReference()
.isCredentialShareableWithParent());
assertEquals(20, aospType.getDefaultUserPropertiesReference().getShowInSettings());
+ assertFalse(aospType.getDefaultUserPropertiesReference().getHideInSettingsInQuietMode());
assertEquals(21, aospType.getDefaultUserPropertiesReference()
.getInheritDevicePolicy());
assertTrue(aospType.getDefaultUserPropertiesReference().getDeleteAppWithParent());
@@ -390,6 +395,7 @@
assertFalse(aospType.getDefaultUserPropertiesReference()
.isCredentialShareableWithParent());
assertEquals(23, aospType.getDefaultUserPropertiesReference().getShowInSettings());
+ assertTrue(aospType.getDefaultUserPropertiesReference().getHideInSettingsInQuietMode());
assertEquals(450, aospType.getDefaultUserPropertiesReference()
.getInheritDevicePolicy());
assertFalse(aospType.getDefaultUserPropertiesReference().getDeleteAppWithParent());
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
index d1f4961..8891413 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -43,6 +43,8 @@
// TODO: remove once Android migrates to JUnit 4.12,
// which provides assertThrows
"testng",
+ "flag-junit",
+ "notification_flags_lib",
],
libs: [
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 9742384..42ad73a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -32,6 +32,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
+import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
@@ -147,6 +148,7 @@
private static final int CUSTOM_LIGHT_ON = 10000;
private static final int CUSTOM_LIGHT_OFF = 10000;
private static final int MAX_VIBRATION_DELAY = 1000;
+ private static final float DEFAULT_VOLUME = 1.0f;
@Before
public void setUp() throws Exception {
@@ -397,19 +399,22 @@
//
private void verifyNeverBeep() throws RemoteException {
- verify(mRingtonePlayer, never()).playAsync(any(), any(), anyBoolean(), any());
+ verify(mRingtonePlayer, never()).playAsync(any(), any(), anyBoolean(), any(), anyFloat());
}
private void verifyBeepUnlooped() throws RemoteException {
- verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(false), any());
+ verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(false), any(),
+ eq(DEFAULT_VOLUME));
}
private void verifyBeepLooped() throws RemoteException {
- verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(true), any());
+ verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(true), any(),
+ eq(DEFAULT_VOLUME));
}
private void verifyBeep(int times) throws RemoteException {
- verify(mRingtonePlayer, times(times)).playAsync(any(), any(), anyBoolean(), any());
+ verify(mRingtonePlayer, times(times)).playAsync(any(), any(), anyBoolean(), any(),
+ eq(DEFAULT_VOLUME));
}
private void verifyNeverStopAudio() throws RemoteException {
@@ -905,7 +910,7 @@
verifyDelayedVibrate(
mService.getVibratorHelper().createFallbackVibration(/* insistent= */ false));
verify(mRingtonePlayer, never()).playAsync
- (anyObject(), anyObject(), anyBoolean(), anyObject());
+ (anyObject(), anyObject(), anyBoolean(), anyObject(), anyFloat());
assertTrue(r.isInterruptive());
assertNotEquals(-1, r.getLastAudiblyAlertedMs());
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
index 81867df..9bd938f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
@@ -57,6 +57,7 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
import android.graphics.Color;
import android.graphics.drawable.Icon;
import android.media.AudioAttributes;
@@ -66,9 +67,11 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.os.UserManager;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
@@ -87,8 +90,11 @@
import com.android.server.lights.LightsManager;
import com.android.server.lights.LogicalLight;
import com.android.server.pm.PackageManagerService;
+
+import java.util.List;
import java.util.Objects;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -102,6 +108,8 @@
@RunWith(AndroidJUnit4.class)
@SuppressLint("GuardedBy")
public class NotificationAttentionHelperTest extends UiServiceTestCase {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Mock AudioManager mAudioManager;
@Mock Vibrator mVibrator;
@@ -115,6 +123,8 @@
IAccessibilityManager mAccessibilityService;
@Mock
KeyguardManager mKeyguardManager;
+ @Mock
+ private UserManager mUserManager;
NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake();
private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake(
1 << 30);
@@ -134,6 +144,8 @@
private AccessibilityManager mAccessibilityManager;
private static final NotificationAttentionHelper.Signals DEFAULT_SIGNALS =
new NotificationAttentionHelper.Signals(false, 0);
+ private static final NotificationAttentionHelper.Signals WORK_PROFILE_SIGNALS =
+ new NotificationAttentionHelper.Signals(true, 0);
private VibrateRepeatMatcher mVibrateOnceMatcher = new VibrateRepeatMatcher(-1);
private VibrateRepeatMatcher mVibrateLoopMatcher = new VibrateRepeatMatcher(0);
@@ -151,6 +163,7 @@
private static final int CUSTOM_LIGHT_ON = 10000;
private static final int CUSTOM_LIGHT_OFF = 10000;
private static final int MAX_VIBRATION_DELAY = 1000;
+ private static final float DEFAULT_VOLUME = 1.0f;
@Before
public void setUp() throws Exception {
@@ -178,6 +191,8 @@
// TODO (b/291907312): remove feature flag
mTestFlagResolver.setFlagOverride(NotificationFlags.ENABLE_ATTENTION_HELPER_REFACTOR, true);
+ // Disable feature flags by default. Tests should enable as needed.
+ mSetFlagsRule.disableFlags(Flags.FLAG_POLITE_NOTIFICATIONS, Flags.FLAG_EXPIRE_BITMAPS);
mService = spy(new NotificationManagerService(getContext(), mNotificationRecordLogger,
mNotificationInstanceIdSequence));
@@ -189,9 +204,9 @@
private void initAttentionHelper(TestableFlagResolver flagResolver) {
mAttentionHelper = new NotificationAttentionHelper(getContext(), mock(LightsManager.class),
- mAccessibilityManager, getContext().getPackageManager(), mUsageStats,
+ mAccessibilityManager, getContext().getPackageManager(), mUserManager, mUsageStats,
mService.mNotificationManagerPrivate, mock(ZenModeHelper.class), flagResolver);
- mAttentionHelper.setVibratorHelper(new VibratorHelper(getContext()));
+ mAttentionHelper.setVibratorHelper(spy(new VibratorHelper(getContext())));
mAttentionHelper.setAudioManager(mAudioManager);
mAttentionHelper.setSystemReady(true);
mAttentionHelper.setLights(mLight);
@@ -282,6 +297,11 @@
true /* noisy */, true /* buzzy*/, false /* lights */);
}
+ private NotificationRecord getBuzzyBeepyNotification(UserHandle userHandle) {
+ return getNotificationRecord(mId, false /* insistent */, false /* once */,
+ true /* noisy */, true /* buzzy*/, false /* lights */, userHandle);
+ }
+
private NotificationRecord getLightsNotification() {
return getNotificationRecord(mId, false /* insistent */, false /* once */,
false /* noisy */, false /* buzzy*/, true /* lights */);
@@ -312,7 +332,13 @@
private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once,
boolean noisy, boolean buzzy, boolean lights) {
return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, buzzy, noisy,
- lights, null, Notification.GROUP_ALERT_ALL, false);
+ lights, null, Notification.GROUP_ALERT_ALL, false, mUser);
+ }
+
+ private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once,
+ boolean noisy, boolean buzzy, boolean lights, UserHandle userHandle) {
+ return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, buzzy, noisy,
+ lights, null, Notification.GROUP_ALERT_ALL, false, userHandle);
}
private NotificationRecord getLeanbackNotificationRecord(int id, boolean insistent,
@@ -320,25 +346,25 @@
boolean noisy, boolean buzzy, boolean lights) {
return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, true, true,
true,
- null, Notification.GROUP_ALERT_ALL, true);
+ null, Notification.GROUP_ALERT_ALL, true, mUser);
}
private NotificationRecord getBeepyNotificationRecord(String groupKey, int groupAlertBehavior) {
return getNotificationRecord(mId, false, false, true, false, false, true, true, true,
- groupKey, groupAlertBehavior, false);
+ groupKey, groupAlertBehavior, false, mUser);
}
private NotificationRecord getLightsNotificationRecord(String groupKey,
int groupAlertBehavior) {
return getNotificationRecord(mId, false, false, false, false, true /*lights*/, true,
- true, true, groupKey, groupAlertBehavior, false);
+ true, true, groupKey, groupAlertBehavior, false, mUser);
}
private NotificationRecord getNotificationRecord(int id,
boolean insistent, boolean once,
boolean noisy, boolean buzzy, boolean lights, boolean defaultVibration,
boolean defaultSound, boolean defaultLights, String groupKey, int groupAlertBehavior,
- boolean isLeanback) {
+ boolean isLeanback, UserHandle userHandle) {
final Builder builder = new Builder(getContext())
.setContentTitle("foo")
@@ -399,7 +425,7 @@
.thenReturn(isLeanback);
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid,
- mPid, n, mUser, null, System.currentTimeMillis());
+ mPid, n, userHandle, null, System.currentTimeMillis());
NotificationRecord r = new NotificationRecord(context, sbn, mChannel);
mService.addNotification(r);
return r;
@@ -410,19 +436,26 @@
//
private void verifyNeverBeep() throws RemoteException {
- verify(mRingtonePlayer, never()).playAsync(any(), any(), anyBoolean(), any());
+ verify(mRingtonePlayer, never()).playAsync(any(), any(), anyBoolean(), any(), anyFloat());
}
private void verifyBeepUnlooped() throws RemoteException {
- verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(false), any());
+ verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(false), any(),
+ eq(DEFAULT_VOLUME));
}
private void verifyBeepLooped() throws RemoteException {
- verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(true), any());
+ verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(true), any(),
+ eq(DEFAULT_VOLUME));
}
private void verifyBeep(int times) throws RemoteException {
- verify(mRingtonePlayer, times(times)).playAsync(any(), any(), anyBoolean(), any());
+ verify(mRingtonePlayer, times(times)).playAsync(any(), any(), anyBoolean(), any(),
+ eq(DEFAULT_VOLUME));
+ }
+
+ private void verifyBeepVolume(float volume) throws RemoteException {
+ verify(mRingtonePlayer, times(1)).playAsync(any(), any(), anyBoolean(), any(), eq(volume));
}
private void verifyNeverStopAudio() throws RemoteException {
@@ -921,7 +954,7 @@
mAttentionHelper.getVibratorHelper().createFallbackVibration(
/* insistent= */ false));
verify(mRingtonePlayer, never()).playAsync(anyObject(), anyObject(), anyBoolean(),
- anyObject());
+ anyObject(), anyFloat());
assertTrue(r.isInterruptive());
assertNotEquals(-1, r.getLastAudiblyAlertedMs());
}
@@ -1948,6 +1981,259 @@
assertEquals(-1, r.getLastAudiblyAlertedMs());
}
+ // TODO b/270456865: Only one of the two strategies will be released.
+ // The other one need to be removed
+ @Test
+ public void testBeepVolume_politeNotif() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+ TestableFlagResolver flagResolver = new TestableFlagResolver();
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_COOLDOWN_RULE, "rule1");
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
+ initAttentionHelper(flagResolver);
+
+ NotificationRecord r = getBeepyNotification();
+
+ // set up internal state
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ Mockito.reset(mRingtonePlayer);
+
+ // update should beep at 50% volume
+ r.isUpdate = true;
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyBeepVolume(0.5f);
+
+ // 2nd update should beep at 0% volume
+ Mockito.reset(mRingtonePlayer);
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyBeepVolume(0.0f);
+
+ verify(mAccessibilityService, times(3)).sendAccessibilityEvent(any(), anyInt());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ // TODO b/270456865: Only one of the two strategies will be released.
+ // The other one need to be removed
+ @Test
+ public void testBeepVolume_politeNotif_Strategy2() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+ TestableFlagResolver flagResolver = new TestableFlagResolver();
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_COOLDOWN_RULE, "rule2");
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
+ initAttentionHelper(flagResolver);
+
+ NotificationRecord r = getBeepyNotification();
+
+ // set up internal state
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ Mockito.reset(mRingtonePlayer);
+
+ // update should beep at 0% volume
+ r.isUpdate = true;
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyBeepVolume(0.0f);
+
+ // 2nd update should beep at 50% volume
+ Mockito.reset(mRingtonePlayer);
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyBeepVolume(0.5f);
+
+ verify(mAccessibilityService, times(3)).sendAccessibilityEvent(any(), anyInt());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testVibrationIntensity_politeNotif() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+ TestableFlagResolver flagResolver = new TestableFlagResolver();
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_COOLDOWN_RULE, "rule1");
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
+ initAttentionHelper(flagResolver);
+
+ NotificationRecord r = getBuzzyBeepyNotification();
+
+ // set up internal state
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ VibratorHelper vibratorHelper = mAttentionHelper.getVibratorHelper();
+ Mockito.reset(vibratorHelper);
+
+ // update should buzz at 50% intensity
+ r.isUpdate = true;
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ verify(vibratorHelper, times(1)).scale(any(), eq(0.5f));
+ Mockito.reset(vibratorHelper);
+
+ // 2nd update should buzz at 0% intensity
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verify(vibratorHelper, times(1)).scale(any(), eq(0.0f));
+ }
+
+ @Test
+ public void testVibrationIntensity_politeNotif_Strategy2() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+ TestableFlagResolver flagResolver = new TestableFlagResolver();
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_COOLDOWN_RULE, "rule2");
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
+ initAttentionHelper(flagResolver);
+
+ NotificationRecord r = getBuzzyBeepyNotification();
+
+ // set up internal state
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ VibratorHelper vibratorHelper = mAttentionHelper.getVibratorHelper();
+ Mockito.reset(vibratorHelper);
+
+ // update should buzz at 0% intensity
+ r.isUpdate = true;
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ verify(vibratorHelper, times(1)).scale(any(), eq(0.0f));
+ Mockito.reset(vibratorHelper);
+
+ // 2nd update should buzz at 50% intensity
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verify(vibratorHelper, times(1)).scale(any(), eq(0.5f));
+ }
+
+ @Test
+ public void testBuzzOnlyOnScreenUnlock_politeNotif() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+ TestableFlagResolver flagResolver = new TestableFlagResolver();
+
+ // When NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED setting is enabled
+ Settings.System.putInt(getContext().getContentResolver(),
+ Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED, 1);
+
+ initAttentionHelper(flagResolver);
+ // And screen is unlocked
+ mAttentionHelper.setUserPresent(true);
+
+ NotificationRecord r = getBuzzyBeepyNotification();
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+
+ // The notification attention should only buzz
+ verifyNeverBeep();
+ verifyVibrate();
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testBeepVolume_politeNotif_disabled() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+ TestableFlagResolver flagResolver = new TestableFlagResolver();
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
+
+ // When NOTIFICATION_COOLDOWN_ENABLED setting is disabled
+ Settings.System.putInt(getContext().getContentResolver(),
+ Settings.System.NOTIFICATION_COOLDOWN_ENABLED, 0);
+
+ initAttentionHelper(flagResolver);
+
+ NotificationRecord r = getBeepyNotification();
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ Mockito.reset(mRingtonePlayer);
+
+ // update should beep at 100% volume
+ r.isUpdate = true;
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyBeepVolume(1.0f);
+
+ // 2nd update should beep at 100% volume
+ Mockito.reset(mRingtonePlayer);
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyBeepVolume(1.0f);
+
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testBeepVolume_politeNotif_workProfile() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+ TestableFlagResolver flagResolver = new TestableFlagResolver();
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_COOLDOWN_RULE, "rule1");
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
+
+ final int workProfileUserId = mUser.getIdentifier() + 1;
+
+ // Enable notifications cooldown for work profile
+ Settings.System.putIntForUser(getContext().getContentResolver(),
+ Settings.System.NOTIFICATION_COOLDOWN_ENABLED, 1, workProfileUserId);
+
+ when(mUserManager.getProfiles(mUser.getIdentifier())).thenReturn(
+ List.of(new UserInfo(workProfileUserId, "work_profile", null,
+ UserInfo.FLAG_PROFILE | UserInfo.FLAG_MANAGED_PROFILE,
+ UserInfo.getDefaultUserType(UserInfo.FLAG_MANAGED_PROFILE))));
+
+ initAttentionHelper(flagResolver);
+
+ final NotificationRecord r = getBuzzyBeepyNotification(UserHandle.of(workProfileUserId));
+
+ // set up internal state
+ mAttentionHelper.buzzBeepBlinkLocked(r, WORK_PROFILE_SIGNALS);
+ Mockito.reset(mRingtonePlayer);
+
+ // update should beep at 50% volume
+ r.isUpdate = true;
+ mAttentionHelper.buzzBeepBlinkLocked(r, WORK_PROFILE_SIGNALS);
+ verifyBeepVolume(0.5f);
+
+ // 2nd update should beep at 0% volume
+ Mockito.reset(mRingtonePlayer);
+ mAttentionHelper.buzzBeepBlinkLocked(r, WORK_PROFILE_SIGNALS);
+ verifyBeepVolume(0.0f);
+
+ verify(mAccessibilityService, times(3)).sendAccessibilityEvent(any(), anyInt());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testBeepVolume_politeNotif_workProfile_disabled() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+ TestableFlagResolver flagResolver = new TestableFlagResolver();
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_COOLDOWN_RULE, "rule1");
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
+
+ final int workProfileUserId = mUser.getIdentifier() + 1;
+
+ // Disable notifications cooldown for work profile
+ Settings.System.putIntForUser(getContext().getContentResolver(),
+ Settings.System.NOTIFICATION_COOLDOWN_ENABLED, 0, workProfileUserId);
+
+ when(mUserManager.getProfiles(mUser.getIdentifier())).thenReturn(
+ List.of(new UserInfo(workProfileUserId, "work_profile", null,
+ UserInfo.FLAG_PROFILE | UserInfo.FLAG_MANAGED_PROFILE,
+ UserInfo.getDefaultUserType(UserInfo.FLAG_MANAGED_PROFILE))));
+
+ initAttentionHelper(flagResolver);
+
+ final NotificationRecord r = getBuzzyBeepyNotification(UserHandle.of(workProfileUserId));
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, WORK_PROFILE_SIGNALS);
+ Mockito.reset(mRingtonePlayer);
+
+ // update should beep at 100% volume
+ r.isUpdate = true;
+ mAttentionHelper.buzzBeepBlinkLocked(r, WORK_PROFILE_SIGNALS);
+ verifyBeepVolume(1.0f);
+
+ // 2nd update should beep at 100% volume
+ Mockito.reset(mRingtonePlayer);
+ mAttentionHelper.buzzBeepBlinkLocked(r, WORK_PROFILE_SIGNALS);
+ verifyBeepVolume(1.0f);
+
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
static class VibrateRepeatMatcher implements ArgumentMatcher<VibrationEffect> {
private final int mRepeatIndex;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 91129a1..3d4b4a6 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -687,7 +687,11 @@
mPackageIntentReceiver = broadcastReceivers.get(i);
}
if (filter.hasAction(Intent.ACTION_USER_SWITCHED)) {
- mUserSwitchIntentReceiver = broadcastReceivers.get(i);
+ // There may be multiple receivers, get the NMS one
+ if (broadcastReceivers.get(i).toString().contains(
+ NotificationManagerService.class.getName())) {
+ mUserSwitchIntentReceiver = broadcastReceivers.get(i);
+ }
}
}
assertNotNull("package intent receiver should exist", mPackageIntentReceiver);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
index cd0389d..ba31944 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -46,10 +46,13 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -583,6 +586,7 @@
final IDisplayAreaOrganizer mockDisplayAreaOrganizer = mock(IDisplayAreaOrganizer.class);
doReturn(mock(IBinder.class)).when(mockDisplayAreaOrganizer).asBinder();
displayArea.mOrganizer = mockDisplayAreaOrganizer;
+ displayArea.mDisplayAreaAppearedSent = true;
spyOn(mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController);
mDisplayContent.addChild(displayArea, 0);
@@ -687,6 +691,56 @@
assertEquals(parent.getChildAt(0), child);
}
+ @Test
+ public void testSetOrganizer() {
+ final TaskDisplayArea displayArea = createTaskDisplayArea(
+ mDisplayContent, mWm, "NewArea", FEATURE_VENDOR_FIRST);
+
+ assertNull(displayArea.mOrganizer);
+ assertFalse(displayArea.mDisplayAreaAppearedSent);
+
+ final IDisplayAreaOrganizer organizer = mock(IDisplayAreaOrganizer.class);
+ final DisplayAreaOrganizerController controller =
+ mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController;
+ spyOn(controller);
+ doNothing().when(controller).onDisplayAreaVanished(any(), any());
+
+ displayArea.setOrganizer(organizer);
+
+ assertEquals(organizer, displayArea.mOrganizer);
+ assertTrue(displayArea.mDisplayAreaAppearedSent);
+ verify(controller).onDisplayAreaAppeared(organizer, displayArea);
+
+ // No duplicated appeared sent.
+ clearInvocations(controller);
+ displayArea.sendDisplayAreaAppeared();
+
+ verify(controller, never()).onDisplayAreaAppeared(any(), any());
+
+ // Sent info changed after appeared.
+ displayArea.sendDisplayAreaInfoChanged();
+
+ verify(controller).onDisplayAreaInfoChanged(organizer, displayArea);
+
+ // Sent info vanished after appeared.
+ displayArea.setOrganizer(null);
+
+ verify(controller).onDisplayAreaVanished(organizer, displayArea);
+ assertNull(displayArea.mOrganizer);
+ assertFalse(displayArea.mDisplayAreaAppearedSent);
+
+ // No callback until appeared sent.
+ clearInvocations(controller);
+
+ displayArea.sendDisplayAreaAppeared();
+ displayArea.sendDisplayAreaInfoChanged();
+ displayArea.sendDisplayAreaVanished(organizer);
+
+ verify(controller, never()).onDisplayAreaAppeared(any(), any());
+ verify(controller, never()).onDisplayAreaInfoChanged(any(), any());
+ verify(controller, never()).onDisplayAreaVanished(any(), any());
+ }
+
private static class TestDisplayArea<T extends WindowContainer> extends DisplayArea<T> {
private TestDisplayArea(WindowManagerService wms, Rect bounds, String name) {
super(wms, ANY, name);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 474720f..c8546c6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -48,6 +48,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
+import static com.android.window.flags.Flags.explicitRefreshRateHints;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -60,6 +61,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
@@ -84,6 +86,7 @@
import android.window.ITaskOrganizer;
import android.window.ITransitionPlayer;
import android.window.RemoteTransition;
+import android.window.SystemPerformanceHinter;
import android.window.TaskFragmentOrganizer;
import android.window.TransitionInfo;
@@ -2433,6 +2436,45 @@
assertTrue((player.mLastReady.getFlags() & FLAG_SYNC) == 0);
}
+ @Test
+ public void testTransitionsTriggerPerformanceHints() {
+ assumeTrue(explicitRefreshRateHints());
+ SystemPerformanceHinter systemPerformanceHinter = mock(SystemPerformanceHinter.class);
+ final TransitionController controller = new TestTransitionController(mAtm);
+ final TestTransitionPlayer player = registerTestTransitionPlayer();
+
+ mSyncEngine = createTestBLASTSyncEngine();
+ controller.setSyncEngine(mSyncEngine);
+ controller.setSystemPerformanceHinter(systemPerformanceHinter);
+ SystemPerformanceHinter.HighPerfSession session = mock(
+ SystemPerformanceHinter.HighPerfSession.class);
+ doReturn(session).when(systemPerformanceHinter).startSession(anyInt(), anyInt(),
+ anyString());
+
+ final Transition transitA = createTestTransition(TRANSIT_OPEN, controller);
+ final Task task = createTask(mDisplayContent,
+ WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
+ final ActivityRecord act = createActivityRecord(task);
+ act.setVisibleRequested(true);
+ act.setVisible(true);
+
+ controller.startCollectOrQueue(transitA, (deferred) -> {
+ });
+ transitA.collect(act);
+
+ verify(systemPerformanceHinter).startSession(
+ eq(SystemPerformanceHinter.HINT_SF), anyInt(), eq("Transition collected"));
+
+ transitA.start();
+ transitA.setAllReady();
+
+ // Aborting here doesn't abort the transition, it aborts the sync allowing the transition to
+ // finish successfully.
+ mSyncEngine.abort(transitA.getSyncId());
+ controller.finishTransition(transitA);
+ verify(session).close();
+ }
+
private static void makeTaskOrganized(Task... tasks) {
final ITaskOrganizer organizer = mock(ITaskOrganizer.class);
for (Task t : tasks) {
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index a7675d6..def52a5 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -942,6 +942,7 @@
* @return the Telecom identifier associated with this {@link Call} . This is not a stable
* identifier and is not guaranteed to be unique across device reboots.
*/
+ @FlaggedApi(Flags.FLAG_CALL_DETAILS_ID_CHANGES)
public @NonNull String getId() { return mTelecomCallId; }
/** {@hide} */
diff --git a/telecomm/java/android/telecom/StreamingCall.java b/telecomm/java/android/telecom/StreamingCall.java
index 29f436d..ad1b6f9 100644
--- a/telecomm/java/android/telecom/StreamingCall.java
+++ b/telecomm/java/android/telecom/StreamingCall.java
@@ -16,6 +16,7 @@
package android.telecom;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
@@ -25,6 +26,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.server.telecom.flags.Flags;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -57,6 +60,7 @@
/**
* The ID associated with this call. This is the same value as {@link CallControl#getCallId()}.
*/
+ @FlaggedApi(Flags.FLAG_CALL_DETAILS_ID_CHANGES)
public static final String EXTRA_CALL_ID = "android.telecom.extra.CALL_ID";
/**
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index f9844bc..a8c077d 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -16,9 +16,12 @@
package android.telephony;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
+import com.android.internal.telephony.flags.Flags;
+
/**
* Describes the cause of a disconnected call. Those disconnect causes can be converted into a more
* generic {@link android.telecom.DisconnectCause} object.
@@ -363,6 +366,7 @@
/**
* Indicates that the call was unable to be made because the satellite modem is enabled.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_ENABLED = 82;
//*********************************************************************************************
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index cb4a6e7..7ccc27e 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -16,6 +16,7 @@
package android.telephony;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -30,6 +31,8 @@
import android.telephony.Annotation.NetworkType;
import android.text.TextUtils;
+import com.android.internal.telephony.flags.Flags;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -206,6 +209,7 @@
/**
* MMS service
*/
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
public static final int SERVICE_TYPE_MMS = 6;
/** @hide */
@@ -702,6 +706,7 @@
*
* @return {@code true} if network is a non-terrestrial network else {@code false}.
*/
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
public boolean isNonTerrestrialNetwork() {
return mIsNonTerrestrialNetwork;
}
@@ -1186,6 +1191,7 @@
* else {@code false}.
* @return The builder.
*/
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
public @NonNull Builder setIsNonTerrestrialNetwork(boolean isNonTerrestrialNetwork) {
mIsNonTerrestrialNetwork = isNonTerrestrialNetwork;
return this;
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 5b8848c..85a85c6 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -16,6 +16,7 @@
package android.telephony;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -35,6 +36,7 @@
import android.telephony.NetworkRegistrationInfo.NRState;
import android.text.TextUtils;
+import com.android.internal.telephony.flags.Flags;
import com.android.telephony.Rlog;
import java.lang.annotation.Retention;
@@ -2256,6 +2258,7 @@
*
* @return {@code true} if device is connected to a non-terrestrial network else {@code false}.
*/
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
public boolean isUsingNonTerrestrialNetwork() {
synchronized (mNetworkRegistrationInfos) {
for (NetworkRegistrationInfo nri : mNetworkRegistrationInfos) {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 548fa97..90fa69f 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -25,6 +25,7 @@
import android.Manifest;
import android.annotation.BytesLong;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.LongDef;
import android.annotation.NonNull;
@@ -128,6 +129,7 @@
import com.android.internal.telephony.OperatorInfo;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.RILConstants;
+import com.android.internal.telephony.flags.Flags;
import com.android.telephony.Rlog;
import java.io.IOException;
@@ -1195,6 +1197,7 @@
* The dialer app receives this event via
* {@link Call.Callback#onConnectionEvent(Call, String, Bundle)}.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final String EVENT_DISPLAY_SOS_MESSAGE =
"android.telephony.event.DISPLAY_SOS_MESSAGE";
@@ -15036,6 +15039,7 @@
* @hide
*/
@TestApi
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int HAL_SERVICE_SATELLITE = 8;
/** @hide */
diff --git a/telephony/java/android/telephony/satellite/AntennaDirection.java b/telephony/java/android/telephony/satellite/AntennaDirection.java
index 22412e6..c690f98 100644
--- a/telephony/java/android/telephony/satellite/AntennaDirection.java
+++ b/telephony/java/android/telephony/satellite/AntennaDirection.java
@@ -16,11 +16,14 @@
package android.telephony.satellite;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.telephony.flags.Flags;
+
import java.util.Objects;
/**
@@ -38,6 +41,7 @@
* @hide
*/
@SystemApi
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public final class AntennaDirection implements Parcelable {
/** Antenna x axis direction. */
private float mX;
@@ -62,11 +66,13 @@
}
@Override
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public int describeContents() {
return 0;
}
@Override
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeFloat(mX);
out.writeFloat(mY);
@@ -74,6 +80,7 @@
}
@NonNull
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final Creator<AntennaDirection> CREATOR =
new Creator<>() {
@Override
@@ -118,14 +125,17 @@
return Objects.hash(mX, mY, mZ);
}
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public float getX() {
return mX;
}
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public float getY() {
return mY;
}
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public float getZ() {
return mZ;
}
diff --git a/telephony/java/android/telephony/satellite/AntennaPosition.java b/telephony/java/android/telephony/satellite/AntennaPosition.java
index 588be6c..8842886 100644
--- a/telephony/java/android/telephony/satellite/AntennaPosition.java
+++ b/telephony/java/android/telephony/satellite/AntennaPosition.java
@@ -16,11 +16,14 @@
package android.telephony.satellite;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.telephony.flags.Flags;
+
import java.util.Objects;
/**
@@ -29,6 +32,7 @@
* @hide
*/
@SystemApi
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public final class AntennaPosition implements Parcelable {
/** Antenna direction used for satellite communication. */
@NonNull AntennaDirection mAntennaDirection;
@@ -49,17 +53,20 @@
}
@Override
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public int describeContents() {
return 0;
}
@Override
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeParcelable(mAntennaDirection, flags);
out.writeInt(mSuggestedHoldPosition);
}
@NonNull
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final Creator<AntennaPosition> CREATOR =
new Creator<>() {
@Override
@@ -100,11 +107,13 @@
}
@NonNull
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public AntennaDirection getAntennaDirection() {
return mAntennaDirection;
}
@SatelliteManager.DeviceHoldPosition
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public int getSuggestedHoldPosition() {
return mSuggestedHoldPosition;
}
diff --git a/telephony/java/android/telephony/satellite/PointingInfo.java b/telephony/java/android/telephony/satellite/PointingInfo.java
index dc4d38b..022a856 100644
--- a/telephony/java/android/telephony/satellite/PointingInfo.java
+++ b/telephony/java/android/telephony/satellite/PointingInfo.java
@@ -16,11 +16,14 @@
package android.telephony.satellite;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.telephony.flags.Flags;
+
import java.util.Objects;
/**
@@ -30,6 +33,7 @@
* @hide
*/
@SystemApi
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public final class PointingInfo implements Parcelable {
/** Satellite azimuth in degrees */
private float mSatelliteAzimuthDegrees;
@@ -50,16 +54,19 @@
}
@Override
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public int describeContents() {
return 0;
}
@Override
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeFloat(mSatelliteAzimuthDegrees);
out.writeFloat(mSatelliteElevationDegrees);
}
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final @android.annotation.NonNull Creator<PointingInfo> CREATOR =
new Creator<PointingInfo>() {
@Override
@@ -101,10 +108,12 @@
return sb.toString();
}
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public float getSatelliteAzimuthDegrees() {
return mSatelliteAzimuthDegrees;
}
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public float getSatelliteElevationDegrees() {
return mSatelliteElevationDegrees;
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteCapabilities.java b/telephony/java/android/telephony/satellite/SatelliteCapabilities.java
index bc45be1..0d8f101 100644
--- a/telephony/java/android/telephony/satellite/SatelliteCapabilities.java
+++ b/telephony/java/android/telephony/satellite/SatelliteCapabilities.java
@@ -16,12 +16,15 @@
package android.telephony.satellite;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.telephony.flags.Flags;
+
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -34,6 +37,7 @@
* @hide
*/
@SystemApi
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public final class SatelliteCapabilities implements Parcelable {
/**
* List of technologies supported by the satellite modem.
@@ -76,11 +80,13 @@
}
@Override
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public int describeContents() {
return 0;
}
@Override
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public void writeToParcel(@NonNull Parcel out, int flags) {
if (mSupportedRadioTechnologies != null && !mSupportedRadioTechnologies.isEmpty()) {
out.writeInt(mSupportedRadioTechnologies.size());
@@ -106,6 +112,7 @@
}
}
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
@NonNull public static final Creator<SatelliteCapabilities> CREATOR = new Creator<>() {
@Override
public SatelliteCapabilities createFromParcel(Parcel in) {
@@ -165,6 +172,7 @@
/**
* @return The list of technologies supported by the satellite modem.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
@NonNull @SatelliteManager.NTRadioTechnology public Set<Integer>
getSupportedRadioTechnologies() {
return mSupportedRadioTechnologies;
@@ -176,6 +184,7 @@
* @return {@code true} if UE needs to point to a satellite to send and receive data and
* {@code false} otherwise.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public boolean isPointingRequired() {
return mIsPointingRequired;
}
@@ -185,6 +194,7 @@
*
* @return The maximum number of bytes per datagram that can be sent over satellite.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public int getMaxBytesPerOutgoingDatagram() {
return mMaxBytesPerOutgoingDatagram;
}
@@ -195,6 +205,7 @@
* @return Map key: {@link SatelliteManager.DeviceHoldPosition} value: AntennaPosition
*/
@NonNull
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public Map<Integer, AntennaPosition> getAntennaPositionMap() {
return mAntennaPositionMap;
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteDatagram.java b/telephony/java/android/telephony/satellite/SatelliteDatagram.java
index 9037f0c..4d67f80 100644
--- a/telephony/java/android/telephony/satellite/SatelliteDatagram.java
+++ b/telephony/java/android/telephony/satellite/SatelliteDatagram.java
@@ -16,17 +16,21 @@
package android.telephony.satellite;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.telephony.flags.Flags;
+
/**
* SatelliteDatagram is used to store data that is to be sent or received over satellite.
* Data is stored in byte array format.
* @hide
*/
@SystemApi
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public final class SatelliteDatagram implements Parcelable {
/**
* Datagram to be sent or received over satellite.
@@ -45,15 +49,18 @@
}
@Override
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public int describeContents() {
return 0;
}
@Override
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeByteArray(mData);
}
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
@NonNull public static final Creator<SatelliteDatagram> CREATOR =
new Creator<>() {
@Override
@@ -73,6 +80,7 @@
* satellite provider. Client application should be aware of how to encode the datagram based
* upon the satellite provider.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
@NonNull public byte[] getSatelliteDatagram() {
return mData;
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java b/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
index cb2920f..b5763c3 100644
--- a/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
@@ -16,9 +16,12 @@
package android.telephony.satellite;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import com.android.internal.telephony.flags.Flags;
+
import java.util.function.Consumer;
/**
@@ -27,6 +30,7 @@
* @hide
*/
@SystemApi
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public interface SatelliteDatagramCallback {
/**
* Called when there is an incoming datagram to be received.
@@ -38,6 +42,7 @@
* that they received the datagram. If the callback is not received within
* five minutes, Telephony will resend the datagram.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
void onSatelliteDatagramReceived(long datagramId, @NonNull SatelliteDatagram datagram,
int pendingCount, @NonNull Consumer<Void> callback);
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index 8dc2de8..7322aeb 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -63,6 +63,7 @@
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SATELLITE)
@SystemApi
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public final class SatelliteManager {
private static final String TAG = "SatelliteManager";
@@ -108,6 +109,7 @@
/**
* Exception from the satellite service containing the {@link SatelliteResult} error code.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static class SatelliteException extends Exception {
@SatelliteResult private final int mErrorCode;
@@ -116,6 +118,7 @@
*
* @param errorCode The {@link SatelliteResult}.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public SatelliteException(@SatelliteResult int errorCode) {
mErrorCode = errorCode;
}
@@ -125,6 +128,7 @@
*
* @return The {@link SatelliteResult}.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
@SatelliteResult public int getErrorCode() {
return mErrorCode;
}
@@ -190,104 +194,127 @@
/**
* The request was successfully processed.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_RESULT_SUCCESS = 0;
/**
* A generic error which should be used only when other specific errors cannot be used.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_RESULT_ERROR = 1;
/**
* Error received from the satellite server.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_RESULT_SERVER_ERROR = 2;
/**
* Error received from the vendor service. This generic error code should be used
* only when the error cannot be mapped to other specific service error codes.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_RESULT_SERVICE_ERROR = 3;
/**
* Error received from satellite modem. This generic error code should be used only when
* the error cannot be mapped to other specific modem error codes.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_RESULT_MODEM_ERROR = 4;
/**
* Error received from the satellite network. This generic error code should be used only when
* the error cannot be mapped to other specific network error codes.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_RESULT_NETWORK_ERROR = 5;
/**
* Telephony is not in a valid state to receive requests from clients.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_RESULT_INVALID_TELEPHONY_STATE = 6;
/**
* Satellite modem is not in a valid state to receive requests from clients.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_RESULT_INVALID_MODEM_STATE = 7;
/**
* Either vendor service, or modem, or Telephony framework has received a request with
* invalid arguments from its clients.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_RESULT_INVALID_ARGUMENTS = 8;
/**
* Telephony framework failed to send a request or receive a response from the vendor service
* or satellite modem due to internal error.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_RESULT_REQUEST_FAILED = 9;
/**
* Radio did not start or is resetting.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_RESULT_RADIO_NOT_AVAILABLE = 10;
/**
* The request is not supported by either the satellite modem or the network.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_RESULT_REQUEST_NOT_SUPPORTED = 11;
/**
* Satellite modem or network has no resources available to handle requests from clients.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_RESULT_NO_RESOURCES = 12;
/**
* Satellite service is not provisioned yet.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_RESULT_SERVICE_NOT_PROVISIONED = 13;
/**
* Satellite service provision is already in progress.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_RESULT_SERVICE_PROVISION_IN_PROGRESS = 14;
/**
* The ongoing request was aborted by either the satellite modem or the network.
* This error is also returned when framework decides to abort current send request as one
* of the previous send request failed.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_RESULT_REQUEST_ABORTED = 15;
/**
* The device/subscriber is barred from accessing the satellite service.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_RESULT_ACCESS_BARRED = 16;
/**
* Satellite modem timeout to receive ACK or response from the satellite network after
* sending a request to the network.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_RESULT_NETWORK_TIMEOUT = 17;
/**
* Satellite network is not reachable from the modem.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_RESULT_NOT_REACHABLE = 18;
/**
* The device/subscriber is not authorized to register with the satellite service provider.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_RESULT_NOT_AUTHORIZED = 19;
/**
* The device does not support satellite.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_RESULT_NOT_SUPPORTED = 20;
/**
* The current request is already in-progress.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_RESULT_REQUEST_IN_PROGRESS = 21;
/**
* Satellite modem is currently busy due to which current request cannot be processed.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_RESULT_MODEM_BUSY = 22;
/** @hide */
@@ -323,22 +350,27 @@
* Unknown Non-Terrestrial radio technology. This generic radio technology should be used
* only when the radio technology cannot be mapped to other specific radio technologies.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int NT_RADIO_TECHNOLOGY_UNKNOWN = 0;
/**
* 3GPP NB-IoT (Narrowband Internet of Things) over Non-Terrestrial-Networks technology.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int NT_RADIO_TECHNOLOGY_NB_IOT_NTN = 1;
/**
* 3GPP 5G NR over Non-Terrestrial-Networks technology.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int NT_RADIO_TECHNOLOGY_NR_NTN = 2;
/**
* 3GPP eMTC (enhanced Machine-Type Communication) over Non-Terrestrial-Networks technology.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int NT_RADIO_TECHNOLOGY_EMTC_NTN = 3;
/**
* Proprietary technology.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int NT_RADIO_TECHNOLOGY_PROPRIETARY = 4;
/** @hide */
@@ -353,12 +385,16 @@
public @interface NTRadioTechnology {}
/** Suggested device hold position is unknown. */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int DEVICE_HOLD_POSITION_UNKNOWN = 0;
/** User is suggested to hold the device in portrait mode. */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int DEVICE_HOLD_POSITION_PORTRAIT = 1;
/** User is suggested to hold the device in landscape mode with left hand. */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int DEVICE_HOLD_POSITION_LANDSCAPE_LEFT = 2;
/** User is suggested to hold the device in landscape mode with right hand. */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int DEVICE_HOLD_POSITION_LANDSCAPE_RIGHT = 3;
/** @hide */
@@ -372,14 +408,18 @@
public @interface DeviceHoldPosition {}
/** Display mode is unknown. */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int DISPLAY_MODE_UNKNOWN = 0;
/** Display mode of the device used for satellite communication for non-foldable phones. */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int DISPLAY_MODE_FIXED = 1;
/** Display mode of the device used for satellite communication for foldabale phones when the
* device is opened. */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int DISPLAY_MODE_OPENED = 2;
/** Display mode of the device used for satellite communication for foldabable phones when the
* device is closed. */
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int DISPLAY_MODE_CLOSED = 3;
/** @hide */
@@ -414,7 +454,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public void requestSatelliteEnabled(boolean enableSatellite, boolean enableDemoMode,
@NonNull @CallbackExecutor Executor executor,
@SatelliteResult @NonNull Consumer<Integer> resultListener) {
@@ -457,7 +497,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public void requestIsSatelliteEnabled(@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
Objects.requireNonNull(executor);
@@ -512,7 +552,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public void requestIsDemoModeEnabled(@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
Objects.requireNonNull(executor);
@@ -565,7 +605,7 @@
*
* @throws IllegalStateException if the Telephony process is not currently available.
*/
-
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public void requestIsSatelliteSupported(@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
Objects.requireNonNull(executor);
@@ -619,7 +659,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public void requestSatelliteCapabilities(@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<SatelliteCapabilities, SatelliteException> callback) {
Objects.requireNonNull(executor);
@@ -664,16 +704,19 @@
* The default state indicating that datagram transfer is idle.
* This should be sent if there are no message transfer activity happening.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE = 0;
/**
* A transition state indicating that a datagram is being sent.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING = 1;
/**
* An end state indicating that datagram sending completed successfully.
* After datagram transfer completes, {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE}
* will be sent if no more messages are pending.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS = 2;
/**
* An end state indicating that datagram sending completed with a failure.
@@ -681,16 +724,19 @@
* must be sent before reporting any additional datagram transfer state changes. All pending
* messages will be reported as failed, to the corresponding applications.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED = 3;
/**
* A transition state indicating that a datagram is being received.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING = 4;
/**
* An end state indicating that datagram receiving completed successfully.
* After datagram transfer completes, {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE}
* will be sent if no more messages are pending.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS = 5;
/**
* An end state indicating that datagram receive operation found that there are no
@@ -698,12 +744,14 @@
* After datagram transfer completes, {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE}
* will be sent if no more messages are pending.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_NONE = 6;
/**
* An end state indicating that datagram receive completed with a failure.
* After datagram transfer completes, {@link #SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE}
* will be sent if no more messages are pending.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED = 7;
/**
* A transition state indicating that Telephony is waiting for satellite modem to connect to a
@@ -721,6 +769,7 @@
* only when the datagram transfer state cannot be mapped to other specific datagram transfer
* states.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_UNKNOWN = -1;
/** @hide */
@@ -743,26 +792,32 @@
/**
* Satellite modem is in idle state.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_MODEM_STATE_IDLE = 0;
/**
* Satellite modem is listening for incoming datagrams.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_MODEM_STATE_LISTENING = 1;
/**
* Satellite modem is sending and/or receiving datagrams.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING = 2;
/**
* Satellite modem is retrying to send and/or receive datagrams.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_MODEM_STATE_DATAGRAM_RETRYING = 3;
/**
* Satellite modem is powered off.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_MODEM_STATE_OFF = 4;
/**
* Satellite modem is unavailable.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_MODEM_STATE_UNAVAILABLE = 5;
/**
* The satellite modem is powered on but the device is not registered to a satellite cell.
@@ -778,6 +833,7 @@
* Satellite modem state is unknown. This generic modem state should be used only when the
* modem state cannot be mapped to other specific modem states.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_MODEM_STATE_UNKNOWN = -1;
/** @hide */
@@ -799,15 +855,18 @@
* Datagram type is unknown. This generic datagram type should be used only when the
* datagram type cannot be mapped to other specific datagram types.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int DATAGRAM_TYPE_UNKNOWN = 0;
/**
* Datagram type indicating that the datagram to be sent or received is of type SOS message.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int DATAGRAM_TYPE_SOS_MESSAGE = 1;
/**
* Datagram type indicating that the datagram to be sent or received is of type
* location sharing.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int DATAGRAM_TYPE_LOCATION_SHARING = 2;
/** @hide */
@@ -857,7 +916,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public void startSatelliteTransmissionUpdates(@NonNull @CallbackExecutor Executor executor,
@SatelliteResult @NonNull Consumer<Integer> resultListener,
@NonNull SatelliteTransmissionUpdateCallback callback) {
@@ -927,7 +986,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public void stopSatelliteTransmissionUpdates(
@NonNull SatelliteTransmissionUpdateCallback callback,
@NonNull @CallbackExecutor Executor executor,
@@ -983,7 +1042,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public void provisionSatelliteService(@NonNull String token, @NonNull byte[] provisionData,
@Nullable CancellationSignal cancellationSignal,
@NonNull @CallbackExecutor Executor executor,
@@ -1036,7 +1095,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public void deprovisionSatelliteService(@NonNull String token,
@NonNull @CallbackExecutor Executor executor,
@SatelliteResult @NonNull Consumer<Integer> resultListener) {
@@ -1076,7 +1135,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
@SatelliteResult public int registerForSatelliteProvisionStateChanged(
@NonNull @CallbackExecutor Executor executor,
@NonNull SatelliteProvisionStateCallback callback) {
@@ -1119,7 +1178,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public void unregisterForSatelliteProvisionStateChanged(
@NonNull SatelliteProvisionStateCallback callback) {
Objects.requireNonNull(callback);
@@ -1158,7 +1217,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public void requestIsSatelliteProvisioned(@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
Objects.requireNonNull(executor);
@@ -1210,7 +1269,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
@SatelliteResult public int registerForSatelliteModemStateChanged(
@NonNull @CallbackExecutor Executor executor,
@NonNull SatelliteStateCallback callback) {
@@ -1250,7 +1309,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public void unregisterForSatelliteModemStateChanged(@NonNull SatelliteStateCallback callback) {
Objects.requireNonNull(callback);
ISatelliteStateCallback internalCallback = sSatelliteStateCallbackMap.remove(callback);
@@ -1288,7 +1347,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
@SatelliteResult public int registerForSatelliteDatagram(
@NonNull @CallbackExecutor Executor executor,
@NonNull SatelliteDatagramCallback callback) {
@@ -1344,7 +1403,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public void unregisterForSatelliteDatagram(@NonNull SatelliteDatagramCallback callback) {
Objects.requireNonNull(callback);
ISatelliteDatagramCallback internalCallback =
@@ -1383,7 +1442,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public void pollPendingSatelliteDatagrams(@NonNull @CallbackExecutor Executor executor,
@SatelliteResult @NonNull Consumer<Integer> resultListener) {
Objects.requireNonNull(executor);
@@ -1436,7 +1495,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public void sendSatelliteDatagram(@DatagramType int datagramType,
@NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI,
@NonNull @CallbackExecutor Executor executor,
@@ -1482,7 +1541,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public void requestIsSatelliteCommunicationAllowedForCurrentLocation(
@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
@@ -1540,7 +1599,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public void requestTimeForNextSatelliteVisibility(@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<Duration, SatelliteException> callback) {
Objects.requireNonNull(executor);
@@ -1594,7 +1653,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public void setDeviceAlignedWithSatellite(boolean isAligned) {
try {
ITelephony telephony = getITelephony();
@@ -1628,7 +1687,7 @@
* @param subId The subscription ID of the carrier.
* @param enableSatellite {@code true} to enable the satellite and {@code false} to disable.
* @param executor The executor on which the error code listener will be called.
- * @param resultListener Listener for the {@link SatelliteError} result of the operation.
+ * @param resultListener Listener for the {@link SatelliteResult} result of the operation.
*
* @throws SecurityException if the caller doesn't have required permission.
* @throws IllegalStateException if the Telephony process is not currently available.
@@ -1662,7 +1721,7 @@
* will return a {@code boolean} with value {@code true} if the satellite
* is enabled and {@code false} otherwise.
* If the request is not successful, {@link OutcomeReceiver#onError(Throwable)}
- * will return a {@link SatelliteException} with the {@link SatelliteError}.
+ * will return a {@link SatelliteException} with the {@link SatelliteResult}.
*
* @throws SecurityException if the caller doesn't have required permission.
* @throws IllegalStateException if the Telephony process is not currently available.
@@ -1688,7 +1747,7 @@
* @param subId The subscription ID of the carrier.
* @param reason Reason for disallowing satellite communication.
* @param executor The executor on which the error code listener will be called.
- * @param resultListener Listener for the {@link SatelliteError} result of the operation.
+ * @param resultListener Listener for the {@link SatelliteResult} result of the operation.
*
* @throws SecurityException if the caller doesn't have required permission.
* @throws IllegalStateException if the Telephony process is not currently available.
@@ -1731,7 +1790,7 @@
* @param subId The subscription ID of the carrier.
* @param reason Reason for disallowing satellite communication.
* @param executor The executor on which the error code listener will be called.
- * @param resultListener Listener for the {@link SatelliteError} result of the operation.
+ * @param resultListener Listener for the {@link SatelliteResult} result of the operation.
*
* @throws SecurityException if the caller doesn't have required permission.
* @throws IllegalStateException if the Telephony process is not currently available.
diff --git a/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java b/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java
index 7116876..a12952b 100644
--- a/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java
@@ -16,14 +16,18 @@
package android.telephony.satellite;
+import android.annotation.FlaggedApi;
import android.annotation.SystemApi;
+import com.android.internal.telephony.flags.Flags;
+
/**
* A callback class for monitoring satellite provision state change events.
*
* @hide
*/
@SystemApi
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public interface SatelliteProvisionStateCallback {
/**
* Called when satellite provision state changes.
@@ -33,5 +37,6 @@
* It is generally expected that the provisioning app retries if
* provisioning fails.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
void onSatelliteProvisionStateChanged(boolean provisioned);
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteStateCallback.java b/telephony/java/android/telephony/satellite/SatelliteStateCallback.java
index 812ff2d..bfe6e10 100644
--- a/telephony/java/android/telephony/satellite/SatelliteStateCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteStateCallback.java
@@ -16,18 +16,23 @@
package android.telephony.satellite;
+import android.annotation.FlaggedApi;
import android.annotation.SystemApi;
+import com.android.internal.telephony.flags.Flags;
+
/**
* A callback class for monitoring satellite modem state change events.
*
* @hide
*/
@SystemApi
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public interface SatelliteStateCallback {
/**
* Called when satellite modem state changes.
* @param state The new satellite modem state.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
void onSatelliteModemStateChanged(@SatelliteManager.SatelliteModemState int state);
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java b/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java
index 7ac06b0..e020970 100644
--- a/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java
@@ -16,9 +16,12 @@
package android.telephony.satellite;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import com.android.internal.telephony.flags.Flags;
+
/**
* A callback class for monitoring satellite position update and datagram transfer state change
* events.
@@ -26,12 +29,14 @@
* @hide
*/
@SystemApi
+@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public interface SatelliteTransmissionUpdateCallback {
/**
* Called when the satellite position changed.
*
* @param pointingInfo The pointing info containing the satellite location.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
void onSatellitePositionChanged(@NonNull PointingInfo pointingInfo);
/**
@@ -41,6 +46,7 @@
* @param sendPendingCount The number of datagrams that are currently being sent.
* @param errorCode If datagram transfer failed, the reason for failure.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
void onSendDatagramStateChanged(
@SatelliteManager.SatelliteDatagramTransferState int state, int sendPendingCount,
@SatelliteManager.SatelliteResult int errorCode);
@@ -52,6 +58,7 @@
* @param receivePendingCount The number of datagrams that are currently pending to be received.
* @param errorCode If datagram transfer failed, the reason for failure.
*/
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
void onReceiveDatagramStateChanged(
@SatelliteManager.SatelliteDatagramTransferState int state, int receivePendingCount,
@SatelliteManager.SatelliteResult int errorCode);
diff --git a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
index 0fcd0d6..7fda550 100644
--- a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
+++ b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
@@ -32,15 +32,15 @@
*
* @param listener The callback interface to handle satellite service indications.
*
- * Valid error codes returned:
- * SatelliteError:ERROR_NONE
- * SatelliteError:SERVICE_ERROR
- * SatelliteError:MODEM_ERROR
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:INVALID_ARGUMENTS
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
- * SatelliteError:NO_RESOURCES
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
*/
void setSatelliteListener(in ISatelliteListener listener);
@@ -53,15 +53,15 @@
* disabling listening mode.
* @param resultCallback The callback to receive the error code result of the operation.
*
- * Valid error codes returned:
- * SatelliteError:ERROR_NONE
- * SatelliteError:SERVICE_ERROR
- * SatelliteError:MODEM_ERROR
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:INVALID_ARGUMENTS
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
- * SatelliteError:NO_RESOURCES
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
*/
void requestSatelliteListeningEnabled(in boolean enable, in int timeout,
in IIntegerConsumer resultCallback);
@@ -84,15 +84,15 @@
* @param enableDemoMode True to enable demo mode and false to disable.
* @param resultCallback The callback to receive the error code result of the operation.
*
- * Valid error codes returned:
- * SatelliteError:ERROR_NONE
- * SatelliteError:SERVICE_ERROR
- * SatelliteError:MODEM_ERROR
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:INVALID_ARGUMENTS
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
- * SatelliteError:NO_RESOURCES
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
*/
void requestSatelliteEnabled(in boolean enableSatellite, in boolean enableDemoMode,
in IIntegerConsumer resultCallback);
@@ -101,39 +101,42 @@
* Request to get whether the satellite modem is enabled.
*
* @param resultCallback The callback to receive the error code result of the operation.
- * This must only be sent when the error is not SatelliteError#ERROR_NONE.
- * @param callback If the result is SatelliteError#ERROR_NONE, the callback to receive
- * whether the satellite modem is enabled.
+ * This must only be sent when the result is not
+ * SatelliteResult#SATELLITE_RESULT_SUCCESS.
+ * @param callback If the result is SatelliteResult#SATELLITE_RESULT_SUCCESS, the callback to
+ * receive whether the satellite modem is enabled.
*
- * Valid error codes returned:
- * SatelliteError:ERROR_NONE
- * SatelliteError:SERVICE_ERROR
- * SatelliteError:MODEM_ERROR
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:INVALID_ARGUMENTS
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
- * SatelliteError:NO_RESOURCES
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
*/
- void requestIsSatelliteEnabled(in IIntegerConsumer resultCallback, in IBooleanConsumer callback);
+ void requestIsSatelliteEnabled(in IIntegerConsumer resultCallback,
+ in IBooleanConsumer callback);
/**
* Request to get whether the satellite service is supported on the device.
*
* @param resultCallback The callback to receive the error code result of the operation.
- * This must only be sent when the error is not SatelliteError#ERROR_NONE.
- * @param callback If the result is SatelliteError#ERROR_NONE, the callback to receive
- * whether the satellite service is supported on the device.
+ * This must only be sent when the result is not
+ * SatelliteResult#SATELLITE_RESULT_SUCCESS.
+ * @param callback If the result is SatelliteResult#SATELLITE_RESULT_SUCCESS, the callback to
+ * receive whether the satellite service is supported on the device.
*
- * Valid error codes returned:
- * SatelliteError:ERROR_NONE
- * SatelliteError:SERVICE_ERROR
- * SatelliteError:MODEM_ERROR
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:INVALID_ARGUMENTS
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
- * SatelliteError:NO_RESOURCES
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
*/
void requestIsSatelliteSupported(in IIntegerConsumer resultCallback,
in IBooleanConsumer callback);
@@ -142,19 +145,20 @@
* Request to get the SatelliteCapabilities of the satellite service.
*
* @param resultCallback The callback to receive the error code result of the operation.
- * This must only be sent when the error is not SatelliteError#ERROR_NONE.
- * @param callback If the result is SatelliteError#ERROR_NONE, the callback to receive
- * the SatelliteCapabilities of the satellite service.
+ * This must only be sent when the result is not
+ * SatelliteResult#SATELLITE_RESULT_SUCCESS.
+ * @param callback If the result is SatelliteResult#SATELLITE_RESULT_SUCCESS, the callback to
+ * receive the SatelliteCapabilities of the satellite service.
*
- * Valid error codes returned:
- * SatelliteError:ERROR_NONE
- * SatelliteError:SERVICE_ERROR
- * SatelliteError:MODEM_ERROR
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:INVALID_ARGUMENTS
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
- * SatelliteError:NO_RESOURCES
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
*/
void requestSatelliteCapabilities(in IIntegerConsumer resultCallback,
in ISatelliteCapabilitiesConsumer callback);
@@ -166,15 +170,15 @@
*
* @param resultCallback The callback to receive the error code result of the operation.
*
- * Valid error codes returned:
- * SatelliteError:ERROR_NONE
- * SatelliteError:SERVICE_ERROR
- * SatelliteError:MODEM_ERROR
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:INVALID_ARGUMENTS
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
- * SatelliteError:NO_RESOURCES
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
*/
void startSendingSatellitePointingInfo(in IIntegerConsumer resultCallback);
@@ -184,15 +188,15 @@
*
* @param resultCallback The callback to receive the error code result of the operation.
*
- * Valid error codes returned:
- * SatelliteError:ERROR_NONE
- * SatelliteError:SERVICE_ERROR
- * SatelliteError:MODEM_ERROR
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:INVALID_ARGUMENTS
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
- * SatelliteError:NO_RESOURCES
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
*/
void stopSendingSatellitePointingInfo(in IIntegerConsumer resultCallback);
@@ -206,18 +210,18 @@
* @param provisionData Data from the provisioning app that can be used by provisioning server
* @param resultCallback The callback to receive the error code result of the operation.
*
- * Valid error codes returned:
- * SatelliteError:ERROR_NONE
- * SatelliteError:SERVICE_ERROR
- * SatelliteError:MODEM_ERROR
- * SatelliteError:NETWORK_ERROR
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:INVALID_ARGUMENTS
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
- * SatelliteError:NO_RESOURCES
- * SatelliteError:REQUEST_ABORTED
- * SatelliteError:NETWORK_TIMEOUT
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_NETWORK_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_ABORTED
+ * SatelliteResult:SATELLITE_RESULT_NETWORK_TIMEOUT
*/
void provisionSatelliteService(in String token, in byte[] provisionData,
in IIntegerConsumer resultCallback);
@@ -230,18 +234,18 @@
* @param token The token of the device/subscription to be deprovisioned.
* @param resultCallback The callback to receive the error code result of the operation.
*
- * Valid error codes returned:
- * SatelliteError:ERROR_NONE
- * SatelliteError:SERVICE_ERROR
- * SatelliteError:MODEM_ERROR
- * SatelliteError:NETWORK_ERROR
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:INVALID_ARGUMENTS
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
- * SatelliteError:NO_RESOURCES
- * SatelliteError:REQUEST_ABORTED
- * SatelliteError:NETWORK_TIMEOUT
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_NETWORK_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_ABORTED
+ * SatelliteResult:SATELLITE_RESULT_NETWORK_TIMEOUT
*/
void deprovisionSatelliteService(in String token, in IIntegerConsumer resultCallback);
@@ -249,19 +253,20 @@
* Request to get whether this device is provisioned with a satellite provider.
*
* @param resultCallback The callback to receive the error code result of the operation.
- * This must only be sent when the error is not SatelliteError#ERROR_NONE.
- * @param callback If the result is SatelliteError#ERROR_NONE, the callback to receive
- * whether this device is provisioned with a satellite provider.
+ * This must only be sent when the result is not
+ * SatelliteResult#SATELLITE_RESULT_SUCCESS.
+ * @param callback If the result is SatelliteResult#SATELLITE_RESULT_SUCCESS, the callback to
+ * receive whether this device is provisioned with a satellite provider.
*
- * Valid error codes returned:
- * SatelliteError:ERROR_NONE
- * SatelliteError:SERVICE_ERROR
- * SatelliteError:MODEM_ERROR
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:INVALID_ARGUMENTS
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
- * SatelliteError:NO_RESOURCES
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
*/
void requestIsSatelliteProvisioned(in IIntegerConsumer resultCallback,
in IBooleanConsumer callback);
@@ -273,20 +278,20 @@
*
* @param resultCallback The callback to receive the error code result of the operation.
*
- * Valid error codes returned:
- * SatelliteError:ERROR_NONE
- * SatelliteError:SERVICE_ERROR
- * SatelliteError:MODEM_ERROR
- * SatelliteError:NETWORK_ERROR
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:INVALID_ARGUMENTS
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
- * SatelliteError:NO_RESOURCES
- * SatelliteError:SATELLITE_ACCESS_BARRED
- * SatelliteError:NETWORK_TIMEOUT
- * SatelliteError:SATELLITE_NOT_REACHABLE
- * SatelliteError:NOT_AUTHORIZED
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_NETWORK_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
+ * SatelliteResult:SATELLITE_RESULT_ACCESS_BARRED
+ * SatelliteResult:SATELLITE_RESULT_NETWORK_TIMEOUT
+ * SatelliteResult:SATELLITE_RESULT_NOT_REACHABLE
+ * SatelliteResult:SATELLITE_RESULT_NOT_AUTHORIZED
*/
void pollPendingSatelliteDatagrams(in IIntegerConsumer resultCallback);
@@ -297,21 +302,21 @@
* @param isEmergency Whether this is an emergency datagram.
* @param resultCallback The callback to receive the error code result of the operation.
*
- * Valid error codes returned:
- * SatelliteError:ERROR_NONE
- * SatelliteError:SERVICE_ERROR
- * SatelliteError:MODEM_ERROR
- * SatelliteError:NETWORK_ERROR
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:INVALID_ARGUMENTS
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
- * SatelliteError:NO_RESOURCES
- * SatelliteError:REQUEST_ABORTED
- * SatelliteError:SATELLITE_ACCESS_BARRED
- * SatelliteError:NETWORK_TIMEOUT
- * SatelliteError:SATELLITE_NOT_REACHABLE
- * SatelliteError:NOT_AUTHORIZED
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_NETWORK_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_ABORTED
+ * SatelliteResult:SATELLITE_RESULT_ACCESS_BARRED
+ * SatelliteResult:SATELLITE_RESULT_NETWORK_TIMEOUT
+ * SatelliteResult:SATELLITE_RESULT_NOT_REACHABLE
+ * SatelliteResult:SATELLITE_RESULT_NOT_AUTHORIZED
*/
void sendSatelliteDatagram(in SatelliteDatagram datagram, in boolean isEmergency,
in IIntegerConsumer resultCallback);
@@ -322,19 +327,20 @@
* ISatelliteListener#onSatelliteModemStateChanged.
*
* @param resultCallback The callback to receive the error code result of the operation.
- * This must only be sent when the error is not SatelliteError#ERROR_NONE.
- * @param callback If the result is SatelliteError#ERROR_NONE, the callback to receive
- * the current satellite modem state.
+ * This must only be sent when the result is not
+ * SatelliteResult#SATELLITE_RESULT_SUCCESS.
+ * @param callback If the result is SatelliteResult#SATELLITE_RESULT_SUCCESS, the callback to
+ * receive the current satellite modem state.
*
- * Valid error codes returned:
- * SatelliteError:ERROR_NONE
- * SatelliteError:SERVICE_ERROR
- * SatelliteError:MODEM_ERROR
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:INVALID_ARGUMENTS
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
- * SatelliteError:NO_RESOURCES
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
*/
void requestSatelliteModemState(in IIntegerConsumer resultCallback,
in IIntegerConsumer callback);
@@ -343,19 +349,20 @@
* Request to get whether satellite communication is allowed for the current location.
*
* @param resultCallback The callback to receive the error code result of the operation.
- * This must only be sent when the error is not SatelliteError#ERROR_NONE.
- * @param callback If the result is SatelliteError#ERROR_NONE, the callback to receive
- * whether satellite communication is allowed for the current location.
+ * This must only be sent when the result is not
+ * SatelliteResult#SATELLITE_RESULT_SUCCESS.
+ * @param callback If the result is SatelliteResult#SATELLITE_RESULT_SUCCESS, the callback to
+ * receive whether satellite communication is allowed for the current location.
*
- * Valid error codes returned:
- * SatelliteError:ERROR_NONE
- * SatelliteError:SERVICE_ERROR
- * SatelliteError:MODEM_ERROR
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:INVALID_ARGUMENTS
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
- * SatelliteError:NO_RESOURCES
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
*/
void requestIsSatelliteCommunicationAllowedForCurrentLocation(
in IIntegerConsumer resultCallback, in IBooleanConsumer callback);
@@ -366,19 +373,20 @@
* This will return 0 if the satellite is currently visible.
*
* @param resultCallback The callback to receive the error code result of the operation.
- * This must only be sent when the error is not SatelliteError#ERROR_NONE.
- * @param callback If the result is SatelliteError#ERROR_NONE, the callback to receive
- * the time after which the satellite will be visible.
+ * This must only be sent when the result is not
+ * SatelliteResult#SATELLITE_RESULT_SUCCESS.
+ * @param callback If the result is SatelliteResult#SATELLITE_RESULT_SUCCESS, the callback to
+ * receive the time after which the satellite will be visible.
*
- * Valid error codes returned:
- * SatelliteError:ERROR_NONE
- * SatelliteError:SERVICE_ERROR
- * SatelliteError:MODEM_ERROR
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:INVALID_ARGUMENTS
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
- * SatelliteError:NO_RESOURCES
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
*/
void requestTimeForNextSatelliteVisibility(in IIntegerConsumer resultCallback,
in IIntegerConsumer callback);
@@ -399,14 +407,14 @@
* attach to them.
* @param resultCallback The callback to receive the error code result of the operation.
*
- * Valid error codes returned:
- * SatelliteError:NONE
- * SatelliteError:INVALID_ARGUMENTS
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:MODEM_ERR
- * SatelliteError:NO_RESOURCES
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
+ * Valid result codes returned:
+ * SatelliteResult:NONE
+ * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:MODEM_ERR
+ * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
*/
void setSatellitePlmn(int simSlot, in List<String> carrierPlmnList,
in List<String> allSatellitePlmnList, in IIntegerConsumer resultCallback);
@@ -420,12 +428,12 @@
* @param serial Serial number of request.
* @param enable {@code true} to enable satellite, {@code false} to disable satellite.
*
- * Valid errors returned:
- * SatelliteError:NONE
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:MODEM_ERR
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
*/
void setSatelliteEnabledForCarrier(int simSlot, boolean satelliteEnabled,
in IIntegerConsumer callback);
@@ -437,12 +445,12 @@
* this information to determine the relevant carrier.
* @param serial Serial number of request.
*
- * Valid errors returned:
- * SatelliteError:NONE
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:MODEM_ERR
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
*/
void requestIsSatelliteEnabledForCarrier(int simSlot, in IIntegerConsumer resultCallback,
in IBooleanConsumer callback);
diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
index a9c09c9..4cee01e 100644
--- a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
+++ b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
@@ -72,172 +72,172 @@
@Override
public void requestSatelliteListeningEnabled(boolean enable, int timeout,
- IIntegerConsumer errorCallback) throws RemoteException {
+ IIntegerConsumer resultCallback) throws RemoteException {
executeMethodAsync(
() -> SatelliteImplBase.this
- .requestSatelliteListeningEnabled(enable, timeout, errorCallback),
+ .requestSatelliteListeningEnabled(enable, timeout, resultCallback),
"requestSatelliteListeningEnabled");
}
@Override
public void enableCellularModemWhileSatelliteModeIsOn(boolean enabled,
- IIntegerConsumer errorCallback) throws RemoteException {
+ IIntegerConsumer resultCallback) throws RemoteException {
executeMethodAsync(
() -> SatelliteImplBase.this
- .enableCellularModemWhileSatelliteModeIsOn(enabled, errorCallback),
+ .enableCellularModemWhileSatelliteModeIsOn(enabled, resultCallback),
"enableCellularModemWhileSatelliteModeIsOn");
}
@Override
public void requestSatelliteEnabled(boolean enableSatellite, boolean enableDemoMode,
- IIntegerConsumer errorCallback) throws RemoteException {
+ IIntegerConsumer resultCallback) throws RemoteException {
executeMethodAsync(
() -> SatelliteImplBase.this
.requestSatelliteEnabled(
- enableSatellite, enableDemoMode, errorCallback),
+ enableSatellite, enableDemoMode, resultCallback),
"requestSatelliteEnabled");
}
@Override
- public void requestIsSatelliteEnabled(IIntegerConsumer errorCallback,
+ public void requestIsSatelliteEnabled(IIntegerConsumer resultCallback,
IBooleanConsumer callback) throws RemoteException {
executeMethodAsync(
() -> SatelliteImplBase.this
- .requestIsSatelliteEnabled(errorCallback, callback),
+ .requestIsSatelliteEnabled(resultCallback, callback),
"requestIsSatelliteEnabled");
}
@Override
- public void requestIsSatelliteSupported(IIntegerConsumer errorCallback,
+ public void requestIsSatelliteSupported(IIntegerConsumer resultCallback,
IBooleanConsumer callback) throws RemoteException {
executeMethodAsync(
() -> SatelliteImplBase.this
- .requestIsSatelliteSupported(errorCallback, callback),
+ .requestIsSatelliteSupported(resultCallback, callback),
"requestIsSatelliteSupported");
}
@Override
- public void requestSatelliteCapabilities(IIntegerConsumer errorCallback,
+ public void requestSatelliteCapabilities(IIntegerConsumer resultCallback,
ISatelliteCapabilitiesConsumer callback) throws RemoteException {
executeMethodAsync(
() -> SatelliteImplBase.this
- .requestSatelliteCapabilities(errorCallback, callback),
+ .requestSatelliteCapabilities(resultCallback, callback),
"requestSatelliteCapabilities");
}
@Override
- public void startSendingSatellitePointingInfo(IIntegerConsumer errorCallback)
+ public void startSendingSatellitePointingInfo(IIntegerConsumer resultCallback)
throws RemoteException {
executeMethodAsync(
- () -> SatelliteImplBase.this.startSendingSatellitePointingInfo(errorCallback),
+ () -> SatelliteImplBase.this.startSendingSatellitePointingInfo(resultCallback),
"startSendingSatellitePointingInfo");
}
@Override
- public void stopSendingSatellitePointingInfo(IIntegerConsumer errorCallback)
+ public void stopSendingSatellitePointingInfo(IIntegerConsumer resultCallback)
throws RemoteException {
executeMethodAsync(
- () -> SatelliteImplBase.this.stopSendingSatellitePointingInfo(errorCallback),
+ () -> SatelliteImplBase.this.stopSendingSatellitePointingInfo(resultCallback),
"stopSendingSatellitePointingInfo");
}
@Override
public void provisionSatelliteService(String token, byte[] provisionData,
- IIntegerConsumer errorCallback) throws RemoteException {
+ IIntegerConsumer resultCallback) throws RemoteException {
executeMethodAsync(
() -> SatelliteImplBase.this
- .provisionSatelliteService(token, provisionData, errorCallback),
+ .provisionSatelliteService(token, provisionData, resultCallback),
"provisionSatelliteService");
}
@Override
- public void deprovisionSatelliteService(String token, IIntegerConsumer errorCallback)
+ public void deprovisionSatelliteService(String token, IIntegerConsumer resultCallback)
throws RemoteException {
executeMethodAsync(
- () -> SatelliteImplBase.this.deprovisionSatelliteService(token, errorCallback),
+ () -> SatelliteImplBase.this.deprovisionSatelliteService(token, resultCallback),
"deprovisionSatelliteService");
}
@Override
- public void requestIsSatelliteProvisioned(IIntegerConsumer errorCallback,
+ public void requestIsSatelliteProvisioned(IIntegerConsumer resultCallback,
IBooleanConsumer callback) throws RemoteException {
executeMethodAsync(
() -> SatelliteImplBase.this
- .requestIsSatelliteProvisioned(errorCallback, callback),
+ .requestIsSatelliteProvisioned(resultCallback, callback),
"requestIsSatelliteProvisioned");
}
@Override
- public void pollPendingSatelliteDatagrams(IIntegerConsumer errorCallback)
+ public void pollPendingSatelliteDatagrams(IIntegerConsumer resultCallback)
throws RemoteException {
executeMethodAsync(
- () -> SatelliteImplBase.this.pollPendingSatelliteDatagrams(errorCallback),
+ () -> SatelliteImplBase.this.pollPendingSatelliteDatagrams(resultCallback),
"pollPendingSatelliteDatagrams");
}
@Override
public void sendSatelliteDatagram(SatelliteDatagram datagram, boolean isEmergency,
- IIntegerConsumer errorCallback) throws RemoteException {
+ IIntegerConsumer resultCallback) throws RemoteException {
executeMethodAsync(
() -> SatelliteImplBase.this
- .sendSatelliteDatagram(datagram, isEmergency, errorCallback),
+ .sendSatelliteDatagram(datagram, isEmergency, resultCallback),
"sendSatelliteDatagram");
}
@Override
- public void requestSatelliteModemState(IIntegerConsumer errorCallback,
+ public void requestSatelliteModemState(IIntegerConsumer resultCallback,
IIntegerConsumer callback) throws RemoteException {
executeMethodAsync(
() -> SatelliteImplBase.this
- .requestSatelliteModemState(errorCallback, callback),
+ .requestSatelliteModemState(resultCallback, callback),
"requestSatelliteModemState");
}
@Override
public void requestIsSatelliteCommunicationAllowedForCurrentLocation(
- IIntegerConsumer errorCallback, IBooleanConsumer callback)
+ IIntegerConsumer resultCallback, IBooleanConsumer callback)
throws RemoteException {
executeMethodAsync(
() -> SatelliteImplBase.this
.requestIsSatelliteCommunicationAllowedForCurrentLocation(
- errorCallback, callback),
+ resultCallback, callback),
"requestIsSatelliteCommunicationAllowedForCurrentLocation");
}
@Override
- public void requestTimeForNextSatelliteVisibility(IIntegerConsumer errorCallback,
+ public void requestTimeForNextSatelliteVisibility(IIntegerConsumer resultCallback,
IIntegerConsumer callback) throws RemoteException {
executeMethodAsync(
() -> SatelliteImplBase.this
- .requestTimeForNextSatelliteVisibility(errorCallback, callback),
+ .requestTimeForNextSatelliteVisibility(resultCallback, callback),
"requestTimeForNextSatelliteVisibility");
}
@Override
public void setSatellitePlmn(int simSlot, List<String> carrierPlmnList,
- List<String> devicePlmnList, IIntegerConsumer errorCallback)
+ List<String> devicePlmnList, IIntegerConsumer resultCallback)
throws RemoteException {
executeMethodAsync(
() -> SatelliteImplBase.this.setSatellitePlmn(
- simSlot, carrierPlmnList, devicePlmnList, errorCallback),
+ simSlot, carrierPlmnList, devicePlmnList, resultCallback),
"setSatellitePlmn");
}
@Override
public void setSatelliteEnabledForCarrier(int simSlot, boolean enableSatellite,
- IIntegerConsumer errorCallback) throws RemoteException {
+ IIntegerConsumer resultCallback) throws RemoteException {
executeMethodAsync(
- () -> SatelliteImplBase.this
- .setSatelliteEnabledForCarrier(simSlot, enableSatellite, errorCallback),
+ () -> SatelliteImplBase.this.setSatelliteEnabledForCarrier(
+ simSlot, enableSatellite, resultCallback),
"setSatelliteEnabledForCarrier");
}
@Override
- public void requestIsSatelliteEnabledForCarrier(int simSlot, IIntegerConsumer errorCallback,
- IBooleanConsumer callback) throws RemoteException {
+ public void requestIsSatelliteEnabledForCarrier(int simSlot,
+ IIntegerConsumer resultCallback, IBooleanConsumer callback) throws RemoteException {
executeMethodAsync(
() -> SatelliteImplBase.this
- .requestIsSatelliteEnabledForCarrier(simSlot, errorCallback, callback),
+ .requestIsSatelliteEnabledForCarrier(simSlot, resultCallback, callback),
"requestIsSatelliteEnabledForCarrier");
}
@@ -260,15 +260,15 @@
*
* @param listener The callback interface to handle satellite service indications.
*
- * Valid error codes returned:
- * SatelliteError:ERROR_NONE
- * SatelliteError:SERVICE_ERROR
- * SatelliteError:MODEM_ERROR
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:INVALID_ARGUMENTS
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
- * SatelliteError:NO_RESOURCES
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
*/
public void setSatelliteListener(@NonNull ISatelliteListener listener) {
// stub implementation
@@ -281,20 +281,20 @@
* @param enable True to enable satellite listening mode and false to disable.
* @param timeout How long the satellite modem should wait for the next incoming page before
* disabling listening mode.
- * @param errorCallback The callback to receive the error code result of the operation.
+ * @param resultCallback The callback to receive the error code result of the operation.
*
- * Valid error codes returned:
- * SatelliteError:ERROR_NONE
- * SatelliteError:SERVICE_ERROR
- * SatelliteError:MODEM_ERROR
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:INVALID_ARGUMENTS
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
- * SatelliteError:NO_RESOURCES
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
*/
public void requestSatelliteListeningEnabled(boolean enable, int timeout,
- @NonNull IIntegerConsumer errorCallback) {
+ @NonNull IIntegerConsumer resultCallback) {
// stub implementation
}
@@ -302,10 +302,10 @@
* Allow cellular modem scanning while satellite mode is on.
* @param enabled {@code true} to enable cellular modem while satellite mode is on
* and {@code false} to disable
- * @param errorCallback The callback to receive the error code result of the operation.
+ * @param resultCallback The callback to receive the error code result of the operation.
*/
public void enableCellularModemWhileSatelliteModeIsOn(boolean enabled,
- @NonNull IIntegerConsumer errorCallback) {
+ @NonNull IIntegerConsumer resultCallback) {
// stub implementation
}
@@ -316,42 +316,43 @@
*
* @param enableSatellite True to enable the satellite modem and false to disable.
* @param enableDemoMode True to enable demo mode and false to disable.
- * @param errorCallback The callback to receive the error code result of the operation.
+ * @param resultCallback The callback to receive the error code result of the operation.
*
- * Valid error codes returned:
- * SatelliteError:ERROR_NONE
- * SatelliteError:SERVICE_ERROR
- * SatelliteError:MODEM_ERROR
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:INVALID_ARGUMENTS
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
- * SatelliteError:NO_RESOURCES
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
*/
public void requestSatelliteEnabled(boolean enableSatellite, boolean enableDemoMode,
- @NonNull IIntegerConsumer errorCallback) {
+ @NonNull IIntegerConsumer resultCallback) {
// stub implementation
}
/**
* Request to get whether the satellite modem is enabled.
*
- * @param errorCallback The callback to receive the error code result of the operation.
- * This must only be sent when the result is not SatelliteError#ERROR_NONE.
- * @param callback If the result is SatelliteError#ERROR_NONE, the callback to receive
- * whether the satellite modem is enabled.
+ * @param resultCallback The callback to receive the error code result of the operation.
+ * This must only be sent when the result is not
+ * SatelliteResult#SATELLITE_RESULT_SUCCESS.
+ * @param callback If the result is SatelliteResult#SATELLITE_RESULT_SUCCESS, the callback to
+ * receive whether the satellite modem is enabled.
*
- * Valid error codes returned:
- * SatelliteError:ERROR_NONE
- * SatelliteError:SERVICE_ERROR
- * SatelliteError:MODEM_ERROR
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:INVALID_ARGUMENTS
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
- * SatelliteError:NO_RESOURCES
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
*/
- public void requestIsSatelliteEnabled(@NonNull IIntegerConsumer errorCallback,
+ public void requestIsSatelliteEnabled(@NonNull IIntegerConsumer resultCallback,
@NonNull IBooleanConsumer callback) {
// stub implementation
}
@@ -359,22 +360,23 @@
/**
* Request to get whether the satellite service is supported on the device.
*
- * @param errorCallback The callback to receive the error code result of the operation.
- * This must only be sent when the result is not SatelliteError#ERROR_NONE.
- * @param callback If the result is SatelliteError#ERROR_NONE, the callback to receive
- * whether the satellite service is supported on the device.
+ * @param resultCallback The callback to receive the error code result of the operation.
+ * This must only be sent when the result is not
+ * SatelliteResult#SATELLITE_RESULT_SUCCESS.
+ * @param callback If the result is SatelliteResult#SATELLITE_RESULT_SUCCESS, the callback to
+ * receive whether the satellite service is supported on the device.
*
- * Valid error codes returned:
- * SatelliteError:ERROR_NONE
- * SatelliteError:SERVICE_ERROR
- * SatelliteError:MODEM_ERROR
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:INVALID_ARGUMENTS
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
- * SatelliteError:NO_RESOURCES
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
*/
- public void requestIsSatelliteSupported(@NonNull IIntegerConsumer errorCallback,
+ public void requestIsSatelliteSupported(@NonNull IIntegerConsumer resultCallback,
@NonNull IBooleanConsumer callback) {
// stub implementation
}
@@ -382,22 +384,23 @@
/**
* Request to get the SatelliteCapabilities of the satellite service.
*
- * @param errorCallback The callback to receive the error code result of the operation.
- * This must only be sent when the result is not SatelliteError#ERROR_NONE.
- * @param callback If the result is SatelliteError#ERROR_NONE, the callback to receive
- * the SatelliteCapabilities of the satellite service.
+ * @param resultCallback The callback to receive the error code result of the operation.
+ * This must only be sent when the result is not
+ * SatelliteResult#SATELLITE_RESULT_SUCCESS.
+ * @param callback If the result is SatelliteResult#SATELLITE_RESULT_SUCCESS, the callback to
+ * receive the SatelliteCapabilities of the satellite service.
*
- * Valid error codes returned:
- * SatelliteError:ERROR_NONE
- * SatelliteError:SERVICE_ERROR
- * SatelliteError:MODEM_ERROR
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:INVALID_ARGUMENTS
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
- * SatelliteError:NO_RESOURCES
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
*/
- public void requestSatelliteCapabilities(@NonNull IIntegerConsumer errorCallback,
+ public void requestSatelliteCapabilities(@NonNull IIntegerConsumer resultCallback,
@NonNull ISatelliteCapabilitiesConsumer callback) {
// stub implementation
}
@@ -407,19 +410,19 @@
* The satellite service should report the satellite pointing info via
* ISatelliteListener#onSatellitePositionChanged as the user device/satellite moves.
*
- * @param errorCallback The callback to receive the error code result of the operation.
+ * @param resultCallback The callback to receive the error code result of the operation.
*
- * Valid error codes returned:
- * SatelliteError:ERROR_NONE
- * SatelliteError:SERVICE_ERROR
- * SatelliteError:MODEM_ERROR
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:INVALID_ARGUMENTS
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
- * SatelliteError:NO_RESOURCES
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
*/
- public void startSendingSatellitePointingInfo(@NonNull IIntegerConsumer errorCallback) {
+ public void startSendingSatellitePointingInfo(@NonNull IIntegerConsumer resultCallback) {
// stub implementation
}
@@ -427,19 +430,19 @@
* User stopped pointing to the satellite.
* The satellite service should stop reporting satellite pointing info to the framework.
*
- * @param errorCallback The callback to receive the error code result of the operation.
+ * @param resultCallback The callback to receive the error code result of the operation.
*
- * Valid error codes returned:
- * SatelliteError:ERROR_NONE
- * SatelliteError:SERVICE_ERROR
- * SatelliteError:MODEM_ERROR
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:INVALID_ARGUMENTS
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
- * SatelliteError:NO_RESOURCES
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
*/
- public void stopSendingSatellitePointingInfo(@NonNull IIntegerConsumer errorCallback) {
+ public void stopSendingSatellitePointingInfo(@NonNull IIntegerConsumer resultCallback) {
// stub implementation
}
@@ -452,23 +455,23 @@
* gateway.
* @param provisionData Data from the provisioning app that can be used by provisioning
* server
- * @param errorCallback The callback to receive the error code result of the operation.
+ * @param resultCallback The callback to receive the error code result of the operation.
*
- * Valid error codes returned:
- * SatelliteError:ERROR_NONE
- * SatelliteError:SERVICE_ERROR
- * SatelliteError:MODEM_ERROR
- * SatelliteError:NETWORK_ERROR
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:INVALID_ARGUMENTS
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
- * SatelliteError:NO_RESOURCES
- * SatelliteError:REQUEST_ABORTED
- * SatelliteError:NETWORK_TIMEOUT
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_NETWORK_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_ABORTED
+ * SatelliteResult:SATELLITE_RESULT_NETWORK_TIMEOUT
*/
public void provisionSatelliteService(@NonNull String token, @NonNull byte[] provisionData,
- @NonNull IIntegerConsumer errorCallback) {
+ @NonNull IIntegerConsumer resultCallback) {
// stub implementation
}
@@ -478,45 +481,46 @@
* Once deprovisioned, ISatelliteListener#onSatelliteProvisionStateChanged should report false.
*
* @param token The token of the device/subscription to be deprovisioned.
- * @param errorCallback The callback to receive the error code result of the operation.
+ * @param resultCallback The callback to receive the error code result of the operation.
*
- * Valid error codes returned:
- * SatelliteError:ERROR_NONE
- * SatelliteError:SERVICE_ERROR
- * SatelliteError:MODEM_ERROR
- * SatelliteError:NETWORK_ERROR
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:INVALID_ARGUMENTS
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
- * SatelliteError:NO_RESOURCES
- * SatelliteError:REQUEST_ABORTED
- * SatelliteError:NETWORK_TIMEOUT
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_NETWORK_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_ABORTED
+ * SatelliteResult:SATELLITE_RESULT_NETWORK_TIMEOUT
*/
public void deprovisionSatelliteService(@NonNull String token,
- @NonNull IIntegerConsumer errorCallback) {
+ @NonNull IIntegerConsumer resultCallback) {
// stub implementation
}
/**
* Request to get whether this device is provisioned with a satellite provider.
*
- * @param errorCallback The callback to receive the error code result of the operation.
- * This must only be sent when the result is not SatelliteError#ERROR_NONE.
- * @param callback If the result is SatelliteError#ERROR_NONE, the callback to receive
- * whether this device is provisioned with a satellite provider.
+ * @param resultCallback The callback to receive the error code result of the operation.
+ * This must only be sent when the result is not
+ * SatelliteResult#SATELLITE_RESULT_SUCCESS.
+ * @param callback If the result is SatelliteResult#SATELLITE_RESULT_SUCCESS, the callback to
+ * receive whether this device is provisioned with a satellite provider.
*
- * Valid error codes returned:
- * SatelliteError:ERROR_NONE
- * SatelliteError:SERVICE_ERROR
- * SatelliteError:MODEM_ERROR
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:INVALID_ARGUMENTS
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
- * SatelliteError:NO_RESOURCES
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
*/
- public void requestIsSatelliteProvisioned(@NonNull IIntegerConsumer errorCallback,
+ public void requestIsSatelliteProvisioned(@NonNull IIntegerConsumer resultCallback,
@NonNull IBooleanConsumer callback) {
// stub implementation
}
@@ -526,24 +530,24 @@
* The satellite service should check if there are any pending datagrams to be received over
* satellite and report them via ISatelliteListener#onSatelliteDatagramsReceived.
*
- * @param errorCallback The callback to receive the error code result of the operation.
+ * @param resultCallback The callback to receive the error code result of the operation.
*
- * Valid error codes returned:
- * SatelliteError:ERROR_NONE
- * SatelliteError:SERVICE_ERROR
- * SatelliteError:MODEM_ERROR
- * SatelliteError:NETWORK_ERROR
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:INVALID_ARGUMENTS
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
- * SatelliteError:NO_RESOURCES
- * SatelliteError:SATELLITE_ACCESS_BARRED
- * SatelliteError:NETWORK_TIMEOUT
- * SatelliteError:SATELLITE_NOT_REACHABLE
- * SatelliteError:NOT_AUTHORIZED
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_NETWORK_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
+ * SatelliteResult:SATELLITE_RESULT_ACCESS_BARRED
+ * SatelliteResult:SATELLITE_RESULT_NETWORK_TIMEOUT
+ * SatelliteResult:SATELLITE_RESULT_NOT_REACHABLE
+ * SatelliteResult:SATELLITE_RESULT_NOT_AUTHORIZED
*/
- public void pollPendingSatelliteDatagrams(@NonNull IIntegerConsumer errorCallback) {
+ public void pollPendingSatelliteDatagrams(@NonNull IIntegerConsumer resultCallback) {
// stub implementation
}
@@ -552,26 +556,26 @@
*
* @param datagram Datagram to send in byte format.
* @param isEmergency Whether this is an emergency datagram.
- * @param errorCallback The callback to receive the error code result of the operation.
+ * @param resultCallback The callback to receive the error code result of the operation.
*
- * Valid error codes returned:
- * SatelliteError:ERROR_NONE
- * SatelliteError:SERVICE_ERROR
- * SatelliteError:MODEM_ERROR
- * SatelliteError:NETWORK_ERROR
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:INVALID_ARGUMENTS
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
- * SatelliteError:NO_RESOURCES
- * SatelliteError:REQUEST_ABORTED
- * SatelliteError:SATELLITE_ACCESS_BARRED
- * SatelliteError:NETWORK_TIMEOUT
- * SatelliteError:SATELLITE_NOT_REACHABLE
- * SatelliteError:NOT_AUTHORIZED
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_NETWORK_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_ABORTED
+ * SatelliteResult:SATELLITE_RESULT_ACCESS_BARRED
+ * SatelliteResult:SATELLITE_RESULT_NETWORK_TIMEOUT
+ * SatelliteResult:SATELLITE_RESULT_NOT_REACHABLE
+ * SatelliteResult:SATELLITE_RESULT_NOT_AUTHORIZED
*/
public void sendSatelliteDatagram(@NonNull SatelliteDatagram datagram, boolean isEmergency,
- @NonNull IIntegerConsumer errorCallback) {
+ @NonNull IIntegerConsumer resultCallback) {
// stub implementation
}
@@ -580,22 +584,23 @@
* The satellite service should report the current satellite modem state via
* ISatelliteListener#onSatelliteModemStateChanged.
*
- * @param errorCallback The callback to receive the error code result of the operation.
- * This must only be sent when the result is not SatelliteError#ERROR_NONE.
- * @param callback If the result is SatelliteError#ERROR_NONE, the callback to receive
- * the current satellite modem state.
+ * @param resultCallback The callback to receive the error code result of the operation.
+ * This must only be sent when the result is not
+ * SatelliteResult#SATELLITE_RESULT_SUCCESS.
+ * @param callback If the result is SatelliteResult#SATELLITE_RESULT_SUCCESS, the callback to
+ * receive the current satellite modem state.
*
- * Valid error codes returned:
- * SatelliteError:ERROR_NONE
- * SatelliteError:SERVICE_ERROR
- * SatelliteError:MODEM_ERROR
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:INVALID_ARGUMENTS
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
- * SatelliteError:NO_RESOURCES
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
*/
- public void requestSatelliteModemState(@NonNull IIntegerConsumer errorCallback,
+ public void requestSatelliteModemState(@NonNull IIntegerConsumer resultCallback,
@NonNull IIntegerConsumer callback) {
// stub implementation
}
@@ -603,23 +608,24 @@
/**
* Request to get whether satellite communication is allowed for the current location.
*
- * @param errorCallback The callback to receive the error code result of the operation.
- * This must only be sent when the result is not SatelliteError#ERROR_NONE.
- * @param callback If the result is SatelliteError#ERROR_NONE, the callback to receive
- * whether satellite communication is allowed for the current location.
+ * @param resultCallback The callback to receive the error code result of the operation.
+ * This must only be sent when the result is not
+ * SatelliteResult#SATELLITE_RESULT_SUCCESS.
+ * @param callback If the result is SatelliteResult#SATELLITE_RESULT_SUCCESS, the callback to
+ * receive whether satellite communication is allowed for the current location.
*
- * Valid error codes returned:
- * SatelliteError:ERROR_NONE
- * SatelliteError:SERVICE_ERROR
- * SatelliteError:MODEM_ERROR
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:INVALID_ARGUMENTS
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
- * SatelliteError:NO_RESOURCES
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
*/
public void requestIsSatelliteCommunicationAllowedForCurrentLocation(
- @NonNull IIntegerConsumer errorCallback, @NonNull IBooleanConsumer callback) {
+ @NonNull IIntegerConsumer resultCallback, @NonNull IBooleanConsumer callback) {
// stub implementation
}
@@ -628,22 +634,23 @@
* representing the duration in seconds after which the satellite will be visible.
* This will return 0 if the satellite is currently visible.
*
- * @param errorCallback The callback to receive the error code result of the operation.
- * This must only be sent when the result is not SatelliteError#ERROR_NONE.
- * @param callback If the result is SatelliteError#ERROR_NONE, the callback to receive
- * the time after which the satellite will be visible.
+ * @param resultCallback The callback to receive the error code result of the operation.
+ * This must only be sent when the result is not
+ * SatelliteResult#SATELLITE_RESULT_SUCCESS.
+ * @param callback If the result is SatelliteResult#SATELLITE_RESULT_SUCCESS, the callback to
+ * receive the time after which the satellite will be visible.
*
- * Valid error codes returned:
- * SatelliteError:ERROR_NONE
- * SatelliteError:SERVICE_ERROR
- * SatelliteError:MODEM_ERROR
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:INVALID_ARGUMENTS
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
- * SatelliteError:NO_RESOURCES
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_SERVICE_ERROR
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
+ * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
*/
- public void requestTimeForNextSatelliteVisibility(@NonNull IIntegerConsumer errorCallback,
+ public void requestTimeForNextSatelliteVisibility(@NonNull IIntegerConsumer resultCallback,
@NonNull IIntegerConsumer callback) {
// stub implementation
}
@@ -658,25 +665,25 @@
*
* @param simLogicalSlotIndex Indicates the SIM logical slot index to which this API will be
* applied. The modem will use this information to determine the relevant carrier.
- * @param errorCallback The callback to receive the error code result of the operation.
+ * @param resultCallback The callback to receive the error code result of the operation.
* @param carrierPlmnList The list of roaming PLMN used for connecting to satellite networks
* supported by user subscription.
* @param allSatellitePlmnList Modem should use the allSatellitePlmnList to identify satellite
* PLMNs that are not supported by the carrier and make sure not to
* attach to them.
*
- * Valid error codes returned:
- * SatelliteError:NONE
- * SatelliteError:INVALID_ARGUMENTS
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:MODEM_ERR
- * SatelliteError:NO_RESOURCES
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
+ * Valid result codes returned:
+ * SatelliteResult:NONE
+ * SatelliteResult:SATELLITE_RESULT_INVALID_ARGUMENTS
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:MODEM_ERR
+ * SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
*/
public void setSatellitePlmn(@NonNull int simLogicalSlotIndex,
@NonNull List<String> carrierPlmnList, @NonNull List<String> allSatellitePlmnList,
- @NonNull IIntegerConsumer errorCallback) {
+ @NonNull IIntegerConsumer resultCallback) {
// stub implementation
}
@@ -689,12 +696,12 @@
* @param satelliteEnabled {@code true} to enable satellite, {@code false} to disable satellite.
* @param callback {@code true} to enable satellite, {@code false} to disable satellite.
*
- * Valid errors returned:
- * SatelliteError:NONE
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:MODEM_ERR
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
*/
public void setSatelliteEnabledForCarrier(@NonNull int simLogicalSlotIndex,
@NonNull boolean satelliteEnabled, @NonNull IIntegerConsumer callback) {
@@ -707,18 +714,18 @@
*
* @param simLogicalSlotIndex Indicates the SIM logical slot index to which this API will be
* applied. The modem will use this information to determine the relevant carrier.
- * @param errorCallback The callback to receive the error code result of the operation.
+ * @param resultCallback The callback to receive the error code result of the operation.
* @param callback {@code true} to satellite enabled, {@code false} to satellite disabled.
*
- * Valid errors returned:
- * SatelliteError:NONE
- * SatelliteError:INVALID_MODEM_STATE
- * SatelliteError:MODEM_ERR
- * SatelliteError:RADIO_NOT_AVAILABLE
- * SatelliteError:REQUEST_NOT_SUPPORTED
+ * Valid result codes returned:
+ * SatelliteResult:SATELLITE_RESULT_SUCCESS
+ * SatelliteResult:SATELLITE_RESULT_INVALID_MODEM_STATE
+ * SatelliteResult:SATELLITE_RESULT_MODEM_ERROR
+ * SatelliteResult:SATELLITE_RESULT_RADIO_NOT_AVAILABLE
+ * SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
*/
public void requestIsSatelliteEnabledForCarrier(@NonNull int simLogicalSlotIndex,
- @NonNull IIntegerConsumer errorCallback, @NonNull IBooleanConsumer callback) {
+ @NonNull IIntegerConsumer resultCallback, @NonNull IBooleanConsumer callback) {
// stub implementation
}
}
diff --git a/test-mock/api/current.txt b/test-mock/api/current.txt
index d1a68d4..241e691 100644
--- a/test-mock/api/current.txt
+++ b/test-mock/api/current.txt
@@ -1,4 +1,6 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
package android.test.mock {
@Deprecated public class MockAccountManager {
diff --git a/test-mock/api/removed.txt b/test-mock/api/removed.txt
index 1496c35..fa2fbd2 100644
--- a/test-mock/api/removed.txt
+++ b/test-mock/api/removed.txt
@@ -1,4 +1,6 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
package android.test.mock {
public class MockContext extends android.content.Context {
diff --git a/test-mock/api/system-current.txt b/test-mock/api/system-current.txt
index 9e022f0..2b5132e 100644
--- a/test-mock/api/system-current.txt
+++ b/test-mock/api/system-current.txt
@@ -1,4 +1,6 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
package android.test.mock {
public class MockContext extends android.content.Context {
diff --git a/test-mock/api/system-removed.txt b/test-mock/api/system-removed.txt
index d802177..14191eb 100644
--- a/test-mock/api/system-removed.txt
+++ b/test-mock/api/system-removed.txt
@@ -1 +1,3 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
diff --git a/test-mock/api/test-current.txt b/test-mock/api/test-current.txt
index 35f076f..1752edc 100644
--- a/test-mock/api/test-current.txt
+++ b/test-mock/api/test-current.txt
@@ -1,4 +1,6 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
package android.test.mock {
public class MockContext extends android.content.Context {
diff --git a/test-mock/api/test-removed.txt b/test-mock/api/test-removed.txt
index d802177..14191eb 100644
--- a/test-mock/api/test-removed.txt
+++ b/test-mock/api/test-removed.txt
@@ -1 +1,3 @@
// Signature format: 2.0
+// - add-additional-overrides=no
+// - migrating=Migration in progress see b/299366704
diff --git a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
index fa86e9c..44de6a6 100644
--- a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
@@ -100,6 +100,7 @@
const val RECEIVER_NAME = "DummyReceiver"
private const val ENGLISH_US_LAYOUT_NAME = "keyboard_layout_english_us"
private const val ENGLISH_UK_LAYOUT_NAME = "keyboard_layout_english_uk"
+ private const val GERMAN_LAYOUT_NAME = "keyboard_layout_german"
private const val VENDOR_SPECIFIC_LAYOUT_NAME = "keyboard_layout_vendorId:1,productId:1"
const val LAYOUT_TYPE_QWERTZ = 2
const val LAYOUT_TYPE_QWERTY = 1
@@ -108,6 +109,7 @@
private val ENGLISH_US_LAYOUT_DESCRIPTOR = createLayoutDescriptor(ENGLISH_US_LAYOUT_NAME)
private val ENGLISH_UK_LAYOUT_DESCRIPTOR = createLayoutDescriptor(ENGLISH_UK_LAYOUT_NAME)
+ private val GERMAN_LAYOUT_DESCRIPTOR = createLayoutDescriptor(GERMAN_LAYOUT_NAME)
private val VENDOR_SPECIFIC_LAYOUT_DESCRIPTOR =
createLayoutDescriptor(VENDOR_SPECIFIC_LAYOUT_NAME)
@@ -710,7 +712,7 @@
assertCorrectLayout(
keyboardDevice,
createImeSubtypeForLanguageTag("de"),
- createLayoutDescriptor("keyboard_layout_german")
+ GERMAN_LAYOUT_DESCRIPTOR
)
assertCorrectLayout(
keyboardDevice,
@@ -775,13 +777,13 @@
assertCorrectLayout(
keyboardDevice,
createImeSubtypeForLanguageTagAndLayoutType("de", "qwertz"),
- createLayoutDescriptor("keyboard_layout_german")
+ GERMAN_LAYOUT_DESCRIPTOR
)
// Wrong layout type should match with language if provided layout type not available
assertCorrectLayout(
keyboardDevice,
createImeSubtypeForLanguageTagAndLayoutType("de", "qwerty"),
- createLayoutDescriptor("keyboard_layout_german")
+ GERMAN_LAYOUT_DESCRIPTOR
)
assertCorrectLayout(
keyboardDevice,
@@ -856,7 +858,7 @@
ArgumentMatchers.eq(createByteArray(
KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
LAYOUT_TYPE_DEFAULT,
- "German",
+ GERMAN_LAYOUT_NAME,
KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD,
"de-Latn",
LAYOUT_TYPE_QWERTZ))
@@ -882,7 +884,7 @@
ArgumentMatchers.eq(createByteArray(
"en",
LAYOUT_TYPE_QWERTY,
- "English (US)",
+ ENGLISH_US_LAYOUT_NAME,
KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE,
"de-Latn",
LAYOUT_TYPE_QWERTZ))
diff --git a/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt b/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt
index b39c932..33ff09b 100644
--- a/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt
@@ -113,7 +113,7 @@
)
val event = builder.addLayoutSelection(
createImeSubtype(1, ULocale.forLanguageTag("en-US"), "qwerty"),
- KeyboardLayout(null, "English(US)(Qwerty)", null, 0, null, 0, 0, 0),
+ "English(US)(Qwerty)",
KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD
).addLayoutSelection(
createImeSubtype(2, ULocale.forLanguageTag("en-US"), "azerty"),
@@ -121,7 +121,7 @@
KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_USER
).addLayoutSelection(
createImeSubtype(3, ULocale.forLanguageTag("en-US"), "qwerty"),
- KeyboardLayout(null, "German", null, 0, null, 0, 0, 0),
+ "German",
KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE
).setIsFirstTimeConfiguration(true).build()
@@ -184,7 +184,7 @@
)
val event = builder.addLayoutSelection(
createImeSubtype(4, null, "qwerty"), // Default language tag
- KeyboardLayout(null, "German", null, 0, null, 0, 0, 0),
+ "German",
KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE
).build()
diff --git a/tests/testables/src/com/android/internal/config/sysui/TestableFlagResolver.java b/tests/testables/src/com/android/internal/config/sysui/TestableFlagResolver.java
index 6a6ab00..a43e1b0 100644
--- a/tests/testables/src/com/android/internal/config/sysui/TestableFlagResolver.java
+++ b/tests/testables/src/com/android/internal/config/sysui/TestableFlagResolver.java
@@ -21,15 +21,39 @@
public class TestableFlagResolver implements SystemUiSystemPropertiesFlags.FlagResolver {
private Map<String, Boolean> mOverrides = new HashMap<>();
+ private Map<String, Integer> mOverridesInt = new HashMap<>();
+ private Map<String, String> mOverridesString = new HashMap<>();
@Override
public boolean isEnabled(SystemUiSystemPropertiesFlags.Flag flag) {
return mOverrides.getOrDefault(flag.mSysPropKey, flag.mDefaultValue);
}
+ @Override
+ public int getIntValue(SystemUiSystemPropertiesFlags.Flag flag) {
+ return mOverridesInt.getOrDefault(flag.mSysPropKey, flag.mDefaultIntValue);
+ }
+
+ @Override
+ public String getStringValue(SystemUiSystemPropertiesFlags.Flag flag) {
+ return mOverridesString.getOrDefault(flag.mSysPropKey, flag.mDefaultStringValue);
+ }
+
public TestableFlagResolver setFlagOverride(SystemUiSystemPropertiesFlags.Flag flag,
boolean isEnabled) {
mOverrides.put(flag.mSysPropKey, isEnabled);
return this;
}
+
+ public TestableFlagResolver setFlagOverride(SystemUiSystemPropertiesFlags.Flag flag,
+ int value) {
+ mOverridesInt.put(flag.mSysPropKey, value);
+ return this;
+ }
+
+ public TestableFlagResolver setFlagOverride(SystemUiSystemPropertiesFlags.Flag flag,
+ String value) {
+ mOverridesString.put(flag.mSysPropKey, value);
+ return this;
+ }
}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt
index 33010ba..678e6ea 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt
@@ -46,8 +46,11 @@
}
methodPolicy = policy
- // TODO: Need to think about the realistic default behavior.
- classPolicy = if (policy != FilterPolicy.Throw) policy else FilterPolicy.Remove
+ // If the default policy is "throw", we convert it to "keep" for classes and fields.
+ classPolicy = when (policy) {
+ FilterPolicy.Throw -> FilterPolicy.Keep
+ else -> policy
+ }
fieldPolicy = classPolicy
}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
index 9c372ff..c6334c4 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
@@ -17,6 +17,8 @@
import com.android.hoststubgen.HostStubGenErrors
import com.android.hoststubgen.HostStubGenInternalException
+import com.android.hoststubgen.asm.CLASS_INITIALIZER_DESC
+import com.android.hoststubgen.asm.CLASS_INITIALIZER_NAME
import com.android.hoststubgen.asm.isAnonymousInnerClass
import com.android.hoststubgen.log
import com.android.hoststubgen.asm.ClassNodes
@@ -81,6 +83,16 @@
}
}
+ // If we throw from the static initializer, the class would be useless, so we convert it
+ // "keep" instead.
+ if (methodName == CLASS_INITIALIZER_NAME && descriptor == CLASS_INITIALIZER_DESC &&
+ fallback.policy == FilterPolicy.Throw) {
+ // TODO Maybe show a warning?? But that'd be too noisy with --default-throw.
+ return FilterPolicy.Keep.withReason(
+ "'throw' on static initializer is handled as 'keep'" +
+ " [original throw reason: ${fallback.reason}]")
+ }
+
return fallback
}
}
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
index 57b6689..ce72a8e 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
@@ -79,7 +79,7 @@
// StaticInitMerger will merge it with the existing one, if any.
visitMethod(
Opcodes.ACC_PRIVATE or Opcodes.ACC_STATIC,
- "<clinit>",
+ CLASS_INITIALIZER_NAME,
"()V",
null,
null
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
index 43ceec4..0761edc 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
@@ -372,7 +372,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer
super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 2, attributes: 3
+ interfaces: 0, fields: 1, methods: 1, attributes: 3
public static boolean sInitialized;
descriptor: Z
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
@@ -387,17 +387,6 @@
x: ldc #x // String Stub!
x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
x: athrow
-
- static {};
- descriptor: ()V
- flags: (0x0008) ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
}
SourceFile: "TinyFrameworkClassWithInitializer.java"
RuntimeVisibleAnnotations:
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
index 43ceec4..0761edc 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
@@ -372,7 +372,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer
super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 2, attributes: 3
+ interfaces: 0, fields: 1, methods: 1, attributes: 3
public static boolean sInitialized;
descriptor: Z
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
@@ -387,17 +387,6 @@
x: ldc #x // String Stub!
x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
x: athrow
-
- static {};
- descriptor: ()V
- flags: (0x0008) ACC_STATIC
- Code:
- stack=3, locals=0, args_size=0
- x: new #x // class java/lang/RuntimeException
- x: dup
- x: ldc #x // String Stub!
- x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
- x: athrow
}
SourceFile: "TinyFrameworkClassWithInitializer.java"
RuntimeVisibleAnnotations:
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
index 079d2a8..8fcd2fb 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
@@ -15,3 +15,8 @@
# Class load hook
class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy ~com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
+
+
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializer stubclass
+ # Testing 'throw' on a static initializer. This should be handled as 'keep'.
+ method <clinit> ()V throw
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh b/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh
index fd48646..722905f 100755
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh
@@ -16,6 +16,10 @@
source "${0%/*}"/../../common.sh
+#**********************************************************************************************
+#This script is broken because it relies on soong intermediate files, which seem to have moved.
+#**********************************************************************************************
+
# This scripts run the "tiny-framework" test, but does most stuff from the command line, using
# the native java and javac commands.
# This is useful to
@@ -57,7 +61,7 @@
test_compile_classpaths=(
$SOONG_INT/external/junit/junit/android_common/combined/junit.jar
- $SOONG_INT/prebuilts/tools/common/m2/truth-prebuilt/android_common/combined/truth-prebuilt.jar
+ $ANDROID_BUILD_TOP/out/target/common/obj/JAVA_LIBRARIES/truth-prebuilt_intermediates/classes.jar
)
test_runtime_classpaths=(
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.java
index 53cfdf6..01a690b 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.java
@@ -18,10 +18,14 @@
import android.hosttest.annotation.HostSideTestClassLoadHook;
import android.hosttest.annotation.HostSideTestWholeClassStub;
+
+// Note, policy-override-tiny-framework.txt hss an override on this class.
@HostSideTestClassLoadHook(
"com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded")
@HostSideTestWholeClassStub
public class TinyFrameworkClassWithInitializer {
+ // Note, this method has a 'throw' in the policy file, which is handled as a 'keep' (because
+ // it's a static initializer), so this won't show up in the stub jar.
static {
sInitialized = true;
}
diff --git a/tools/hoststubgen/scripts/run-all-tests.sh b/tools/hoststubgen/scripts/run-all-tests.sh
index 7600942..2e9cf42 100755
--- a/tools/hoststubgen/scripts/run-all-tests.sh
+++ b/tools/hoststubgen/scripts/run-all-tests.sh
@@ -33,7 +33,9 @@
run ./hoststubgen/test-tiny-framework/diff-and-update-golden.sh
run ./hoststubgen/test-framework/run-test-without-atest.sh
-run ./hoststubgen/test-tiny-framework/run-test-manually.sh
+
+#This script is broken because it relies on soong intermediate files, which seem to have moved.
+#run ./hoststubgen/test-tiny-framework/run-test-manually.sh
run atest tiny-framework-dump-test
run ./scripts/build-framework-hostside-jars-and-extract.sh