Merge "Replace class TEAMFOOD with aconfig TEAMFOOD" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index c30164c..a1375d7 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}",
@@ -35,13 +36,18 @@
":com.android.media.flags.bettertogether-aconfig-java{.generated_srcjars}",
":sdk_sandbox_flags_lib{.generated_srcjars}",
":android.permission.flags-aconfig-java{.generated_srcjars}",
+ ":android.database.sqlite-aconfig-java{.generated_srcjars}",
":hwui_flags_java_lib{.generated_srcjars}",
+ ":framework_graphics_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}",
":android.view.contentprotection.flags-aconfig-java{.generated_srcjars}",
":android.service.voice.flags-aconfig-java{.generated_srcjars}",
+ ":android.service.autofill.flags-aconfig-java{.generated_srcjars}",
+ ":com.android.net.flags-aconfig-java{.generated_srcjars}",
]
filegroup {
@@ -242,6 +248,11 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+cc_aconfig_library {
+ name: "aconfig_view_flags_c_lib",
+ aconfig_declarations: "android.view.flags-aconfig",
+}
+
// View.accessibility
aconfig_declarations {
name: "android.view.accessibility.flags-aconfig",
@@ -286,6 +297,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",
@@ -310,6 +328,24 @@
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",
+ ],
+
+}
+
+// SQLite
+aconfig_declarations {
+ name: "android.database.sqlite-aconfig",
+ package: "android.database.sqlite",
+ srcs: ["core/java/android/database/sqlite/*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.database.sqlite-aconfig-java",
+ aconfig_declarations: "android.database.sqlite-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
// Biometrics
@@ -332,6 +368,12 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+java_aconfig_library {
+ name: "framework_graphics_flags_java_lib",
+ aconfig_declarations: "framework_graphics_flags",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// Display
java_aconfig_library {
name: "display_flags_lib",
@@ -339,6 +381,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",
@@ -416,3 +464,39 @@
aconfig_declarations: "android.service.voice.flags-aconfig",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+
+// Autofill
+aconfig_declarations {
+ name: "android.service.autofill.flags-aconfig",
+ package: "android.service.autofill",
+ srcs: [
+ "services/autofill/bugfixes.aconfig",
+ "services/autofill/features.aconfig",
+ ],
+}
+
+java_aconfig_library {
+ name: "android.service.autofill.flags-aconfig-java",
+ 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"],
+}
+
+// CoreNetworking
+java_aconfig_library {
+ name: "com.android.net.flags-aconfig-java",
+ aconfig_declarations: "com.android.net.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/Android.bp b/apex/jobscheduler/service/Android.bp
index a4a0b4b..887f7fe 100644
--- a/apex/jobscheduler/service/Android.bp
+++ b/apex/jobscheduler/service/Android.bp
@@ -13,6 +13,10 @@
name: "service-jobscheduler",
installable: true,
+ defaults: [
+ "service-jobscheduler-aconfig-libraries",
+ ],
+
srcs: [
"java/**/*.java",
":framework-jobscheduler-shared-srcs",
diff --git a/apex/jobscheduler/service/aconfig/Android.bp b/apex/jobscheduler/service/aconfig/Android.bp
new file mode 100644
index 0000000..24ecd3d
--- /dev/null
+++ b/apex/jobscheduler/service/aconfig/Android.bp
@@ -0,0 +1,29 @@
+// JobScheduler
+aconfig_declarations {
+ name: "service-job.flags-aconfig",
+ package: "com.android.server.job",
+ srcs: [
+ "job.aconfig",
+ ],
+}
+
+java_aconfig_library {
+ name: "service-jobscheduler-job.flags-aconfig-java",
+ aconfig_declarations: "service-job.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+ visibility: ["//frameworks/base:__subpackages__"],
+}
+
+service_jobscheduler_aconfig_srcjars = [
+ ":service-jobscheduler-job.flags-aconfig-java{.generated_srcjars}",
+]
+
+// Aconfig declarations and libraries for the core framework
+java_defaults {
+ name: "service-jobscheduler-aconfig-libraries",
+ // Add java_aconfig_libraries to here to add them to the core framework
+ srcs: service_jobscheduler_aconfig_srcjars,
+ // Add aconfig-annotations-lib as a dependency for the optimization
+ libs: ["aconfig-annotations-lib"],
+ visibility: ["//frameworks/base:__subpackages__"],
+}
diff --git a/apex/jobscheduler/service/aconfig/job.aconfig b/apex/jobscheduler/service/aconfig/job.aconfig
new file mode 100644
index 0000000..3e835b8
--- /dev/null
+++ b/apex/jobscheduler/service/aconfig/job.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.server.job"
+
+flag {
+ name: "relax_prefetch_connectivity_constraint_only_on_charger"
+ namespace: "backstage_power"
+ description: "Only relax a prefetch job's connectivity constraint when the device is charging"
+ bug: "299329948"
+}
\ No newline at end of file
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index f252a0b..158d914 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -1030,6 +1030,12 @@
"light_idle_to_initial_flex";
private static final String KEY_LIGHT_IDLE_TIMEOUT_MAX_FLEX = "light_max_idle_to_flex";
private static final String KEY_LIGHT_IDLE_FACTOR = "light_idle_factor";
+ private static final String KEY_LIGHT_IDLE_INCREASE_LINEARLY =
+ "light_idle_increase_linearly";
+ private static final String KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS =
+ "light_idle_linear_increase_factor_ms";
+ private static final String KEY_LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS =
+ "light_idle_flex_linear_increase_factor_ms";
private static final String KEY_LIGHT_MAX_IDLE_TIMEOUT = "light_max_idle_to";
private static final String KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET =
"light_idle_maintenance_min_budget";
@@ -1079,6 +1085,10 @@
private long mDefaultLightIdleTimeoutMaxFlex =
!COMPRESS_TIME ? 15 * 60 * 1000L : 60 * 1000L;
private float mDefaultLightIdleFactor = 2f;
+ private boolean mDefaultLightIdleIncreaseLinearly;
+ private long mDefaultLightIdleLinearIncreaseFactorMs = mDefaultLightIdleTimeout;
+ private long mDefaultLightIdleFlexLinearIncreaseFactorMs =
+ mDefaultLightIdleTimeoutInitialFlex;
private long mDefaultLightMaxIdleTimeout =
!COMPRESS_TIME ? 15 * 60 * 1000L : 60 * 1000L;
private long mDefaultLightIdleMaintenanceMinBudget =
@@ -1174,6 +1184,37 @@
public float LIGHT_IDLE_FACTOR = mDefaultLightIdleFactor;
/**
+ * Whether to increase the light idle mode time linearly or exponentially.
+ * If true, will increase linearly
+ * (i.e. {@link #LIGHT_IDLE_TIMEOUT} + x * {@link #LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS}).
+ * If false, will increase by exponentially
+ * (i.e. {@link #LIGHT_IDLE_TIMEOUT} * ({@link #LIGHT_IDLE_FACTOR} ^ x)).
+ * This will also impact how the light idle flex value
+ * ({@link #LIGHT_IDLE_TIMEOUT_INITIAL_FLEX}) is increased (using
+ * {@link #LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS} for the linear increase)..
+ *
+ * @see #KEY_LIGHT_IDLE_INCREASE_LINEARLY
+ */
+ public boolean LIGHT_IDLE_INCREASE_LINEARLY = mDefaultLightIdleIncreaseLinearly;
+
+ /**
+ * Amount of time to increase the light idle time by, if increasing it linearly.
+ *
+ * @see #KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS
+ * @see #LIGHT_IDLE_INCREASE_LINEARLY
+ */
+ public long LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS = mDefaultLightIdleLinearIncreaseFactorMs;
+
+ /**
+ * Amount of time to increase the light idle flex time by, if increasing it linearly.
+ *
+ * @see #KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS
+ * @see #LIGHT_IDLE_INCREASE_LINEARLY
+ */
+ public long LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS =
+ mDefaultLightIdleFlexLinearIncreaseFactorMs;
+
+ /**
* This is the maximum time we will stay in light idle mode.
*
* @see #KEY_LIGHT_MAX_IDLE_TIMEOUT
@@ -1409,6 +1450,16 @@
mDefaultLightIdleTimeoutMaxFlex);
mDefaultLightIdleFactor = res.getFloat(
com.android.internal.R.integer.device_idle_light_idle_factor);
+ mDefaultLightIdleIncreaseLinearly = res.getBoolean(
+ com.android.internal.R.bool.device_idle_light_idle_increase_linearly);
+ mDefaultLightIdleLinearIncreaseFactorMs = getTimeout(res.getInteger(
+ com.android.internal.R.integer
+ .device_idle_light_idle_linear_increase_factor_ms),
+ mDefaultLightIdleLinearIncreaseFactorMs);
+ mDefaultLightIdleFlexLinearIncreaseFactorMs = getTimeout(res.getInteger(
+ com.android.internal.R.integer
+ .device_idle_light_idle_flex_linear_increase_factor_ms),
+ mDefaultLightIdleFlexLinearIncreaseFactorMs);
mDefaultLightMaxIdleTimeout = getTimeout(
res.getInteger(com.android.internal.R.integer.device_idle_light_max_idle_to_ms),
mDefaultLightMaxIdleTimeout);
@@ -1487,6 +1538,9 @@
LIGHT_IDLE_TIMEOUT_INITIAL_FLEX = mDefaultLightIdleTimeoutInitialFlex;
LIGHT_IDLE_TIMEOUT_MAX_FLEX = mDefaultLightIdleTimeoutMaxFlex;
LIGHT_IDLE_FACTOR = mDefaultLightIdleFactor;
+ LIGHT_IDLE_INCREASE_LINEARLY = mDefaultLightIdleIncreaseLinearly;
+ LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS = mDefaultLightIdleLinearIncreaseFactorMs;
+ LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS = mDefaultLightIdleFlexLinearIncreaseFactorMs;
LIGHT_MAX_IDLE_TIMEOUT = mDefaultLightMaxIdleTimeout;
LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = mDefaultLightIdleMaintenanceMinBudget;
LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = mDefaultLightIdleMaintenanceMaxBudget;
@@ -1556,6 +1610,21 @@
LIGHT_IDLE_FACTOR = Math.max(1, properties.getFloat(
KEY_LIGHT_IDLE_FACTOR, mDefaultLightIdleFactor));
break;
+ case KEY_LIGHT_IDLE_INCREASE_LINEARLY:
+ LIGHT_IDLE_INCREASE_LINEARLY = properties.getBoolean(
+ KEY_LIGHT_IDLE_INCREASE_LINEARLY,
+ mDefaultLightIdleIncreaseLinearly);
+ break;
+ case KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS:
+ LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS = properties.getLong(
+ KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS,
+ mDefaultLightIdleLinearIncreaseFactorMs);
+ break;
+ case KEY_LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS:
+ LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS = properties.getLong(
+ KEY_LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS,
+ mDefaultLightIdleFlexLinearIncreaseFactorMs);
+ break;
case KEY_LIGHT_MAX_IDLE_TIMEOUT:
LIGHT_MAX_IDLE_TIMEOUT = properties.getLong(
KEY_LIGHT_MAX_IDLE_TIMEOUT, mDefaultLightMaxIdleTimeout);
@@ -1716,6 +1785,20 @@
pw.print(LIGHT_IDLE_FACTOR);
pw.println();
+ pw.print(" "); pw.print(KEY_LIGHT_IDLE_INCREASE_LINEARLY); pw.print("=");
+ pw.print(LIGHT_IDLE_INCREASE_LINEARLY);
+ pw.println();
+
+ pw.print(" "); pw.print(KEY_LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS);
+ pw.print("=");
+ pw.print(LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS);
+ pw.println();
+
+ pw.print(" "); pw.print(KEY_LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS);
+ pw.print("=");
+ pw.print(LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS);
+ pw.println();
+
pw.print(" "); pw.print(KEY_LIGHT_MAX_IDLE_TIMEOUT); pw.print("=");
TimeUtils.formatDuration(LIGHT_MAX_IDLE_TIMEOUT, pw);
pw.println();
@@ -3694,10 +3777,18 @@
}
mMaintenanceStartTime = 0;
scheduleLightAlarmLocked(mNextLightIdleDelay, mNextLightIdleDelayFlex, true);
- mNextLightIdleDelay = Math.min(mConstants.LIGHT_MAX_IDLE_TIMEOUT,
- (long) (mNextLightIdleDelay * mConstants.LIGHT_IDLE_FACTOR));
- mNextLightIdleDelayFlex = Math.min(mConstants.LIGHT_IDLE_TIMEOUT_MAX_FLEX,
- (long) (mNextLightIdleDelayFlex * mConstants.LIGHT_IDLE_FACTOR));
+ if (!mConstants.LIGHT_IDLE_INCREASE_LINEARLY) {
+ mNextLightIdleDelay = Math.min(mConstants.LIGHT_MAX_IDLE_TIMEOUT,
+ (long) (mNextLightIdleDelay * mConstants.LIGHT_IDLE_FACTOR));
+ mNextLightIdleDelayFlex = Math.min(mConstants.LIGHT_IDLE_TIMEOUT_MAX_FLEX,
+ (long) (mNextLightIdleDelayFlex * mConstants.LIGHT_IDLE_FACTOR));
+ } else {
+ mNextLightIdleDelay = Math.min(mConstants.LIGHT_MAX_IDLE_TIMEOUT,
+ mNextLightIdleDelay + mConstants.LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS);
+ mNextLightIdleDelayFlex = Math.min(mConstants.LIGHT_IDLE_TIMEOUT_MAX_FLEX,
+ mNextLightIdleDelayFlex
+ + mConstants.LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS);
+ }
moveToLightStateLocked(LIGHT_STATE_IDLE, reason);
addEvent(EVENT_LIGHT_IDLE, null);
mGoingIdleWakeLock.acquire();
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 3cbee5d0..384a480 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -173,8 +173,6 @@
import dalvik.annotation.optimization.NeverCompile;
-import libcore.util.EmptyArray;
-
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -260,7 +258,7 @@
/**
* A map from uid to the last op-mode we have seen for
* {@link AppOpsManager#OP_SCHEDULE_EXACT_ALARM}. Used for evaluating permission state change
- * when the denylist changes.
+ * when the app-op changes.
*/
@VisibleForTesting
@GuardedBy("mLock")
@@ -671,9 +669,6 @@
@VisibleForTesting
final class Constants implements DeviceConfig.OnPropertiesChangedListener,
EconomyManagerInternal.TareStateChangeListener {
- @VisibleForTesting
- static final int MAX_EXACT_ALARM_DENY_LIST_SIZE = 250;
-
// Key names stored in the settings value.
@VisibleForTesting
static final String KEY_MIN_FUTURITY = "min_futurity";
@@ -727,8 +722,6 @@
@VisibleForTesting
static final String KEY_PRIORITY_ALARM_DELAY = "priority_alarm_delay";
@VisibleForTesting
- static final String KEY_EXACT_ALARM_DENY_LIST = "exact_alarm_deny_list";
- @VisibleForTesting
static final String KEY_MIN_DEVICE_IDLE_FUZZ = "min_device_idle_fuzz";
@VisibleForTesting
static final String KEY_MAX_DEVICE_IDLE_FUZZ = "max_device_idle_fuzz";
@@ -835,13 +828,6 @@
public long PRIORITY_ALARM_DELAY = DEFAULT_PRIORITY_ALARM_DELAY;
/**
- * Read-only set of apps that won't get SCHEDULE_EXACT_ALARM when the app-op mode for
- * OP_SCHEDULE_EXACT_ALARM is MODE_DEFAULT. Since this is read-only and volatile, this can
- * be accessed without synchronizing on {@link #mLock}.
- */
- public volatile Set<String> EXACT_ALARM_DENY_LIST = Collections.emptySet();
-
- /**
* Minimum time interval that an IDLE_UNTIL will be pulled earlier to a subsequent
* WAKE_FROM_IDLE alarm.
*/
@@ -1025,21 +1011,6 @@
PRIORITY_ALARM_DELAY = properties.getLong(KEY_PRIORITY_ALARM_DELAY,
DEFAULT_PRIORITY_ALARM_DELAY);
break;
- case KEY_EXACT_ALARM_DENY_LIST:
- final String rawValue = properties.getString(KEY_EXACT_ALARM_DENY_LIST,
- "");
- final String[] values = rawValue.isEmpty()
- ? EmptyArray.STRING
- : rawValue.split(",", MAX_EXACT_ALARM_DENY_LIST_SIZE + 1);
- if (values.length > MAX_EXACT_ALARM_DENY_LIST_SIZE) {
- Slog.w(TAG, "Deny list too long, truncating to "
- + MAX_EXACT_ALARM_DENY_LIST_SIZE + " elements.");
- updateExactAlarmDenyList(
- Arrays.copyOf(values, MAX_EXACT_ALARM_DENY_LIST_SIZE));
- } else {
- updateExactAlarmDenyList(values);
- }
- break;
case KEY_MIN_DEVICE_IDLE_FUZZ:
case KEY_MAX_DEVICE_IDLE_FUZZ:
if (!deviceIdleFuzzBoundariesUpdated) {
@@ -1110,28 +1081,6 @@
}
}
- private void updateExactAlarmDenyList(String[] newDenyList) {
- final Set<String> newSet = Collections.unmodifiableSet(new ArraySet<>(newDenyList));
- final Set<String> removed = new ArraySet<>(EXACT_ALARM_DENY_LIST);
- final Set<String> added = new ArraySet<>(newDenyList);
-
- added.removeAll(EXACT_ALARM_DENY_LIST);
- removed.removeAll(newSet);
- if (added.size() > 0) {
- mHandler.obtainMessage(AlarmHandler.EXACT_ALARM_DENY_LIST_PACKAGES_ADDED, added)
- .sendToTarget();
- }
- if (removed.size() > 0) {
- mHandler.obtainMessage(AlarmHandler.EXACT_ALARM_DENY_LIST_PACKAGES_REMOVED, removed)
- .sendToTarget();
- }
- if (newDenyList.length == 0) {
- EXACT_ALARM_DENY_LIST = Collections.emptySet();
- } else {
- EXACT_ALARM_DENY_LIST = newSet;
- }
- }
-
private void updateDeviceIdleFuzzBoundaries() {
final DeviceConfig.Properties properties = DeviceConfig.getProperties(
DeviceConfig.NAMESPACE_ALARM_MANAGER,
@@ -1277,9 +1226,6 @@
TimeUtils.formatDuration(PRIORITY_ALARM_DELAY, pw);
pw.println();
- pw.print(KEY_EXACT_ALARM_DENY_LIST, EXACT_ALARM_DENY_LIST);
- pw.println();
-
pw.print(KEY_MIN_DEVICE_IDLE_FUZZ);
pw.print("=");
TimeUtils.formatDuration(MIN_DEVICE_IDLE_FUZZ, pw);
@@ -2114,14 +2060,10 @@
? permissionState
: (newMode == AppOpsManager.MODE_ALLOWED);
} else {
- final boolean allowedByDefault =
- !mConstants.EXACT_ALARM_DENY_LIST.contains(packageName);
hadPermission = (oldMode == AppOpsManager.MODE_DEFAULT)
- ? allowedByDefault
- : (oldMode == AppOpsManager.MODE_ALLOWED);
+ || (oldMode == AppOpsManager.MODE_ALLOWED);
hasPermission = (newMode == AppOpsManager.MODE_DEFAULT)
- ? allowedByDefault
- : (newMode == AppOpsManager.MODE_ALLOWED);
+ || (newMode == AppOpsManager.MODE_ALLOWED);
}
if (hadPermission && !hasPermission) {
@@ -2769,11 +2711,8 @@
// Compatibility permission check for older apps.
final int mode = mAppOps.checkOpNoThrow(AppOpsManager.OP_SCHEDULE_EXACT_ALARM, uid,
packageName);
- if (mode == AppOpsManager.MODE_DEFAULT) {
- hasPermission = !mConstants.EXACT_ALARM_DENY_LIST.contains(packageName);
- } else {
- hasPermission = (mode == AppOpsManager.MODE_ALLOWED);
- }
+ hasPermission = (mode == AppOpsManager.MODE_DEFAULT)
+ || (mode == AppOpsManager.MODE_ALLOWED);
}
mStatLogger.logDurationStat(Stats.HAS_SCHEDULE_EXACT_ALARM, start);
return hasPermission;
@@ -3993,63 +3932,6 @@
}
/**
- * Called when the {@link Constants#EXACT_ALARM_DENY_LIST}, changes with the packages that
- * either got added or deleted.
- * These packages may lose or gain the SCHEDULE_EXACT_ALARM permission.
- *
- * Note that these packages don't need to be installed on the device, but if they are and they
- * do undergo a permission change, we will handle them appropriately.
- *
- * This should not be called with the lock held as it calls out to other services.
- * This is not expected to get called frequently.
- */
- void handleChangesToExactAlarmDenyList(ArraySet<String> changedPackages, boolean added) {
- Slog.w(TAG, "Packages " + changedPackages + (added ? " added to" : " removed from")
- + " the exact alarm deny list.");
-
- final int[] startedUserIds = mActivityManagerInternal.getStartedUserIds();
-
- for (int i = 0; i < changedPackages.size(); i++) {
- final String changedPackage = changedPackages.valueAt(i);
- for (final int userId : startedUserIds) {
- final int uid = mPackageManagerInternal.getPackageUid(changedPackage, 0, userId);
- if (uid <= 0) {
- continue;
- }
- if (!isExactAlarmChangeEnabled(changedPackage, userId)) {
- continue;
- }
- if (isScheduleExactAlarmDeniedByDefault(changedPackage, userId)) {
- continue;
- }
- if (hasUseExactAlarmInternal(changedPackage, uid)) {
- continue;
- }
- if (!mExactAlarmCandidates.contains(UserHandle.getAppId(uid))) {
- // Permission isn't requested, deny list doesn't matter.
- continue;
- }
- final int appOpMode;
- synchronized (mLock) {
- appOpMode = mLastOpScheduleExactAlarm.get(uid,
- AppOpsManager.opToDefaultMode(AppOpsManager.OP_SCHEDULE_EXACT_ALARM));
- }
- if (appOpMode != AppOpsManager.MODE_DEFAULT) {
- // Deny list doesn't matter.
- continue;
- }
- // added: true => package was added to the deny list
- // added: false => package was removed from the deny list
- if (added) {
- removeExactAlarmsOnPermissionRevoked(uid, changedPackage, /*killUid = */ true);
- } else {
- sendScheduleExactAlarmPermissionStateChangedBroadcast(changedPackage, userId);
- }
- }
- }
- }
-
- /**
* Called when an app loses the permission to use exact alarms. This will happen when the app
* no longer has either {@link Manifest.permission#SCHEDULE_EXACT_ALARM} or
* {@link Manifest.permission#USE_EXACT_ALARM}.
@@ -4931,8 +4813,8 @@
public static final int CHARGING_STATUS_CHANGED = 6;
public static final int REMOVE_FOR_CANCELED = 7;
public static final int REMOVE_EXACT_ALARMS = 8;
- public static final int EXACT_ALARM_DENY_LIST_PACKAGES_ADDED = 9;
- public static final int EXACT_ALARM_DENY_LIST_PACKAGES_REMOVED = 10;
+ // Unused id 9
+ // Unused id 10
public static final int REFRESH_EXACT_ALARM_CANDIDATES = 11;
public static final int TARE_AFFORDABILITY_CHANGED = 12;
public static final int CHECK_EXACT_ALARM_PERMISSION_ON_UPDATE = 13;
@@ -5041,12 +4923,6 @@
String packageName = (String) msg.obj;
removeExactAlarmsOnPermissionRevoked(uid, packageName, /*killUid = */true);
break;
- case EXACT_ALARM_DENY_LIST_PACKAGES_ADDED:
- handleChangesToExactAlarmDenyList((ArraySet<String>) msg.obj, true);
- break;
- case EXACT_ALARM_DENY_LIST_PACKAGES_REMOVED:
- handleChangesToExactAlarmDenyList((ArraySet<String>) msg.obj, false);
- break;
case REFRESH_EXACT_ALARM_CANDIDATES:
refreshExactAlarmCandidates();
break;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 43d2ae9..f47766e 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -1335,13 +1335,13 @@
private void handleOpTimeoutLocked() {
switch (mVerb) {
case VERB_BINDING:
- onSlowAppResponseLocked(/* reschedule */ false, /* updateStopReasons */ true,
+ // The system may have been too busy. Don't drop the job or trigger an ANR.
+ onSlowAppResponseLocked(/* reschedule */ true, /* updateStopReasons */ true,
/* texCounterMetricId */
"job_scheduler.value_cntr_w_uid_slow_app_response_binding",
/* debugReason */ "timed out while binding",
/* anrMessage */ "Timed out while trying to bind",
- CompatChanges.isChangeEnabled(ANR_PRE_UDC_APIS_ON_SLOW_RESPONSES,
- mRunningJob.getUid()));
+ /* triggerAnr */ false);
break;
case VERB_STARTING:
// Client unresponsive - wedged or failed to respond in time. We don't really
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index 6d938de..45f15db 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -22,6 +22,8 @@
import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+import static com.android.server.job.Flags.FLAG_RELAX_PREFETCH_CONNECTIVITY_CONSTRAINT_ONLY_ON_CHARGER;
+import static com.android.server.job.Flags.relaxPrefetchConnectivityConstraintOnlyOnCharger;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -929,6 +931,11 @@
// Need to at least know the estimated download bytes for a prefetch job.
return false;
}
+ if (relaxPrefetchConnectivityConstraintOnlyOnCharger()) {
+ if (!mService.isBatteryCharging()) {
+ return false;
+ }
+ }
// See if we match after relaxing any unmetered request
final NetworkCapabilities.Builder builder =
@@ -1735,6 +1742,14 @@
Predicate<JobStatus> predicate) {
final long nowElapsed = sElapsedRealtimeClock.millis();
+ pw.println("Aconfig flags:");
+ pw.increaseIndent();
+ pw.print(FLAG_RELAX_PREFETCH_CONNECTIVITY_CONSTRAINT_ONLY_ON_CHARGER,
+ relaxPrefetchConnectivityConstraintOnlyOnCharger());
+ pw.println();
+ pw.decreaseIndent();
+ pw.println();
+
if (mRequestedWhitelistJobs.size() > 0) {
pw.print("Requested standby exceptions:");
for (int i = 0; i < mRequestedWhitelistJobs.size(); i++) {
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/Android.bp b/api/Android.bp
index 45e70719..222275f 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -82,7 +82,6 @@
"framework-media",
"framework-mediaprovider",
"framework-ondevicepersonalization",
- "framework-pdf",
"framework-permission",
"framework-permission-s",
"framework-scheduling",
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/StubLibraries.bp b/api/StubLibraries.bp
index 2d9c988..fa4bc0f 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -438,6 +438,26 @@
full_api_surface_stub: "android_module_lib_stubs_current_full.from-text",
}
+// This module generates a stub jar that is a union of the test and module lib
+// non-updatable api contributions. Modules should not depend on the stub jar
+// generated from this module, as this module is strictly used for hiddenapi only.
+java_api_library {
+ name: "android-non-updatable.stubs.test_module_lib",
+ api_surface: "module_lib",
+ api_contributions: [
+ "api-stubs-docs-non-updatable.api.contribution",
+ "system-api-stubs-docs-non-updatable.api.contribution",
+ "test-api-stubs-docs-non-updatable.api.contribution",
+ "module-lib-api-stubs-docs-non-updatable.api.contribution",
+ ],
+ defaults: ["android-non-updatable_from_text_defaults"],
+ full_api_surface_stub: "android_test_module_lib_stubs_current.from-text",
+
+ // This module is only used for hiddenapi, and other modules should not
+ // depend on this module.
+ visibility: ["//visibility:private"],
+}
+
java_defaults {
name: "android_stubs_dists_default",
dist: {
@@ -757,6 +777,30 @@
}
java_api_library {
+ name: "android_test_module_lib_stubs_current.from-text",
+ api_surface: "module-lib",
+ defaults: [
+ "android_stubs_current_contributions",
+ "android_system_stubs_current_contributions",
+ "android_test_stubs_current_contributions",
+ "android_module_lib_stubs_current_contributions",
+ ],
+ libs: [
+ "android_module_lib_stubs_current_full.from-text",
+ "stub-annotations",
+ ],
+ api_contributions: [
+ "test-api-stubs-docs-non-updatable.api.contribution",
+ ],
+
+ // This module is only used to build android-non-updatable.stubs.test_module_lib
+ // and other modules should not depend on this module.
+ visibility: [
+ "//visibility:private",
+ ],
+}
+
+java_api_library {
name: "android_system_server_stubs_current.from-text",
api_surface: "system-server",
api_contributions: [
diff --git a/api/javadoc-lint-baseline b/api/javadoc-lint-baseline
index 8713126..c28480e 100644
--- a/api/javadoc-lint-baseline
+++ b/api/javadoc-lint-baseline
@@ -8,9 +8,12 @@
android/adservices/ondevicepersonalization/ExecuteOutput.java:31: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.ExecuteOutput [101]
android/adservices/ondevicepersonalization/ExecuteOutput.java:93: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.IsolatedWorker#onExecute() IsolatedWorker#onExecute()" in android.adservices.ondevicepersonalization.ExecuteOutput.Builder [101]
android/adservices/ondevicepersonalization/IsolatedService.java:18: lint: Unresolved link/see tag "SurfaceView" in android.adservices.ondevicepersonalization.IsolatedService [101]
+android/adservices/ondevicepersonalization/IsolatedService.java:18: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.OnDevicePersonalizationManager#execute" in android.adservices.ondevicepersonalization.IsolatedService [101]
android/adservices/ondevicepersonalization/IsolatedService.java:119: lint: Unresolved link/see tag "IsolatedCmputationCallback#onWebViewEvent()" in android.adservices.ondevicepersonalization.IsolatedService [101]
+android/adservices/ondevicepersonalization/IsolatedService.java:119: lint: Unresolved link/see tag "IsolatedCmputationCallback#onEvent()" in android.adservices.ondevicepersonalization.IsolatedService [101]
android/adservices/ondevicepersonalization/IsolatedService.java:119: lint: Unresolved link/see tag "WebView" in android.adservices.ondevicepersonalization.IsolatedService [101]
android/adservices/ondevicepersonalization/IsolatedWorker.java:9: lint: Unresolved link/see tag "RunTimeException" in android.adservices.ondevicepersonalization.IsolatedWorker [101]
+android/adservices/ondevicepersonalization/IsolatedWorker.java:24: lint: Unresolved link/see tag "android.adservices.ondevicepersonalization.OnDevicePersonalizationManager#execute" in android.adservices.ondevicepersonalization.IsolatedWorker [101]
android/adservices/ondevicepersonalization/IsolatedWorker.java:57: lint: Unresolved link/see tag "#onExecute()" in android.adservices.ondevicepersonalization.IsolatedWorker [101]
android/adservices/ondevicepersonalization/IsolatedWorker.java:74: lint: Unresolved link/see tag "#onRender()" in android.adservices.ondevicepersonalization.IsolatedWorker [101]
android/adservices/ondevicepersonalization/OnDevicePersonalizationManager.java:-11: lint: Unresolved link/see tag "requestSurfacePackage" in android.adservices.ondevicepersonalization.OnDevicePersonalizationManager [101]
@@ -83,35 +86,12 @@
android/app/job/JobParameters.java:128: lint: Unresolved link/see tag "android.app.job.JobInfo.Builder#setPeriodic(boolean) periodic jobs" in android.app.job.JobParameters [101]
android/app/sdksandbox/AppOwnedSdkSandboxInterface.java:9: lint: Unresolved link/see tag "SdkSandboxController#getAppOwnedSdkSandboxInterfaces" in android.app.sdksandbox.AppOwnedSdkSandboxInterface [101]
android/app/sdksandbox/SdkSandboxManager.java:112: lint: Unresolved link/see tag "AppOwnedSdkSandboxInterfaces" in android.app.sdksandbox.SdkSandboxManager [101]
-android/companion/CompanionDeviceService.java:273: lint: Unresolved link/see tag "android.companion.AssociationInfo#isSelfManaged() self-managed" in android.companion.CompanionDeviceService [101]
-android/companion/CompanionDeviceService.java:282: lint: Unresolved link/see tag "android.companion.AssociationInfo#isSelfManaged() self-managed" in android.companion.CompanionDeviceService [101]
-android/companion/virtual/VirtualDevice.java:15: lint: Unresolved link/see tag "android.companion.virtual.VirtualDeviceManager.VirtualDevice VirtualDeviceManager.VirtualDevice" in android.companion.virtual.VirtualDevice [101]
-android/companion/virtual/VirtualDevice.java:70: lint: Unresolved link/see tag "android.companion.virtual.VirtualDeviceParams.Builder#setName(String)" in android.companion.virtual.VirtualDevice [101]
android/content/AttributionSource.java:291: lint: Unresolved link/see tag "setNextAttributionSource" in android.content.AttributionSource.Builder [101]
android/content/Context.java:2872: lint: Unresolved link/see tag "android.telephony.MmsManager" in android.content.Context [101]
android/content/Intent.java:4734: lint: Unresolved link/see tag "android.content.pm.UserInfo#isProfile()" in android.content.Intent [101]
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]
-android/credentials/CredentialDescription.java:89: lint: Unresolved link/see tag "android.credentials.CredentialDescription#mSupportedElementKeys CredentialDescription#mSupportedElementKeys" in android.credentials.CredentialDescription [101]
-android/credentials/CredentialDescription.java:89: lint: Unresolved link/see tag "android.credentials.CredentialDescription#mType CredentialDescription#mType" in android.credentials.CredentialDescription [101]
-android/credentials/CredentialDescription.java:101: lint: Unresolved link/see tag "android.credentials.CredentialDescription#mSupportedElementKeys CredentialDescription#mSupportedElementKeys" in android.credentials.CredentialDescription [101]
-android/credentials/CredentialDescription.java:101: lint: Unresolved link/see tag "android.credentials.CredentialDescription#mType CredentialDescription#mType" in android.credentials.CredentialDescription [101]
-android/credentials/GetCredentialException.java:22: lint: Unresolved link/see tag "android.credentials.CredentialManager#getCredential(android.credentials.GetCredentialRequest,android.app.Activity,android.os.CancellationSignal,java.util.concurrent.Executor,android.os.OutcomeReceiver) CredentialManager#getCredential(GetCredentialRequest, Activity, CancellationSignal, Executor, OutcomeReceiver)" in android.credentials.GetCredentialException [101]
-android/credentials/GetCredentialException.java:103: lint: Unresolved link/see tag "android.credentials.CredentialManager#getCredential(android.credentials.GetCredentialRequest,android.app.Activity,android.os.CancellationSignal,java.util.concurrent.Executor,android.os.OutcomeReceiver)" in android.credentials.GetCredentialException [101]
-android/credentials/PrepareGetCredentialResponse.java:20: lint: Unresolved link/see tag "android.credentials.CredentialManager#getCredential(android.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle,android.app.Activity,android.os.CancellationSignal,java.util.concurrent.Executor,android.os.OutcomeReceiver) CredentialManager#getCredential(PendingGetCredentialHandle, Activity, CancellationSignal, Executor, OutcomeReceiver)" in android.credentials.PrepareGetCredentialResponse [101]
-android/credentials/PrepareGetCredentialResponse.java:68: lint: Unresolved link/see tag "android.credentials.CredentialManager#getCredential(android.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle,android.app.Activity,android.os.CancellationSignal,java.util.concurrent.Executor,android.os.OutcomeReceiver) CredentialManager#getCredential(PendingGetCredentialHandle, Activity, CancellationSignal, Executor, OutcomeReceiver)" in android.credentials.PrepareGetCredentialResponse [101]
-android/credentials/PrepareGetCredentialResponse.java:83: lint: Unresolved link/see tag "android.credentials.CredentialManager#getCredential(android.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle,android.app.Activity,android.os.CancellationSignal,java.util.concurrent.Executor,android.os.OutcomeReceiver)" in android.credentials.PrepareGetCredentialResponse.PendingGetCredentialHandle [101]
android/graphics/Paint.java:838: lint: Unresolved link/see tag "android.annotation.ColorLong ColorLong" in android.graphics.Paint [101]
android/graphics/text/LineBreaker.java:246: lint: Unresolved link/see tag "StaticLayout.Builder#setUseBoundsForWidth(boolean)" in android.graphics.text.LineBreaker.Builder [101]
android/hardware/camera2/CameraCharacteristics.java:2169: lint: Unresolved link/see tag "android.hardware.camera2.CameraDevice#stream-use-case-capability-additional-guaranteed-configurations guideline" in android.hardware.camera2.CameraCharacteristics [101]
@@ -162,8 +142,6 @@
android/media/AudioManager.java:311: lint: Unresolved link/see tag "android.media.audiopolicy.AudioVolumeGroup" in android.media.AudioManager [101]
android/media/AudioManager.java:313: lint: Unresolved link/see tag "android.media.audiopolicy.AudioVolumeGroup" in android.media.AudioManager [101]
android/media/AudioMetadata.java:118: lint: Unresolved link/see tag "android.media.AudioPresentation.ContentClassifier One of {@link android.media.AudioPresentation#CONTENT_UNKNOWN AudioPresentation#CONTENT_UNKNOWN}, {@link android.media.AudioPresentation#CONTENT_MAIN AudioPresentation#CONTENT_MAIN}, {@link android.media.AudioPresentation#CONTENT_MUSIC_AND_EFFECTS AudioPresentation#CONTENT_MUSIC_AND_EFFECTS}, {@link android.media.AudioPresentation#CONTENT_VISUALLY_IMPAIRED AudioPresentation#CONTENT_VISUALLY_IMPAIRED}, {@link android.media.AudioPresentation#CONTENT_HEARING_IMPAIRED AudioPresentation#CONTENT_HEARING_IMPAIRED}, {@link android.media.AudioPresentation#CONTENT_DIALOG AudioPresentation#CONTENT_DIALOG}, {@link android.media.AudioPresentation#CONTENT_COMMENTARY AudioPresentation#CONTENT_COMMENTARY}, {@link android.media.AudioPresentation#CONTENT_EMERGENCY AudioPresentation#CONTENT_EMERGENCY}, {@link android.media.AudioPresentation#CONTENT_VOICEOVER AudioPresentation#CONTENT_VOICEOVER}." in android.media.AudioMetadata.Format [101]
-android/media/MediaRouter2.java:162: lint: Unresolved link/see tag "#getInstance(android.content.Context,java.lang.String)" in android.media.MediaRouter2 [101]
-android/media/midi/MidiUmpDeviceService.java:-1: lint: Unresolved link/see tag "#MidiDeviceService" in android.media.midi.MidiUmpDeviceService [101]
android/media/tv/SectionRequest.java:44: lint: Unresolved link/see tag "android.media.tv.BroadcastInfoRequest.RequestOption BroadcastInfoRequest.RequestOption" in android.media.tv.SectionRequest [101]
android/media/tv/SectionResponse.java:39: lint: Unresolved link/see tag "android.media.tv.BroadcastInfoRequest.RequestOption BroadcastInfoRequest.RequestOption" in android.media.tv.SectionResponse [101]
android/media/tv/TableRequest.java:48: lint: Unresolved link/see tag "android.media.tv.BroadcastInfoRequest.RequestOption BroadcastInfoRequest.RequestOption" in android.media.tv.TableRequest [101]
@@ -209,160 +187,18 @@
android/security/KeyStoreException.java:27: lint: Unresolved link/see tag "android.security.KeyStoreException.PublicErrorCode PublicErrorCode" in android.security.KeyStoreException [101]
android/service/autofill/FillResponse.java:86: lint: Unresolved link/see tag "setFieldClassificationIds" in android.service.autofill.FillResponse.Builder [101]
android/service/autofill/SaveInfo.java:623: lint: Unresolved link/see tag "FillRequest.getHints()" in android.service.autofill.SaveInfo.Builder [101]
-android/service/credentials/Action.java:3: lint: Unresolved link/see tag "androidx.credentials.provider" in android.service.credentials.Action [101]
-android/service/credentials/Action.java:3: lint: Unresolved link/see tag "androidx.credentials.provider.Action" in android.service.credentials.Action [101]
-android/service/credentials/BeginCreateCredentialResponse.java:85: lint: Unresolved link/see tag "Manifest.permission.PROVIDE_REMOTE_CREDENTIALS" in android.service.credentials.BeginCreateCredentialResponse.Builder [101]
-android/service/credentials/BeginGetCredentialResponse.java:80: lint: Unresolved link/see tag "Manifest.permission.PROVIDE_REMOTE_CREDENTIALS" in android.service.credentials.BeginGetCredentialResponse.Builder [101]
-android/service/credentials/CallingAppInfo.java:73: lint: Unresolved link/see tag "android.Manifest.permission.CREDENTIAL_MANAGER_SET_ORIGIN" in android.service.credentials.CallingAppInfo [101]
-android/service/credentials/CreateEntry.java:6: lint: Unresolved link/see tag "androidx.credentials.provider" in android.service.credentials.CreateEntry [101]
-android/service/credentials/CreateEntry.java:6: lint: Unresolved link/see tag "androidx.credentials.provider.CreateEntry" in android.service.credentials.CreateEntry [101]
-android/service/credentials/CredentialEntry.java:11: lint: Unresolved link/see tag "androidx.credentials.provider" in android.service.credentials.CredentialEntry [101]
-android/service/credentials/CredentialEntry.java:11: lint: Unresolved link/see tag "androidx.credentials.provider.CredentialEntry" in android.service.credentials.CredentialEntry [101]
-android/service/credentials/RemoteEntry.java:13: lint: Unresolved link/see tag "androidx.credentials.provider" in android.service.credentials.RemoteEntry [101]
-android/service/credentials/RemoteEntry.java:13: lint: Unresolved link/see tag "androidx.credentials.provider.RemoteEntry" in android.service.credentials.RemoteEntry [101]
android/service/notification/NotificationListenerService.java:417: lint: Unresolved link/see tag "android.service.notification.NotificationAssistantService notification assistant" in android.service.notification.NotificationListenerService [101]
android/service/notification/NotificationListenerService.java:435: lint: Unresolved link/see tag "android.service.notification.NotificationAssistantService notification assistant" in android.service.notification.NotificationListenerService [101]
android/service/notification/NotificationListenerService.java:1155: lint: Unresolved link/see tag "android.service.notification.NotificationAssistantService NotificationAssistantService" in android.service.notification.NotificationListenerService.Ranking [101]
android/service/notification/NotificationListenerService.java:1166: lint: Unresolved link/see tag "android.service.notification.NotificationAssistantService NotificationAssistantService" in android.service.notification.NotificationListenerService.Ranking [101]
android/service/quickaccesswallet/WalletCard.java:285: lint: Unresolved link/see tag "PackageManager.FEATURE_WALLET_LOCATION_BASED_SUGGESTIONS" in android.service.quickaccesswallet.WalletCard.Builder [101]
android/service/voice/VoiceInteractionSession.java:293: lint: Unresolved link/see tag "android.service.voice.VoiceInteractionService#KEY_SHOW_SESSION_ID VoiceInteractionService#KEY_SHOW_SESSION_ID" in android.service.voice.VoiceInteractionSession [101]
-android/telecom/Call.java:94: lint: unable to parse link/see tag: #playDtmfTone(char [101]
-android/telecom/CallControl.java:163: lint: Unresolved link/see tag "android.telecom.CallStreamingService CallStreamingService" in android.telecom.CallControl [101]
-android/telecom/CallControlCallback.java:63: lint: Unresolved link/see tag "android.telecom.CallAttributes.CallType" in android.telecom.CallControlCallback [101]
-android/telecom/PhoneAccountSuggestion.java:17: lint: Unresolved link/see tag "android.telecom.PhoneAccountSuggestionService PhoneAccountSuggestionService" in android.telecom.PhoneAccountSuggestion [101]
-android/telephony/CarrierConfigManager.java:2934: lint: Unresolved link/see tag "android.telephony.TelephonyManager.PremiumCapability TelephonyManager.PremiumCapability" in android.telephony.CarrierConfigManager [101]
-android/telephony/CarrierConfigManager.java:4020: lint: Unresolved link/see tag "ImsRegistrationImplBase.ImsRegistrationTech" in android.telephony.CarrierConfigManager.Ims [101]
-android/telephony/CarrierConfigManager.java:4108: lint: Unresolved link/see tag "ImsRegistrationImplBase.ImsRegistrationTech" in android.telephony.CarrierConfigManager.Ims [101]
-android/telephony/CarrierConfigManager.java:3920: lint: Unresolved link/see tag "android.telephony.ims.SipDelegateManager" in android.telephony.CarrierConfigManager.Ims [101]
-android/telephony/CarrierConfigManager.java:4001: lint: Unresolved link/see tag "ImsRegistrationImplBase.ImsRegistrationTech" in android.telephony.CarrierConfigManager.Ims [101]
-android/telephony/CarrierConfigManager.java:4089: lint: Unresolved link/see tag "ImsRegistrationImplBase.ImsRegistrationTech" in android.telephony.CarrierConfigManager.Ims [101]
-android/telephony/NetworkRegistrationInfo.java:131: lint: Unresolved link/see tag "android.telephony.Annotation.NetworkType NetworkType" in android.telephony.NetworkRegistrationInfo [101]
-android/telephony/PhoneStateListener.java:293: lint: Unresolved link/see tag "android.telephony.PreciseDisconnectCause PreciseDisconnectCause" in android.telephony.PhoneStateListener [101]
-android/telephony/PhoneStateListener.java:544: lint: Unresolved link/see tag "android.telephony.PreciseDisconnectCause PreciseDisconnectCause" in android.telephony.PhoneStateListener [101]
-android/telephony/SubscriptionManager.java:1385: lint: Unresolved link/see tag "Build.VERSION_CODES.P" in android.telephony.SubscriptionManager.OnSubscriptionsChangedListener [101]
-android/telephony/SubscriptionManager.java:1385: lint: Unresolved link/see tag "Build.VERSION_CODES.V" in android.telephony.SubscriptionManager.OnSubscriptionsChangedListener [101]
-android/telephony/SubscriptionPlan.java:134: lint: Unresolved link/see tag "android.telephony.Annotation.NetworkType NetworkType" in android.telephony.SubscriptionPlan [101]
-android/telephony/SubscriptionPlan.java:288: lint: Unresolved link/see tag "android.telephony.Annotation.NetworkType NetworkType" in android.telephony.SubscriptionPlan.Builder [101]
-android/telephony/TelephonyCallback.java:113: lint: Unresolved link/see tag "android.telephony.PreciseDisconnectCause PreciseDisconnectCause" in android.telephony.TelephonyCallback.CallDisconnectCauseListener [101]
-android/telephony/TelephonyManager.java:2544: lint: Unresolved link/see tag "android.telephony.TelephonyManager.CarrierRestrictionStatus CarrierRestrictionStatus" in android.telephony.TelephonyManager [101]
-android/telephony/TelephonyManager.java:2841: lint: Unresolved link/see tag "android.telephony.TelephonyManager.SetOpportunisticSubscriptionResult TelephonyManager.SetOpportunisticSubscriptionResult" in android.telephony.TelephonyManager [101]
-android/telephony/TelephonyManager.java:3273: lint: Unresolved link/see tag "android.telephony.TelephonyManager.PurchasePremiumCapabilityResult PurchasePremiumCapabilityResult" in android.telephony.TelephonyManager [101]
-android/telephony/TelephonyManager.java:3989: lint: Unresolved link/see tag "android.telephony.TelephonyManager.MobileDataPolicy MobileDataPolicy" in android.telephony.TelephonyManager [101]
-android/telephony/data/ApnSetting.java:39: lint: Unresolved link/see tag "android.telephony.data.DataCallResponse#getMtuV4() DataCallResponse#getMtuV4()" in android.telephony.data.ApnSetting [101]
-android/telephony/data/ApnSetting.java:50: lint: Unresolved link/see tag "android.telephony.data.DataCallResponse#getMtuV6() DataCallResponse#getMtuV6()" in android.telephony.data.ApnSetting [101]
-android/telephony/data/ApnSetting.java:597: lint: Unresolved link/see tag "android.telephony.data.DataCallResponse#getMtuV4() DataCallResponse#getMtuV4()" in android.telephony.data.ApnSetting.Builder [101]
-android/telephony/data/ApnSetting.java:611: lint: Unresolved link/see tag "android.telephony.data.DataCallResponse#getMtuV6() DataCallResponse#getMtuV6()" in android.telephony.data.ApnSetting.Builder [101]
-android/telephony/euicc/EuiccManager.java:331: lint: Unresolved link/see tag "android.service.euicc.EuiccService#ACTION_BIND_CARRIER_PROVISIONING_SERVICE" in android.telephony.euicc.EuiccManager [101]
-android/telephony/ims/ImsMmTelManager.java:117: lint: Unresolved link/see tag "android.telephony.ims.ImsService ImsService" in android.telephony.ims.ImsMmTelManager [101]
-android/telephony/ims/ImsRcsManager.java:43: lint: Unresolved link/see tag "android.telephony.ims.ImsService ImsService" in android.telephony.ims.ImsRcsManager [101]
-android/telephony/ims/ProvisioningManager.java:102: lint: Unresolved link/see tag "android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability MmTelFeature.MmTelCapabilities.MmTelCapability" in android.telephony.ims.ProvisioningManager [101]
-android/telephony/ims/ProvisioningManager.java:102: lint: Unresolved link/see tag "android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationTech ImsRegistrationImplBase.ImsRegistrationTech" in android.telephony.ims.ProvisioningManager [101]
-android/telephony/ims/ProvisioningManager.java:136: lint: Unresolved link/see tag "android.telephony.ims.ImsRcsManager.RcsImsCapabilityFlag ImsRcsManager.RcsImsCapabilityFlag" in android.telephony.ims.ProvisioningManager [101]
-android/telephony/ims/RegistrationManager.java:21: lint: Unresolved link/see tag "android.telephony.ims.feature.ImsFeature ImsFeature" in android.telephony.ims.RegistrationManager [101]
-android/telephony/ims/RegistrationManager.java:24: lint: Unresolved link/see tag "android.telephony.ims.ImsService ImsService" in android.telephony.ims.RegistrationManager [101]
android/text/DynamicLayout.java:141: lint: Unresolved link/see tag "LineBreakconfig" in android.text.DynamicLayout [101]
android/text/WordSegmentFinder.java:13: lint: Unresolved link/see tag "android.text.method.WordIterator WordIterator" in android.text.WordSegmentFinder [101]
-android/view/InputDevice.java:71: lint: Unresolved link/see tag "InputManagerGlobal.InputDeviceListener" in android.view.InputDevice [101]
android/view/PixelCopy.java:468: lint: Unresolved link/see tag "android.view.PixelCopy.CopyResultStatus CopyResultStatus" in android.view.PixelCopy.Result [101]
-android/view/ScrollFeedbackProvider.java:-25: lint: Unresolved link/see tag "InputManager" in android.view.ScrollFeedbackProvider [101]
-android/view/ScrollFeedbackProvider.java:-25: lint: Unresolved link/see tag "InputManager#getInputDeviceIds()" in android.view.ScrollFeedbackProvider [101]
-android/view/SurfaceControl.java:823: lint: Unresolved link/see tag "android.view.SurfaceControl.TrustedPresentationCallback TrustedPresentationCallback" in android.view.SurfaceControl.Transaction [101]
-android/view/SurfaceControl.java:900: lint: Unresolved link/see tag "android.view.SurfaceControl.TrustedPresentationCallback TrustedPresentationCallback" in android.view.SurfaceControl.Transaction [101]
-android/view/SurfaceControl.java:908: lint: Unresolved link/see tag "android.view.SurfaceControl.TrustedPresentationCallback TrustedPresentationCallback" in android.view.SurfaceControl.Transaction [101]
-android/view/View.java:1647: lint: Unresolved link/see tag "androidx.core.view.ViewCompat#setAccessibilityPaneTitle(View, CharSequence)" in android.view.View [101]
-android/view/View.java:4669: lint: Unresolved link/see tag "androidx.core.view.ViewCompat#setScreenReaderFocusable(View, boolean)" in android.view.View [101]
-android/view/View.java:4712: lint: Unresolved link/see tag "androidx.core.view.ViewCompat#setAccessibilityHeading(View, boolean)" in android.view.View [101]
-android/view/WindowManager.java:230: lint: Unresolved link/see tag "android.annotation.UiContext" in android.view.WindowManager [101]
-android/view/WindowManager.java:247: lint: Unresolved link/see tag "android.annotation.UiContext" in android.view.WindowManager [101]
-android/view/WindowManager.java:822: lint: @attr must be a field: android.R.attr#Window_windowNoMoveAnimation [106]
-android/view/WindowManager.java:832: lint: @attr must be a field: android.R.attr#Window_windowNoMoveAnimation [106]
-android/view/WindowMetrics.java:22: lint: Unresolved link/see tag "android.annotation.UiContext" in android.view.WindowMetrics [101]
-android/view/WindowMetrics.java:57: lint: Unresolved link/see tag "android.annotation.UiContext" in android.view.WindowMetrics [101]
-android/view/WindowMetrics.java:114: lint: Unresolved link/see tag "android.annotation.UiContext" in android.view.WindowMetrics [101]
-android/view/WindowMetrics.java:127: lint: Unresolved link/see tag "android.annotation.UiContext" in android.view.WindowMetrics [101]
-android/view/accessibility/AccessibilityNodeInfo.java:368: lint: Unresolved link/see tag "androidx.core.view.ViewCompat#addAccessibilityAction(View, AccessibilityNodeInfoCompat.AccessibilityActionCompat)" in android.view.accessibility.AccessibilityNodeInfo [101]
-android/view/accessibility/AccessibilityNodeInfo.java:3246: lint: Unresolved link/see tag "androidx.core.view.ViewCompat#addAccessibilityAction(View, AccessibilityNodeInfoCompat.AccessibilityActionCompat)" in android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction [101]
-android/view/displayhash/DisplayHashResultCallback.java:38: lint: Unresolved link/see tag "android.view.displayhash.DisplayHashResultCallback.DisplayHashErrorCode DisplayHashErrorCode" in android.view.displayhash.DisplayHashResultCallback [101]
-android/view/inputmethod/EditorInfo.java:107: lint: Unresolved link/see tag "android.widget.Editor Editor" in android.view.inputmethod.EditorInfo [101]
-android/view/inputmethod/EditorInfo.java:122: lint: Unresolved link/see tag "android.widget.Editor Editor" in android.view.inputmethod.EditorInfo [101]
-android/view/inputmethod/InputMethodManager.java:423: lint: Unresolved link/see tag "android.widget.Editor Editor" in android.view.inputmethod.InputMethodManager [101]
-android/view/inputmethod/InputMethodManager.java:447: lint: Unresolved link/see tag "android.widget.Editor Editor" in android.view.inputmethod.InputMethodManager [101]
-android/view/inputmethod/InputMethodManager.java:456: lint: Unresolved link/see tag "android.widget.Editor Editor" in android.view.inputmethod.InputMethodManager [101]
-android/view/inspector/PropertyReader.java:141: lint: Unresolved link/see tag "android.annotation.ColorInt ColorInt" in android.view.inspector.PropertyReader [101]
android/window/BackEvent.java:24: lint: Unresolved link/see tag "android.window.BackMotionEvent BackMotionEvent" in android.window.BackEvent [101]
-com/android/server/wm/CompatModePackages.java:92: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED" in android [101]
-com/android/server/wm/CompatModePackages.java:92: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED_INVERSE" in android [101]
-com/android/server/wm/CompatModePackages.java:92: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_30" in android [101]
-com/android/server/wm/CompatModePackages.java:92: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_35" in android [101]
-com/android/server/wm/CompatModePackages.java:92: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_40" in android [101]
-com/android/server/wm/CompatModePackages.java:92: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_45" in android [101]
-com/android/server/wm/CompatModePackages.java:92: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_50" in android [101]
-com/android/server/wm/CompatModePackages.java:92: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_55" in android [101]
-com/android/server/wm/CompatModePackages.java:92: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_60" in android [101]
-com/android/server/wm/CompatModePackages.java:92: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_65" in android [101]
-com/android/server/wm/CompatModePackages.java:92: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_70" in android [101]
-com/android/server/wm/CompatModePackages.java:92: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_75" in android [101]
-com/android/server/wm/CompatModePackages.java:92: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_80" in android [101]
-com/android/server/wm/CompatModePackages.java:92: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_85" in android [101]
-com/android/server/wm/CompatModePackages.java:92: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_90" in android [101]
-com/android/server/wm/CompatModePackages.java:122: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED" in android [101]
-com/android/server/wm/CompatModePackages.java:122: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED_INVERSE" in android [101]
-com/android/server/wm/CompatModePackages.java:122: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_30" in android [101]
-com/android/server/wm/CompatModePackages.java:122: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_35" in android [101]
-com/android/server/wm/CompatModePackages.java:122: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_40" in android [101]
-com/android/server/wm/CompatModePackages.java:122: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_45" in android [101]
-com/android/server/wm/CompatModePackages.java:122: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_50" in android [101]
-com/android/server/wm/CompatModePackages.java:122: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_55" in android [101]
-com/android/server/wm/CompatModePackages.java:122: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_60" in android [101]
-com/android/server/wm/CompatModePackages.java:122: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_65" in android [101]
-com/android/server/wm/CompatModePackages.java:122: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_70" in android [101]
-com/android/server/wm/CompatModePackages.java:122: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_75" in android [101]
-com/android/server/wm/CompatModePackages.java:122: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_80" in android [101]
-com/android/server/wm/CompatModePackages.java:122: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_85" in android [101]
-com/android/server/wm/CompatModePackages.java:122: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_90" in android [101]
-com/android/server/wm/CompatModePackages.java:135: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED" in android [101]
-com/android/server/wm/CompatModePackages.java:135: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED_INVERSE" in android [101]
-com/android/server/wm/CompatModePackages.java:135: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_90" in android [101]
-com/android/server/wm/CompatModePackages.java:148: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED" in android [101]
-com/android/server/wm/CompatModePackages.java:148: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED_INVERSE" in android [101]
-com/android/server/wm/CompatModePackages.java:148: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_85" in android [101]
-com/android/server/wm/CompatModePackages.java:161: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED" in android [101]
-com/android/server/wm/CompatModePackages.java:161: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED_INVERSE" in android [101]
-com/android/server/wm/CompatModePackages.java:161: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_80" in android [101]
-com/android/server/wm/CompatModePackages.java:174: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED" in android [101]
-com/android/server/wm/CompatModePackages.java:174: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED_INVERSE" in android [101]
-com/android/server/wm/CompatModePackages.java:174: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_75" in android [101]
-com/android/server/wm/CompatModePackages.java:187: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED" in android [101]
-com/android/server/wm/CompatModePackages.java:187: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED_INVERSE" in android [101]
-com/android/server/wm/CompatModePackages.java:187: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_70" in android [101]
-com/android/server/wm/CompatModePackages.java:200: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED" in android [101]
-com/android/server/wm/CompatModePackages.java:200: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED_INVERSE" in android [101]
-com/android/server/wm/CompatModePackages.java:200: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_65" in android [101]
-com/android/server/wm/CompatModePackages.java:213: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED" in android [101]
-com/android/server/wm/CompatModePackages.java:213: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED_INVERSE" in android [101]
-com/android/server/wm/CompatModePackages.java:213: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_60" in android [101]
-com/android/server/wm/CompatModePackages.java:226: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED" in android [101]
-com/android/server/wm/CompatModePackages.java:226: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED_INVERSE" in android [101]
-com/android/server/wm/CompatModePackages.java:226: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_55" in android [101]
-com/android/server/wm/CompatModePackages.java:239: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED" in android [101]
-com/android/server/wm/CompatModePackages.java:239: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED_INVERSE" in android [101]
-com/android/server/wm/CompatModePackages.java:239: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_50" in android [101]
-com/android/server/wm/CompatModePackages.java:252: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED" in android [101]
-com/android/server/wm/CompatModePackages.java:252: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED_INVERSE" in android [101]
-com/android/server/wm/CompatModePackages.java:252: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_45" in android [101]
-com/android/server/wm/CompatModePackages.java:265: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED" in android [101]
-com/android/server/wm/CompatModePackages.java:265: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED_INVERSE" in android [101]
-com/android/server/wm/CompatModePackages.java:265: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_40" in android [101]
-com/android/server/wm/CompatModePackages.java:278: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED" in android [101]
-com/android/server/wm/CompatModePackages.java:278: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED_INVERSE" in android [101]
-com/android/server/wm/CompatModePackages.java:278: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_35" in android [101]
-com/android/server/wm/CompatModePackages.java:291: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED" in android [101]
-com/android/server/wm/CompatModePackages.java:291: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALED_INVERSE" in android [101]
-com/android/server/wm/CompatModePackages.java:291: lint: Unresolved link/see tag "CompatModePackages#DOWNSCALE_30" in android [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]
@@ -370,22 +206,11 @@
android/service/voice/AlwaysOnHotwordDetector.java:278: lint: Unresolved link/see tag "#STATE_ERROR" in android [101]
android/service/voice/AlwaysOnHotwordDetector.java:278: lint: Unresolved link/see tag "Callback#onFailure" in android [101]
android/service/voice/AlwaysOnHotwordDetector.java:278: lint: Unresolved link/see tag "Callback#onUnknownFailure" in android [101]
-android/telephony/TelephonyRegistryManager.java:242: lint: Unresolved link/see tag "#listenFromListener" in android [101]
-android/view/animation/AnimationUtils.java:64: lint: Unresolved link/see tag "Build.VERSION_CODES#VANILLA_ICE_CREAM" in android.view.animation.AnimationUtils [101]
-android/view/contentcapture/ContentCaptureSession.java:188: lint: Unresolved link/see tag "UPSIDE_DOWN_CAKE" in android.view.contentcapture.ContentCaptureSession [101]
com/android/internal/policy/PhoneWindow.java:172: lint: Unresolved link/see tag "Build.VERSION_CODES#VANILLA_ICE_CREAM" in com.android.internal.policy.PhoneWindow [101]
-com/android/server/companion/virtual/VirtualDeviceImpl.java:134: lint: Unresolved link/see tag "DisplayManager" in android [101]
-com/android/server/companion/virtual/VirtualDeviceImpl.java:134: lint: Unresolved link/see tag "VirtualDeviceManager.VirtualDevice" in android [101]
com/android/server/broadcastradio/aidl/ConversionUtils.java:70: lint: Unresolved link/see tag "IdentifierType#DAB_SID_EXT" in android [101]
com/android/server/broadcastradio/aidl/ConversionUtils.java:70: lint: Unresolved link/see tag "ProgramSelector#IDENTIFIER_TYPE_DAB_DMB_SID_EXT" in android [101]
com/android/server/broadcastradio/aidl/ConversionUtils.java:70: lint: Unresolved link/see tag "RadioTuner" in android [101]
-com/android/server/media/MediaSessionRecord.java:104: lint: Unresolved link/see tag "ComponentName" in android [101]
-com/android/server/media/MediaSessionRecord.java:104: lint: Unresolved link/see tag "IllegalArgumentException" in android [101]
-com/android/server/media/MediaSessionRecord.java:104: lint: Unresolved link/see tag "MediaSession#setMediaButtonBroadcastReceiver(ComponentName)" in android [101]
-com/android/server/media/MediaSessionRecord.java:114: lint: Unresolved link/see tag "IllegalArgumentException" in android [101]
-com/android/server/media/MediaSessionRecord.java:114: lint: Unresolved link/see tag "MediaSession#setMediaButtonReceiver(PendingIntent)" in android [101]
-com/android/server/media/MediaSessionRecord.java:114: lint: Unresolved link/see tag "PendingIntent" in android [101]
com/android/server/pm/PackageInstallerSession.java:313: lint: Unresolved link/see tag "Build.VERSION_CODES#S API 31" in android [101]
com/android/server/pm/PackageInstallerSession.java:313: lint: Unresolved link/see tag "PackageInstaller.SessionParams#setRequireUserAction" in android [101]
com/android/server/pm/PackageInstallerSession.java:327: lint: Unresolved link/see tag "#requestUserPreapproval(PreapprovalDetails, IntentSender)" in android [101]
@@ -393,14 +218,8 @@
com/android/server/pm/PackageInstallerSession.java:327: lint: Unresolved link/see tag "PackageInstaller.SessionParams#setRequestUpdateOwnership(boolean)" in android [101]
com/android/server/pm/PackageInstallerSession.java:358: lint: Unresolved link/see tag "IntentSender" in android [101]
com/android/server/devicepolicy/DevicePolicyManagerService.java:860: lint: Unresolved link/see tag "android.security.IKeyChainService#setGrant" in android [101]
-android/telephony/SubscriptionManager.java:1370: lint: Unresolved link/see tag "Build.VERSION_CODES.Q" in android.telephony.SubscriptionManager.OnSubscriptionsChangedListener [101]
-android/companion/virtual/sensor/VirtualSensorDirectChannelWriter.java:-4: lint: Invalid tag: @Override [131]
-android/companion/virtual/sensor/VirtualSensorDirectChannelWriter.java:-1: lint: Invalid tag: @Override [131]
-android/companion/virtual/sensor/VirtualSensorDirectChannelWriter.java:2: lint: Invalid tag: @Override [131]
android/os/BatteryStatsManager.java:260: lint: Invalid tag: @Deprecated [131]
android/os/BatteryStatsManager.java:275: lint: Invalid tag: @Deprecated [131]
-android/view/WindowManager.java:906: lint: @attr must be a field: android.R.attr#Window_windowNoMoveAnimation [106]
-android/view/WindowManager.java:916: lint: @attr must be a field: android.R.attr#Window_windowNoMoveAnimation [106]
java/lang/ClassLoader.java:853: lint: Unknown tag: @systemProperty [103]
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 96a9595..36cdd7f 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;
@@ -10992,6 +10994,7 @@
field public static final String ACTION_PACKAGE_REMOVED = "android.intent.action.PACKAGE_REMOVED";
field public static final String ACTION_PACKAGE_REPLACED = "android.intent.action.PACKAGE_REPLACED";
field public static final String ACTION_PACKAGE_RESTARTED = "android.intent.action.PACKAGE_RESTARTED";
+ field @FlaggedApi("android.content.pm.stay_stopped") public static final String ACTION_PACKAGE_UNSTOPPED = "android.intent.action.PACKAGE_UNSTOPPED";
field public static final String ACTION_PACKAGE_VERIFIED = "android.intent.action.PACKAGE_VERIFIED";
field public static final String ACTION_PASTE = "android.intent.action.PASTE";
field public static final String ACTION_PICK = "android.intent.action.PICK";
@@ -12673,6 +12676,7 @@
method public boolean isDeviceUpgrading();
method public abstract boolean isInstantApp();
method public abstract boolean isInstantApp(@NonNull String);
+ method @FlaggedApi("android.content.pm.stay_stopped") public boolean isPackageStopped(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method public boolean isPackageSuspended(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method public boolean isPackageSuspended();
method @CheckResult public abstract boolean isPermissionRevokedByPolicy(@NonNull String, @NonNull String);
@@ -14348,14 +14352,14 @@
public final class SQLiteDatabase extends android.database.sqlite.SQLiteClosable {
method public void beginTransaction();
method public void beginTransactionNonExclusive();
- method public void beginTransactionReadOnly();
+ method @FlaggedApi("android.database.sqlite.sqlite_apis_15") public void beginTransactionReadOnly();
method public void beginTransactionWithListener(@Nullable android.database.sqlite.SQLiteTransactionListener);
method public void beginTransactionWithListenerNonExclusive(@Nullable android.database.sqlite.SQLiteTransactionListener);
- method public void beginTransactionWithListenerReadOnly(@Nullable android.database.sqlite.SQLiteTransactionListener);
+ method @FlaggedApi("android.database.sqlite.sqlite_apis_15") public void beginTransactionWithListenerReadOnly(@Nullable android.database.sqlite.SQLiteTransactionListener);
method public android.database.sqlite.SQLiteStatement compileStatement(String) throws android.database.SQLException;
method @NonNull public static android.database.sqlite.SQLiteDatabase create(@Nullable android.database.sqlite.SQLiteDatabase.CursorFactory);
method @NonNull public static android.database.sqlite.SQLiteDatabase createInMemory(@NonNull android.database.sqlite.SQLiteDatabase.OpenParams);
- method @NonNull public android.database.sqlite.SQLiteRawStatement createRawStatement(@NonNull String);
+ method @FlaggedApi("android.database.sqlite.sqlite_apis_15") @NonNull public android.database.sqlite.SQLiteRawStatement createRawStatement(@NonNull String);
method public int delete(@NonNull String, @Nullable String, @Nullable String[]);
method public static boolean deleteDatabase(@NonNull java.io.File);
method public void disableWriteAheadLogging();
@@ -14366,13 +14370,13 @@
method public void execSQL(@NonNull String, @NonNull Object[]) throws android.database.SQLException;
method public static String findEditTable(String);
method public java.util.List<android.util.Pair<java.lang.String,java.lang.String>> getAttachedDbs();
- method public long getLastChangedRowCount();
- method public long getLastInsertRowId();
+ method @FlaggedApi("android.database.sqlite.sqlite_apis_15") public long getLastChangedRowCount();
+ method @FlaggedApi("android.database.sqlite.sqlite_apis_15") public long getLastInsertRowId();
method public long getMaximumSize();
method public long getPageSize();
method public String getPath();
method @Deprecated public java.util.Map<java.lang.String,java.lang.String> getSyncedTables();
- method public long getTotalChangedRowCount();
+ method @FlaggedApi("android.database.sqlite.sqlite_apis_15") public long getTotalChangedRowCount();
method public int getVersion();
method public boolean inTransaction();
method public long insert(@NonNull String, @Nullable String, @Nullable android.content.ContentValues);
@@ -14594,7 +14598,7 @@
method public int update(@NonNull android.database.sqlite.SQLiteDatabase, @NonNull android.content.ContentValues, @Nullable String, @Nullable String[]);
}
- public final class SQLiteRawStatement implements java.io.Closeable {
+ @FlaggedApi("android.database.sqlite.sqlite_apis_15") public final class SQLiteRawStatement implements java.io.Closeable {
method public void bindBlob(int, @NonNull byte[]) throws android.database.sqlite.SQLiteException;
method public void bindBlob(int, @NonNull byte[], int, int) throws android.database.sqlite.SQLiteException;
method public void bindDouble(int, double) throws android.database.sqlite.SQLiteException;
@@ -15667,7 +15671,7 @@
public final class Gainmap implements android.os.Parcelable {
ctor public Gainmap(@NonNull android.graphics.Bitmap);
- ctor public Gainmap(@NonNull android.graphics.Gainmap, @NonNull android.graphics.Bitmap);
+ ctor @FlaggedApi("com.android.graphics.hwui.flags.gainmap_constructor_with_metadata") public Gainmap(@NonNull android.graphics.Gainmap, @NonNull android.graphics.Bitmap);
method public int describeContents();
method @NonNull public float getDisplayRatioForFullHdr();
method @NonNull public float[] getEpsilonHdr();
@@ -16086,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();
@@ -16303,7 +16309,7 @@
method public void arcTo(float, float, float, float, float, float, boolean);
method public void close();
method @Deprecated public void computeBounds(@NonNull android.graphics.RectF, boolean);
- method public void computeBounds(@NonNull android.graphics.RectF);
+ method @FlaggedApi("com.android.graphics.flags.exact_compute_bounds") public void computeBounds(@NonNull android.graphics.RectF);
method public void conicTo(float, float, float, float, float);
method public void cubicTo(float, float, float, float, float, float);
method @NonNull public android.graphics.Path.FillType getFillType();
@@ -17666,6 +17672,7 @@
field public static final int LINE_BREAK_STYLE_LOOSE = 1; // 0x1
field public static final int LINE_BREAK_STYLE_NONE = 0; // 0x0
field public static final int LINE_BREAK_STYLE_NORMAL = 2; // 0x2
+ field @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static final int LINE_BREAK_STYLE_NO_BREAK = 4; // 0x4
field public static final int LINE_BREAK_STYLE_STRICT = 3; // 0x3
field @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static final int LINE_BREAK_STYLE_UNSPECIFIED = -1; // 0xffffffff
field public static final int LINE_BREAK_WORD_STYLE_NONE = 0; // 0x0
@@ -18539,8 +18546,10 @@
ctor public BiometricPrompt.CryptoObject(@NonNull javax.crypto.Mac);
ctor @Deprecated public BiometricPrompt.CryptoObject(@NonNull android.security.identity.IdentityCredential);
ctor public BiometricPrompt.CryptoObject(@NonNull android.security.identity.PresentationSession);
+ ctor @FlaggedApi("android.hardware.biometrics.add_key_agreement_crypto_object") public BiometricPrompt.CryptoObject(@NonNull javax.crypto.KeyAgreement);
method public javax.crypto.Cipher getCipher();
method @Deprecated @Nullable public android.security.identity.IdentityCredential getIdentityCredential();
+ method @FlaggedApi("android.hardware.biometrics.add_key_agreement_crypto_object") @Nullable public javax.crypto.KeyAgreement getKeyAgreement();
method public javax.crypto.Mac getMac();
method @Nullable public android.security.identity.PresentationSession getPresentationSession();
method public java.security.Signature getSignature();
@@ -28560,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";
@@ -31969,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
@@ -41552,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();
@@ -44211,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
@@ -44320,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();
@@ -44336,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
@@ -44551,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);
@@ -45337,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";
@@ -46670,10 +46682,10 @@
method @NonNull public android.text.DynamicLayout.Builder setHyphenationFrequency(int);
method @NonNull public android.text.DynamicLayout.Builder setIncludePad(boolean);
method @NonNull public android.text.DynamicLayout.Builder setJustificationMode(int);
- method @NonNull public android.text.DynamicLayout.Builder setLineBreakConfig(@NonNull android.graphics.text.LineBreakConfig);
+ method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") @NonNull public android.text.DynamicLayout.Builder setLineBreakConfig(@NonNull android.graphics.text.LineBreakConfig);
method @NonNull public android.text.DynamicLayout.Builder setLineSpacing(float, @FloatRange(from=0.0) float);
method @NonNull public android.text.DynamicLayout.Builder setTextDirection(@NonNull android.text.TextDirectionHeuristic);
- method @NonNull public android.text.DynamicLayout.Builder setUseBoundsForWidth(boolean);
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.text.DynamicLayout.Builder setUseBoundsForWidth(boolean);
method @NonNull public android.text.DynamicLayout.Builder setUseLineSpacingFromFallbacks(boolean);
}
@@ -47197,7 +47209,7 @@
method @NonNull public android.text.StaticLayout.Builder setMaxLines(@IntRange(from=0) int);
method public android.text.StaticLayout.Builder setText(CharSequence);
method @NonNull public android.text.StaticLayout.Builder setTextDirection(@NonNull android.text.TextDirectionHeuristic);
- method @NonNull public android.text.StaticLayout.Builder setUseBoundsForWidth(boolean);
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.text.StaticLayout.Builder setUseBoundsForWidth(boolean);
method @NonNull public android.text.StaticLayout.Builder setUseLineSpacingFromFallbacks(boolean);
}
@@ -47910,6 +47922,10 @@
method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") @NonNull public android.graphics.text.LineBreakConfig getLineBreakConfig();
}
+ @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static final class LineBreakConfigSpan.NoBreakSpan extends android.text.style.LineBreakConfigSpan {
+ ctor @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public LineBreakConfigSpan.NoBreakSpan();
+ }
+
@FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static final class LineBreakConfigSpan.NoHyphenationSpan extends android.text.style.LineBreakConfigSpan {
ctor @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public LineBreakConfigSpan.NoHyphenationSpan();
}
@@ -53113,6 +53129,7 @@
method @Nullable public abstract android.view.View getCurrentFocus();
method @NonNull public abstract android.view.View getDecorView();
method public static int getDefaultFeatures(android.content.Context);
+ method @FlaggedApi("com.android.graphics.hwui.flags.limited_hdr") public float getDesiredHdrHeadroom();
method public android.transition.Transition getEnterTransition();
method public android.transition.Transition getExitTransition();
method protected final int getFeatures();
@@ -53182,6 +53199,7 @@
method public abstract void setDecorCaptionShade(int);
method public void setDecorFitsSystemWindows(boolean);
method protected void setDefaultWindowFormat(int);
+ method @FlaggedApi("com.android.graphics.hwui.flags.limited_hdr") public void setDesiredHdrHeadroom(@FloatRange(from=0.0f, to=10000.0) float);
method public void setDimAmount(float);
method public void setElevation(float);
method public void setEnterTransition(android.transition.Transition);
@@ -53531,6 +53549,7 @@
method public int describeContents();
method public int getBlurBehindRadius();
method public int getColorMode();
+ method @FlaggedApi("com.android.graphics.hwui.flags.limited_hdr") public float getDesiredHdrHeadroom();
method public int getFitInsetsSides();
method public int getFitInsetsTypes();
method public final CharSequence getTitle();
@@ -53540,6 +53559,7 @@
method public void setBlurBehindRadius(@IntRange(from=0) int);
method public void setCanPlayMoveAnimation(boolean);
method public void setColorMode(int);
+ method @FlaggedApi("com.android.graphics.hwui.flags.limited_hdr") public void setDesiredHdrHeadroom(@FloatRange(from=0.0f, to=10000.0f) float);
method public void setFitInsetsIgnoringVisibility(boolean);
method public void setFitInsetsSides(int);
method public void setFitInsetsTypes(int);
@@ -54650,7 +54670,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();
@@ -54677,7 +54696,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);
@@ -54698,10 +54716,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);
@@ -55153,12 +55167,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 358c8e7..a1da9e0 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,8 @@
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_SANDBOXED_DETECTION_TRAINING_DATA = "android.permission.RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA";
+ 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 +656,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 +3212,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);
@@ -3946,7 +3949,7 @@
field public static final int DELETE_FAILED_OWNER_BLOCKED = -4; // 0xfffffffc
field public static final int DELETE_KEEP_DATA = 1; // 0x1
field public static final int DELETE_SUCCEEDED = 1; // 0x1
- field public static final String EXTRA_REQUEST_PERMISSIONS_DEVICE_ID = "android.content.pm.extra.REQUEST_PERMISSIONS_DEVICE_ID";
+ field @FlaggedApi("android.permission.flags.device_aware_permission_apis") public static final String EXTRA_REQUEST_PERMISSIONS_DEVICE_ID = "android.content.pm.extra.REQUEST_PERMISSIONS_DEVICE_ID";
field public static final String EXTRA_REQUEST_PERMISSIONS_LEGACY_ACCESS_PERMISSION_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_LEGACY_ACCESS_PERMISSION_NAMES";
field public static final String EXTRA_REQUEST_PERMISSIONS_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES";
field public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS";
@@ -4529,7 +4532,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 +9608,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 +9708,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 +9775,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 +13231,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 +13712,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 +16682,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 db751a4..eeddeb2 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();
@@ -1181,6 +1181,7 @@
method @Nullable public CharSequence getLabel(@NonNull android.content.Context);
method @Nullable public android.graphics.drawable.Drawable getServiceIcon(@NonNull android.content.Context);
method @NonNull public android.content.pm.ServiceInfo getServiceInfo();
+ method @FlaggedApi("android.credentials.flags.settings_activity_enabled") @Nullable public CharSequence getSettingsActivity();
method @Nullable public CharSequence getSettingsSubtitle();
method @NonNull public boolean hasCapability(@NonNull String);
method public boolean isEnabled();
@@ -1903,7 +1904,7 @@
method public android.media.PlaybackParams setAudioStretchMode(int);
}
- public final class RingtoneSelection {
+ @FlaggedApi("android.os.vibrator.haptics_customization_enabled") public final class RingtoneSelection {
method @NonNull public static android.media.RingtoneSelection fromUri(@Nullable android.net.Uri, int);
method public int getSoundSource();
method @Nullable public android.net.Uri getSoundUri();
@@ -1915,14 +1916,16 @@
field public static final int FROM_URI_RINGTONE_SELECTION_ONLY = 3; // 0x3
field public static final int FROM_URI_RINGTONE_SELECTION_OR_SOUND = 1; // 0x1
field public static final int FROM_URI_RINGTONE_SELECTION_OR_VIBRATION = 2; // 0x2
- field public static final int SOUND_SOURCE_DEFAULT = 0; // 0x0
field public static final int SOUND_SOURCE_OFF = 1; // 0x1
+ field public static final int SOUND_SOURCE_SYSTEM_DEFAULT = 3; // 0x3
+ field public static final int SOUND_SOURCE_UNSPECIFIED = 0; // 0x0
field public static final int SOUND_SOURCE_URI = 2; // 0x2
- field public static final int VIBRATION_SOURCE_APPLICATION_PROVIDED = 3; // 0x3
+ field public static final int VIBRATION_SOURCE_APPLICATION_DEFAULT = 4; // 0x4
field public static final int VIBRATION_SOURCE_AUDIO_CHANNEL = 10; // 0xa
- field public static final int VIBRATION_SOURCE_DEFAULT = 0; // 0x0
field public static final int VIBRATION_SOURCE_HAPTIC_GENERATOR = 11; // 0xb
field public static final int VIBRATION_SOURCE_OFF = 1; // 0x1
+ field public static final int VIBRATION_SOURCE_SYSTEM_DEFAULT = 3; // 0x3
+ field public static final int VIBRATION_SOURCE_UNSPECIFIED = 0; // 0x0
field public static final int VIBRATION_SOURCE_URI = 2; // 0x2
}
@@ -2610,17 +2613,17 @@
package android.os.vibrator.persistence {
- public class ParsedVibration {
+ @FlaggedApi("android.os.vibrator.enable_vibration_serialization_apis") public class ParsedVibration {
method @NonNull public java.util.List<android.os.VibrationEffect> getVibrationEffects();
method @Nullable public android.os.VibrationEffect resolve(@NonNull android.os.Vibrator);
}
- public final class VibrationXmlParser {
+ @FlaggedApi("android.os.vibrator.enable_vibration_serialization_apis") public final class VibrationXmlParser {
method @Nullable public static android.os.vibrator.persistence.ParsedVibration parseDocument(@NonNull java.io.Reader) throws java.io.IOException;
method @Nullable public static android.os.VibrationEffect parseVibrationEffect(@NonNull java.io.Reader) throws java.io.IOException;
}
- public final class VibrationXmlSerializer {
+ @FlaggedApi("android.os.vibrator.enable_vibration_serialization_apis") public final class VibrationXmlSerializer {
method public static void serialize(@NonNull android.os.VibrationEffect, @NonNull java.io.Writer) throws java.io.IOException, android.os.vibrator.persistence.VibrationXmlSerializer.SerializationFailedException;
}
@@ -2955,10 +2958,6 @@
method @Deprecated public boolean isBound();
}
- public class NotificationRankingUpdate implements android.os.Parcelable {
- method public final boolean isFdNotNullAndClosed();
- }
-
}
package android.service.quickaccesswallet {
@@ -3188,7 +3187,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;
@@ -3281,12 +3280,12 @@
}
public class MeasuredParagraph {
- method @NonNull public static android.text.MeasuredParagraph buildForStaticLayoutTest(@NonNull android.text.TextPaint, @Nullable android.graphics.text.LineBreakConfig, @NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.text.TextDirectionHeuristic, int, boolean, @Nullable android.text.MeasuredParagraph.StyleRunCallback);
+ method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") @NonNull public static android.text.MeasuredParagraph buildForStaticLayoutTest(@NonNull android.text.TextPaint, @Nullable android.graphics.text.LineBreakConfig, @NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.text.TextDirectionHeuristic, int, boolean, @Nullable android.text.MeasuredParagraph.StyleRunCallback);
}
- public static interface MeasuredParagraph.StyleRunCallback {
- method public void onAppendReplacementRun(@NonNull android.graphics.Paint, @IntRange(from=0) int, @FloatRange(from=0) @Px float);
- method public void onAppendStyleRun(@NonNull android.graphics.Paint, @Nullable android.graphics.text.LineBreakConfig, @IntRange(from=0) int, boolean);
+ @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static interface MeasuredParagraph.StyleRunCallback {
+ method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public void onAppendReplacementRun(@NonNull android.graphics.Paint, @IntRange(from=0) int, @FloatRange(from=0) @Px float);
+ method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public void onAppendStyleRun(@NonNull android.graphics.Paint, @Nullable android.graphics.text.LineBreakConfig, @IntRange(from=0) int, boolean);
}
public static final class Selection.MemoryTextWatcher implements android.text.TextWatcher {
@@ -3408,7 +3407,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);
@@ -3569,8 +3568,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);
@@ -3627,7 +3626,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-lint-baseline.txt b/core/api/test-lint-baseline.txt
index 107be8b..93e39d5 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -191,8 +191,14 @@
New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_DEFAULT
UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_OFF:
New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_OFF
+UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_SYSTEM_DEFAULT:
+ New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_SYSTEM_DEFAULT
+UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_UNSPECIFIED:
+ New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_UNSPECIFIED
UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_URI:
New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_URI
+UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_APPLICATION_DEFAULT:
+ New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_APPLICATION_DEFAULT
UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_APPLICATION_PROVIDED:
New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_APPLICATION_PROVIDED
UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_AUDIO_CHANNEL:
@@ -203,6 +209,10 @@
New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_HAPTIC_GENERATOR
UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_OFF:
New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_OFF
+UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_SYSTEM_DEFAULT:
+ New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_SYSTEM_DEFAULT
+UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_UNSPECIFIED:
+ New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_UNSPECIFIED
UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_URI:
New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_URI
UnflaggedApi: android.media.RingtoneSelection#fromUri(android.net.Uri, int):
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 13a1bd6..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: [
@@ -431,6 +426,16 @@
},
}
+aidl_interface {
+ name: "android.companion.virtual.virtualdevice_aidl",
+ unstable: true,
+ host_supported: true,
+ srcs: [
+ "android/companion/virtualnative/IVirtualDeviceManagerNative.aidl",
+ ],
+ local_include_dir: ".",
+}
+
filegroup {
name: "frameworks-base-java-overview",
srcs: ["overview.html"],
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 3bf2cca..f68681b 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4288,7 +4288,7 @@
}
/**
- * Start monitoring changes to the imoportance of uids running in the system.
+ * Start monitoring changes to the importance of uids running in the system.
* @param listener The listener callback that will receive change reports.
* @param importanceCutpoint The level of importance in which the caller is interested
* in differences. For example, if {@link RunningAppProcessInfo#IMPORTANCE_PERCEPTIBLE}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 9a90df9..e12181a 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -101,6 +101,7 @@
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.content.res.ResourcesImpl;
import android.content.res.loader.ResourcesLoader;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDebug;
@@ -297,6 +298,7 @@
public static final boolean DEBUG_MEMORY_TRIM = false;
private static final boolean DEBUG_PROVIDER = false;
public static final boolean DEBUG_ORDER = false;
+ private static final boolean DEBUG_APP_INFO = true;
private static final long MIN_TIME_BETWEEN_GCS = 5*1000;
/**
* The delay to release the provider when it has no more references. It reduces the number of
@@ -6473,10 +6475,35 @@
resApk.updateApplicationInfo(ai, oldPaths);
}
+ ResourcesImpl beforeImpl = getApplication().getResources().getImpl();
+
synchronized (mResourcesManager) {
// Update all affected Resources objects to use new ResourcesImpl
mResourcesManager.applyAllPendingAppInfoUpdates();
}
+
+ ResourcesImpl afterImpl = getApplication().getResources().getImpl();
+
+ if ((beforeImpl != afterImpl) && !Arrays.equals(beforeImpl.getAssets().getApkAssets(),
+ afterImpl.getAssets().getApkAssets())) {
+ List<String> beforeAssets = Arrays.asList(beforeImpl.getAssets().getApkPaths());
+ List<String> afterAssets = Arrays.asList(afterImpl.getAssets().getApkPaths());
+
+ List<String> onlyBefore = new ArrayList<>(beforeAssets);
+ onlyBefore.removeAll(afterAssets);
+ List<String> onlyAfter = new ArrayList<>(afterAssets);
+ onlyAfter.removeAll(beforeAssets);
+
+ Slog.i(TAG, "ApplicationInfo updating for " + ai.packageName + ", new timestamp: "
+ + ai.createTimestamp + "\nassets removed: " + onlyBefore + "\nassets added: "
+ + onlyAfter);
+
+ if (DEBUG_APP_INFO) {
+ Slog.v(TAG, "ApplicationInfo updating for " + ai.packageName
+ + ", assets before change: " + beforeAssets + "\n assets after change: "
+ + afterAssets);
+ }
+ }
}
/**
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index ca10d14..ecbc9b1 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1478,7 +1478,8 @@
AppProtoEnums.APP_OP_RECORD_AUDIO_SANDBOXED;
/**
- * Allows the assistant app to receive the PCC-validated hotword and be voice-triggered.
+ * Allows the assistant app to be voice-triggered by detected hotwords from a trusted detection
+ * service.
*
* @hide
*/
@@ -1486,13 +1487,13 @@
AppProtoEnums.APP_OP_RECEIVE_SANDBOX_TRIGGER_AUDIO;
/**
- * Allows the assistant app to get the training data from the PCC sandbox to improve the
- * hotword training model.
+ * Allows the privileged assistant app to receive the training data from the sandboxed hotword
+ * detection service.
*
* @hide
*/
- public static final int OP_RECEIVE_SANDBOX_TRAINING_DATA =
- AppProtoEnums.APP_OP_RECEIVE_SANDBOX_TRAINING_DATA;
+ public static final int OP_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA =
+ AppProtoEnums.APP_OP_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA;
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -1640,7 +1641,7 @@
OPSTR_CAMERA_SANDBOXED,
OPSTR_RECORD_AUDIO_SANDBOXED,
OPSTR_RECEIVE_SANDBOX_TRIGGER_AUDIO,
- OPSTR_RECEIVE_SANDBOX_TRAINING_DATA
+ OPSTR_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA
})
public @interface AppOpString {}
@@ -2252,22 +2253,22 @@
public static final String OPSTR_USE_FULL_SCREEN_INTENT = "android:use_full_screen_intent";
/**
- * Allows the assistant app to receive the PCC-validated hotword and be voice-triggered.
+ * Allows the assistant app to be voice-triggered by detected hotwords from a trusted detection
+ * service.
*
* @hide
*/
- @SystemApi
public static final String OPSTR_RECEIVE_SANDBOX_TRIGGER_AUDIO =
"android:receive_sandbox_trigger_audio";
/**
- * Allows the assistant app to get the training data from the PCC sandbox to improve
- * the hotword training model.
+ * Allows the privileged assistant app to receive training data from the sandboxed hotword
+ * detection service.
*
* @hide
*/
- public static final String OPSTR_RECEIVE_SANDBOX_TRAINING_DATA =
- "android:receive_sandbox_training_data";
+ public static final String OPSTR_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA =
+ "android:RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA";
/** {@link #sAppOpsToNote} not initialized yet for this op */
private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
@@ -2379,7 +2380,9 @@
OP_RUN_USER_INITIATED_JOBS,
OP_FOREGROUND_SERVICE_SPECIAL_USE,
OP_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD,
- OP_USE_FULL_SCREEN_INTENT
+ OP_USE_FULL_SCREEN_INTENT,
+ OP_RECEIVE_SANDBOX_TRIGGER_AUDIO,
+ OP_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA
};
static final AppOpInfo[] sAppOpInfos = new AppOpInfo[]{
@@ -2810,10 +2813,13 @@
new AppOpInfo.Builder(OP_RECEIVE_SANDBOX_TRIGGER_AUDIO,
OPSTR_RECEIVE_SANDBOX_TRIGGER_AUDIO,
"RECEIVE_SANDBOX_TRIGGER_AUDIO")
- .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
- new AppOpInfo.Builder(OP_RECEIVE_SANDBOX_TRAINING_DATA,
- OPSTR_RECEIVE_SANDBOX_TRAINING_DATA,
- "RECEIVE_SANDBOX_TRAINING_DATA").build()
+ .setPermission(Manifest.permission.RECEIVE_SANDBOX_TRIGGER_AUDIO)
+ .setDefaultMode(AppOpsManager.MODE_DEFAULT).build(),
+ new AppOpInfo.Builder(OP_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA,
+ OPSTR_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA,
+ "RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA")
+ .setPermission(Manifest.permission.RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA)
+ .setDefaultMode(AppOpsManager.MODE_DEFAULT).build()
};
// The number of longs needed to form a full bitmask of app ops
diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java
index 7b3d017..d15c79f 100644
--- a/core/java/android/app/ApplicationExitInfo.java
+++ b/core/java/android/app/ApplicationExitInfo.java
@@ -468,6 +468,15 @@
*/
public static final int SUBREASON_EXCESSIVE_BINDER_OBJECTS = 29;
+ /**
+ * The process was killed by the [kernel] Out-of-memory (OOM) killer; this
+ * would be set only when the reason is {@link #REASON_LOW_MEMORY}.
+ *
+ * For internal use only.
+ * @hide
+ */
+ public static final int SUBREASON_OOM_KILL = 30;
+
// If there is any OEM code which involves additional app kill reasons, it should
// be categorized in {@link #REASON_OTHER}, with subreason code starting from 1000.
@@ -644,6 +653,7 @@
SUBREASON_PACKAGE_UPDATE,
SUBREASON_UNDELIVERED_BROADCAST,
SUBREASON_EXCESSIVE_BINDER_OBJECTS,
+ SUBREASON_OOM_KILL,
})
@Retention(RetentionPolicy.SOURCE)
public @interface SubReason {}
@@ -1371,6 +1381,8 @@
return "UNDELIVERED BROADCAST";
case SUBREASON_EXCESSIVE_BINDER_OBJECTS:
return "EXCESSIVE BINDER OBJECTS";
+ case SUBREASON_OOM_KILL:
+ return "OOM KILL";
default:
return "UNKNOWN";
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index e5a73be..21ed098 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2954,6 +2954,17 @@
}
}
+ @Override
+ public boolean isPackageStopped(@NonNull String packageName) throws NameNotFoundException {
+ try {
+ return mPM.isPackageStoppedForUser(packageName, getUserId());
+ } catch (IllegalArgumentException ie) {
+ throw new NameNotFoundException(packageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/** @hide */
@Override
public void setApplicationCategoryHint(String packageName, int categoryHint) {
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/app/servertransaction/WindowStateResizeItem.java b/core/java/android/app/servertransaction/WindowStateResizeItem.java
new file mode 100644
index 0000000..9828133
--- /dev/null
+++ b/core/java/android/app/servertransaction/WindowStateResizeItem.java
@@ -0,0 +1,193 @@
+/*
+ * 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.app.servertransaction;
+
+import static android.view.Display.INVALID_DISPLAY;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ClientTransactionHandler;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.util.MergedConfiguration;
+import android.view.IWindow;
+import android.view.InsetsState;
+import android.window.ClientWindowFrames;
+
+import java.util.Objects;
+
+/**
+ * Message to deliver window resize info.
+ * @hide
+ */
+public class WindowStateResizeItem extends ClientTransactionItem {
+
+ private IWindow mWindow;
+ private ClientWindowFrames mFrames;
+ private boolean mReportDraw;
+ private MergedConfiguration mConfiguration;
+ private InsetsState mInsetsState;
+ private boolean mForceLayout;
+ private boolean mAlwaysConsumeSystemBars;
+ private int mDisplayId;
+ private int mSyncSeqId;
+ private boolean mDragResizing;
+
+ @Override
+ public void execute(@NonNull ClientTransactionHandler client,
+ @NonNull PendingTransactionActions pendingActions) {
+ try {
+ mWindow.resized(mFrames, mReportDraw, mConfiguration, mInsetsState, mForceLayout,
+ mAlwaysConsumeSystemBars, mDisplayId, mSyncSeqId, mDragResizing);
+ } catch (RemoteException e) {
+ // Should be a local call.
+ throw new RuntimeException(e);
+ }
+ }
+
+ // ObjectPoolItem implementation
+
+ private WindowStateResizeItem() {}
+
+ /** Obtains an instance initialized with provided params. */
+ public static WindowStateResizeItem obtain(@NonNull IWindow window,
+ @NonNull ClientWindowFrames frames, boolean reportDraw,
+ @NonNull MergedConfiguration configuration, @NonNull InsetsState insetsState,
+ boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId, int syncSeqId,
+ boolean dragResizing) {
+ WindowStateResizeItem instance =
+ ObjectPool.obtain(WindowStateResizeItem.class);
+ if (instance == null) {
+ instance = new WindowStateResizeItem();
+ }
+ instance.mWindow = requireNonNull(window);
+ instance.mFrames = requireNonNull(frames);
+ instance.mReportDraw = reportDraw;
+ instance.mConfiguration = requireNonNull(configuration);
+ instance.mInsetsState = requireNonNull(insetsState);
+ instance.mForceLayout = forceLayout;
+ instance.mAlwaysConsumeSystemBars = alwaysConsumeSystemBars;
+ instance.mDisplayId = displayId;
+ instance.mSyncSeqId = syncSeqId;
+ instance.mDragResizing = dragResizing;
+
+ return instance;
+ }
+
+ @Override
+ public void recycle() {
+ mWindow = null;
+ mFrames = null;
+ mReportDraw = false;
+ mConfiguration = null;
+ mInsetsState = null;
+ mForceLayout = false;
+ mAlwaysConsumeSystemBars = false;
+ mDisplayId = INVALID_DISPLAY;
+ mSyncSeqId = -1;
+ mDragResizing = false;
+ ObjectPool.recycle(this);
+ }
+
+ // Parcelable implementation
+
+ /** Writes to Parcel. */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeStrongBinder(mWindow.asBinder());
+ dest.writeTypedObject(mFrames, flags);
+ dest.writeBoolean(mReportDraw);
+ dest.writeTypedObject(mConfiguration, flags);
+ dest.writeTypedObject(mInsetsState, flags);
+ dest.writeBoolean(mForceLayout);
+ dest.writeBoolean(mAlwaysConsumeSystemBars);
+ dest.writeInt(mDisplayId);
+ dest.writeInt(mSyncSeqId);
+ dest.writeBoolean(mDragResizing);
+ }
+
+ /** Reads from Parcel. */
+ private WindowStateResizeItem(@NonNull Parcel in) {
+ mWindow = IWindow.Stub.asInterface(in.readStrongBinder());
+ mFrames = in.readTypedObject(ClientWindowFrames.CREATOR);
+ mReportDraw = in.readBoolean();
+ mConfiguration = in.readTypedObject(MergedConfiguration.CREATOR);
+ mInsetsState = in.readTypedObject(InsetsState.CREATOR);
+ mForceLayout = in.readBoolean();
+ mAlwaysConsumeSystemBars = in.readBoolean();
+ mDisplayId = in.readInt();
+ mSyncSeqId = in.readInt();
+ mDragResizing = in.readBoolean();
+ }
+
+ public static final @NonNull Creator<WindowStateResizeItem> CREATOR = new Creator<>() {
+ public WindowStateResizeItem createFromParcel(@NonNull Parcel in) {
+ return new WindowStateResizeItem(in);
+ }
+
+ public WindowStateResizeItem[] newArray(int size) {
+ return new WindowStateResizeItem[size];
+ }
+ };
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final WindowStateResizeItem other = (WindowStateResizeItem) o;
+ return Objects.equals(mWindow, other.mWindow)
+ && Objects.equals(mFrames, other.mFrames)
+ && mReportDraw == other.mReportDraw
+ && Objects.equals(mConfiguration, other.mConfiguration)
+ && Objects.equals(mInsetsState, other.mInsetsState)
+ && mForceLayout == other.mForceLayout
+ && mAlwaysConsumeSystemBars == other.mAlwaysConsumeSystemBars
+ && mDisplayId == other.mDisplayId
+ && mSyncSeqId == other.mSyncSeqId
+ && mDragResizing == other.mDragResizing;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + Objects.hashCode(mWindow);
+ result = 31 * result + Objects.hashCode(mFrames);
+ result = 31 * result + (mReportDraw ? 1 : 0);
+ result = 31 * result + Objects.hashCode(mConfiguration);
+ result = 31 * result + Objects.hashCode(mInsetsState);
+ result = 31 * result + (mForceLayout ? 1 : 0);
+ result = 31 * result + (mAlwaysConsumeSystemBars ? 1 : 0);
+ result = 31 * result + mDisplayId;
+ result = 31 * result + mSyncSeqId;
+ result = 31 * result + (mDragResizing ? 1 : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "WindowStateResizeItem{window=" + mWindow
+ + ", reportDrawn=" + mReportDraw
+ + ", configuration=" + mConfiguration
+ + "}";
+ }
+}
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/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 2fb428b..a84845a 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -240,6 +240,12 @@
public static final int MESSAGE_REQUEST_PERMISSION_RESTORE = 0x63826983; // ?RES
/**
+ * The length limit of Association tag.
+ * @hide
+ */
+ private static final int ASSOCIATION_TAG_LENGTH_LIMIT = 100;
+
+ /**
* Callback for applications to receive updates about and the outcome of
* {@link AssociationRequest} issued via {@code associate()} call.
*
@@ -1409,7 +1415,7 @@
/**
* Sets the {@link AssociationInfo#getTag() tag} for this association.
*
- * <p>The length of the tag must be at most 20 characters.
+ * <p>The length of the tag must be at most 100 characters to save disk space.
*
* <p>This allows to store useful information about the associated devices.
*
@@ -1421,8 +1427,8 @@
public void setAssociationTag(int associationId, @NonNull String tag) {
Objects.requireNonNull(tag, "tag cannot be null");
- if (tag.length() > 20) {
- throw new IllegalArgumentException("Length of the tag must be at most 20 characters");
+ if (tag.length() > ASSOCIATION_TAG_LENGTH_LIMIT) {
+ throw new IllegalArgumentException("Length of the tag must be at most 100 characters");
}
try {
diff --git a/core/java/android/companion/CompanionDeviceService.java b/core/java/android/companion/CompanionDeviceService.java
index 03e75e9..570ecaa 100644
--- a/core/java/android/companion/CompanionDeviceService.java
+++ b/core/java/android/companion/CompanionDeviceService.java
@@ -161,16 +161,16 @@
public static final int DEVICE_EVENT_BT_DISCONNECTED = 3;
/**
- * A companion app for a {@link AssociationInfo#isSelfManaged() self-managed} device will
- * receive the callback {@link #onDeviceEvent(AssociationInfo, int)} if it reports that a
- * device has appeared on its own.
+ * A companion app for a self-managed device will receive the callback
+ * {@link #onDeviceEvent(AssociationInfo, int)} if it reports that a device has appeared on its
+ * own.
*/
public static final int DEVICE_EVENT_SELF_MANAGED_APPEARED = 4;
/**
- * A companion app for a {@link AssociationInfo#isSelfManaged() self-managed} device will
- * receive the callback {@link #onDeviceEvent(AssociationInfo, int)} if it reports that a
- * device has disappeared on its own.
+ * A companion app for a self-managed device will receive the callback
+ * {@link #onDeviceEvent(AssociationInfo, int)} if it reports that a device has disappeared on
+ * its own.
*/
public static final int DEVICE_EVENT_SELF_MANAGED_DISAPPEARED = 5;
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..93a3e782 100644
--- a/core/java/android/companion/virtual/VirtualDevice.java
+++ b/core/java/android/companion/virtual/VirtualDevice.java
@@ -33,9 +33,6 @@
*
* <p>Read-only device representation exposing the properties of an existing virtual device.
*
- * <p class="note">Not to be confused with {@link VirtualDeviceManager.VirtualDevice}, which is used
- * by the virtual device creator and allows them to manage the device.
- *
* @see VirtualDeviceManager#registerVirtualDeviceListener
*/
public final class VirtualDevice implements Parcelable {
@@ -113,14 +110,13 @@
* <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;
}
/**
* Returns the name of the virtual device (optionally) provided during its creation.
- *
- * @see VirtualDeviceParams.Builder#setName(String)
*/
public @Nullable String getName() {
return mName;
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/companion/virtual/flags.aconfig b/core/java/android/companion/virtual/flags.aconfig
index d0e13cd..cf274f5 100644
--- a/core/java/android/companion/virtual/flags.aconfig
+++ b/core/java/android/companion/virtual/flags.aconfig
@@ -8,6 +8,14 @@
}
flag {
+ name: "enable_native_vdm"
+ namespace: "virtual_devices"
+ description: "Enable native VDM service"
+ bug: "303535376"
+ is_fixed_read_only: true
+}
+
+flag {
name: "dynamic_policy"
namespace: "virtual_devices"
description: "Enable dynamic policy API"
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelWriter.java b/core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelWriter.java
index bf78dd0..b9451a7 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelWriter.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelWriter.java
@@ -46,15 +46,15 @@
* <pre>
* VirtualSensorDirectChannelWriter writer = new VirtualSensorDirectChannelWriter();
* VirtualSensorDirectChannelCallback callback = new VirtualSensorDirectChannelCallback() {
- * @Override
+ * {@literal @}Override
* public void onDirectChannelCreated(int channelHandle, SharedMemory sharedMemory) {
* writer.addChannel(channelHandle, sharedMemory);
* }
- * @Override
+ * {@literal @}Override
* public void onDirectChannelDestroyed(int channelHandle);
* writer.removeChannel(channelHandle);
* }
- * @Override
+ * {@literal @}Override
* public void onDirectChannelConfigured(int channelHandle, VirtualSensor sensor, int rateLevel,
* int reportToken)
* if (!writer.configureChannel(channelHandle, sensor, rateLevel, reportToken)) {
diff --git a/core/java/android/companion/virtualnative/IVirtualDeviceManagerNative.aidl b/core/java/android/companion/virtualnative/IVirtualDeviceManagerNative.aidl
new file mode 100644
index 0000000..9f09d04
--- /dev/null
+++ b/core/java/android/companion/virtualnative/IVirtualDeviceManagerNative.aidl
@@ -0,0 +1,65 @@
+/*
+ * 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.companion.virtualnative;
+
+/**
+ * Parallel implementation of certain VirtualDeviceManager APIs that need to be exposed to native
+ * code.
+ *
+ * <p>These APIs are a parallel definition to the APIs in VirtualDeviceManager and/or
+ * VirtualDeviceManagerInternal, so they can technically diverge. However, it's good practice to
+ * keep these APIs in sync with each other.</p>
+ *
+ * <p>Even though the name implies otherwise, the implementation is actually in Java. The 'native'
+ * suffix comes from the intended usage - native framework backends that need to communicate with
+ * VDM for some reason.</p>
+ *
+ * <p>Because these APIs are exposed to native code that runs in the app process, they may be
+ * accessed by apps directly, even though they're hidden. Care should be taken to avoid exposing
+ * sensitive data or potential security holes.</p>
+ *
+ * @hide
+ */
+interface IVirtualDeviceManagerNative {
+ /**
+ * Counterpart to VirtualDeviceParams#DevicePolicy.
+ */
+ const int DEVICE_POLICY_DEFAULT = 0;
+ const int DEVICE_POLICY_CUSTOM = 1;
+
+ /**
+ * Counterpart to VirtualDeviceParams#PolicyType.
+ */
+ const int POLICY_TYPE_SENSORS = 0;
+ const int POLICY_TYPE_AUDIO = 1;
+ const int POLICY_TYPE_RECENTS = 2;
+ const int POLICY_TYPE_ACTIVITY = 3;
+
+ /**
+ * Returns the IDs for all VirtualDevices where an app with the given is running.
+ *
+ * Note that this returns only VirtualDevice IDs: if the app is not running on any virtual
+ * device, then an an empty array is returned. This does not include information about whether
+ * the app is running on the default device or not.
+ */
+ int[] getDeviceIdsForUid(int uid);
+
+ /**
+ * Returns the device policy for the given virtual device and policy type.
+ */
+ int getDevicePolicy(int deviceId, int policyType);
+}
\ No newline at end of file
diff --git a/core/java/android/companion/virtualnative/OWNERS b/core/java/android/companion/virtualnative/OWNERS
new file mode 100644
index 0000000..2968104
--- /dev/null
+++ b/core/java/android/companion/virtualnative/OWNERS
@@ -0,0 +1 @@
+include /services/companion/java/com/android/server/companion/virtual/OWNERS
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/Intent.java b/core/java/android/content/Intent.java
index 44a9acd..5f4c05f 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2790,6 +2790,20 @@
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_PACKAGE_CHANGED = "android.intent.action.PACKAGE_CHANGED";
+
+ /**
+ * Broadcast Action: An application package that was previously in the stopped state has been
+ * started and is no longer considered stopped.
+ * <ul>
+ * <li> {@link #EXTRA_UID} containing the integer uid assigned to the package.
+ * </ul>
+ *
+ * <p class="note">This is a protected intent that can only be sent by the system.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @FlaggedApi(android.content.pm.Flags.FLAG_STAY_STOPPED)
+ public static final String ACTION_PACKAGE_UNSTOPPED = "android.intent.action.PACKAGE_UNSTOPPED";
+
/**
* Broadcast Action: Sent to the system rollback manager when a package
* needs to have rollback enabled.
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/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index aca88d6..9926415 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -308,6 +308,8 @@
boolean isPackageQuarantinedForUser(String packageName, int userId);
+ boolean isPackageStoppedForUser(String packageName, int userId);
+
Bundle getSuspendedPackageAppExtras(String packageName, int userId);
/**
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 3a9e9bf..d837aae 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -2730,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/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 3fc515d..4d5d056 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4605,6 +4605,16 @@
public static final String FEATURE_WALLET_LOCATION_BASED_SUGGESTIONS =
"android.software.wallet_location_based_suggestions";
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has
+ * the rotary encoder hardware to support rotating bezel on watch.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_ROTARY_ENCODER_LOW_RES =
+ "android.hardware.rotaryencoder.lowres";
+
/** @hide */
public static final boolean APP_ENUMERATION_ENABLED_BY_DEFAULT = true;
@@ -4802,6 +4812,7 @@
* @hide
*/
@SystemApi
+ @FlaggedApi(android.permission.flags.Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS)
public static final String EXTRA_REQUEST_PERMISSIONS_DEVICE_ID =
"android.content.pm.extra.REQUEST_PERMISSIONS_DEVICE_ID";
@@ -9877,6 +9888,18 @@
}
/**
+ * Query if an app is currently stopped.
+ *
+ * @return {@code true} if the given package is stopped, {@code false} otherwise
+ * @throws NameNotFoundException if the package could not be found.
+ * @see ApplicationInfo#FLAG_STOPPED
+ */
+ @FlaggedApi(android.content.pm.Flags.FLAG_STAY_STOPPED)
+ public boolean isPackageStopped(@NonNull String packageName) throws NameNotFoundException {
+ throw new UnsupportedOperationException("isPackageStopped not implemented");
+ }
+
+ /**
* Query if an app is currently quarantined.
*
* @return {@code true} if the given package is quarantined, {@code false} otherwise
@@ -9887,7 +9910,6 @@
public boolean isPackageQuarantined(@NonNull String packageName) throws NameNotFoundException {
throw new UnsupportedOperationException("isPackageQuarantined not implemented");
}
-
/**
* Provide a hint of what the {@link ApplicationInfo#category} value should
* be for the given package.
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/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index ea0f049..3ec239c 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -8,8 +8,23 @@
}
flag {
+ name: "save_global_and_guest_restrictions_on_system_user_xml_read_only"
+ namespace: "multiuser"
+ description: "Save guest and device policy global restrictions on the SYSTEM user's XML file. (Read only flag)"
+ bug: "301067944"
+ is_fixed_read_only: true
+}
+
+flag {
name: "bind_wallpaper_service_on_its_own_thread_during_a_user_switch"
namespace: "multiuser"
description: "Bind wallpaper service on its own thread instead of system_server's main handler during a user switch."
bug: "302100344"
}
+
+flag {
+ name: "support_communal_profile"
+ namespace: "multiuser"
+ description: "Framework support for communal profile."
+ bug: "285426179"
+}
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/CreateCredentialException.java b/core/java/android/credentials/CreateCredentialException.java
index c344004..8f07d19 100644
--- a/core/java/android/credentials/CreateCredentialException.java
+++ b/core/java/android/credentials/CreateCredentialException.java
@@ -18,7 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.Activity;
+import android.content.Context;
import android.os.CancellationSignal;
import android.os.OutcomeReceiver;
@@ -28,8 +28,8 @@
/**
* Represents an error encountered during the
- * {@link CredentialManager#createCredential(CreateCredentialRequest,
- * Activity, CancellationSignal, Executor, OutcomeReceiver)} operation.
+ * {@link CredentialManager#createCredential(Context, CreateCredentialRequest,
+ * CancellationSignal, Executor, OutcomeReceiver)} operation.
*/
public class CreateCredentialException extends Exception {
/**
@@ -41,7 +41,7 @@
/**
* The error type value for when no create options are available from any provider(s),
- * for the given {@link CredentialManager#createCredential(CreateCredentialRequest, Activity,
+ * for the given {@link CredentialManager#createCredential(Context, CreateCredentialRequest,
* CancellationSignal, Executor, OutcomeReceiver)} request.
*/
@NonNull
diff --git a/core/java/android/credentials/CredentialDescription.java b/core/java/android/credentials/CredentialDescription.java
index db71624..755e659 100644
--- a/core/java/android/credentials/CredentialDescription.java
+++ b/core/java/android/credentials/CredentialDescription.java
@@ -155,8 +155,7 @@
}
/**
- * {@link CredentialDescription#mType} and
- * {@link CredentialDescription#mSupportedElementKeys} are enough for hashing. Constructor
+ * {@link #getType()} and {@link #getSupportedElementKeys()} are enough for hashing. Constructor
* enforces {@link CredentialEntry} to have the same type and
* {@link android.app.slice.Slice} contained by the entry can not be hashed.
*/
@@ -166,8 +165,7 @@
}
/**
- * {@link CredentialDescription#mType} and
- * {@link CredentialDescription#mSupportedElementKeys} are enough for equality check.
+ * {@link #getType()} and {@link #getSupportedElementKeys()} are enough for equality check.
*/
@Override
public boolean equals(Object obj) {
diff --git a/core/java/android/credentials/CredentialProviderInfo.java b/core/java/android/credentials/CredentialProviderInfo.java
index 95ca011..8503072 100644
--- a/core/java/android/credentials/CredentialProviderInfo.java
+++ b/core/java/android/credentials/CredentialProviderInfo.java
@@ -16,12 +16,14 @@
package android.credentials;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ServiceInfo;
+import android.credentials.flags.Flags;
import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -42,6 +44,7 @@
@NonNull private final List<String> mCapabilities = new ArrayList<>();
@Nullable private final CharSequence mOverrideLabel;
@Nullable private CharSequence mSettingsSubtitle = null;
+ @Nullable private CharSequence mSettingsActivity = null;
private final boolean mIsSystemProvider;
private final boolean mIsEnabled;
private final boolean mIsPrimary;
@@ -59,6 +62,7 @@
mIsEnabled = builder.mIsEnabled;
mIsPrimary = builder.mIsPrimary;
mOverrideLabel = builder.mOverrideLabel;
+ mSettingsActivity = builder.mSettingsActivity;
}
/** Returns true if the service supports the given {@code credentialType}, false otherwise. */
@@ -104,10 +108,7 @@
return mIsEnabled;
}
- /**
- * Returns whether the provider is set as primary by the user.
- *
- */
+ /** Returns whether the provider is set as primary by the user. */
public boolean isPrimary() {
return mIsPrimary;
}
@@ -118,6 +119,18 @@
return mSettingsSubtitle;
}
+ /**
+ * Returns the settings activity.
+ *
+ * @hide
+ */
+ @Nullable
+ @TestApi
+ @FlaggedApi(Flags.FLAG_SETTINGS_ACTIVITY_ENABLED)
+ public CharSequence getSettingsActivity() {
+ return mSettingsActivity;
+ }
+
/** Returns the component name for the service. */
@NonNull
public ComponentName getComponentName() {
@@ -133,6 +146,7 @@
dest.writeBoolean(mIsPrimary);
TextUtils.writeToParcel(mOverrideLabel, dest, flags);
TextUtils.writeToParcel(mSettingsSubtitle, dest, flags);
+ TextUtils.writeToParcel(mSettingsActivity, dest, flags);
}
@Override
@@ -161,6 +175,9 @@
+ "settingsSubtitle="
+ mSettingsSubtitle
+ ", "
+ + "settingsActivity="
+ + mSettingsActivity
+ + ", "
+ "capabilities="
+ String.join(",", mCapabilities)
+ "}";
@@ -174,6 +191,7 @@
mIsPrimary = in.readBoolean();
mOverrideLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mSettingsSubtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mSettingsActivity = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
}
public static final @NonNull Parcelable.Creator<CredentialProviderInfo> CREATOR =
@@ -196,6 +214,7 @@
@NonNull private List<String> mCapabilities = new ArrayList<>();
private boolean mIsSystemProvider = false;
@Nullable private CharSequence mSettingsSubtitle = null;
+ @Nullable private CharSequence mSettingsActivity = null;
private boolean mIsEnabled = false;
private boolean mIsPrimary = false;
@Nullable private CharSequence mOverrideLabel = null;
@@ -231,6 +250,16 @@
return this;
}
+ /**
+ * Sets the settings activity.
+ *
+ * @hide
+ */
+ public @NonNull Builder setSettingsActivity(@Nullable CharSequence settingsActivity) {
+ mSettingsActivity = settingsActivity;
+ return this;
+ }
+
/** Sets a list of capabilities this provider service can support. */
public @NonNull Builder addCapabilities(@NonNull List<String> capabilities) {
mCapabilities.addAll(capabilities);
diff --git a/core/java/android/credentials/GetCandidateCredentialsResponse.java b/core/java/android/credentials/GetCandidateCredentialsResponse.java
index 231e4bc..1b130a9 100644
--- a/core/java/android/credentials/GetCandidateCredentialsResponse.java
+++ b/core/java/android/credentials/GetCandidateCredentialsResponse.java
@@ -53,6 +53,15 @@
mCandidateProviderDataList = new ArrayList<>(candidateProviderDataList);
}
+ /**
+ * Returns candidate provider data list.
+ *
+ * @hide
+ */
+ public List<GetCredentialProviderData> getCandidateProviderDataList() {
+ return mCandidateProviderDataList;
+ }
+
protected GetCandidateCredentialsResponse(Parcel in) {
List<GetCredentialProviderData> candidateProviderDataList = new ArrayList<>();
in.readTypedList(candidateProviderDataList, GetCredentialProviderData.CREATOR);
diff --git a/core/java/android/credentials/GetCredentialException.java b/core/java/android/credentials/GetCredentialException.java
index 720c53b..0421d1f 100644
--- a/core/java/android/credentials/GetCredentialException.java
+++ b/core/java/android/credentials/GetCredentialException.java
@@ -18,7 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.Activity;
+import android.content.Context;
import android.os.CancellationSignal;
import android.os.OutcomeReceiver;
@@ -28,8 +28,8 @@
/**
* Represents an error encountered during the
- * {@link CredentialManager#getCredential(GetCredentialRequest,
- * Activity, CancellationSignal, Executor, OutcomeReceiver)} operation.
+ * {@link CredentialManager#getCredential(Context, GetCredentialRequest,
+ * CancellationSignal, Executor, OutcomeReceiver)} operation.
*/
public class GetCredentialException extends Exception {
/**
@@ -41,7 +41,7 @@
/**
* The error type value for when no credential is found available for the given {@link
- * CredentialManager#getCredential(GetCredentialRequest, Activity, CancellationSignal,
+ * CredentialManager#getCredential(Context, GetCredentialRequest, CancellationSignal,
* Executor, OutcomeReceiver)} request.
*/
@NonNull
diff --git a/core/java/android/credentials/PrepareGetCredentialResponse.java b/core/java/android/credentials/PrepareGetCredentialResponse.java
index 056b18a..212f571 100644
--- a/core/java/android/credentials/PrepareGetCredentialResponse.java
+++ b/core/java/android/credentials/PrepareGetCredentialResponse.java
@@ -22,7 +22,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
-import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
import android.content.IntentSender;
@@ -36,7 +35,7 @@
/**
* A response object that prefetches user app credentials and provides metadata about them. It can
* then be used to issue the full credential retrieval flow via the
- * {@link CredentialManager#getCredential(PendingGetCredentialHandle, Activity, CancellationSignal,
+ * {@link CredentialManager#getCredential(Context, PendingGetCredentialHandle, CancellationSignal,
* Executor, OutcomeReceiver)} method to perform the remaining flows such as consent collection
* and credential selection, to officially retrieve a credential.
*/
@@ -44,7 +43,7 @@
/**
* A handle that represents a pending get-credential operation. Pass this handle to {@link
- * CredentialManager#getCredential(PendingGetCredentialHandle, Activity, CancellationSignal,
+ * CredentialManager#getCredential(Context, PendingGetCredentialHandle, CancellationSignal,
* Executor, OutcomeReceiver)} to perform the remaining flows to officially retrieve a
* credential.
*/
@@ -144,7 +143,7 @@
/**
* Returns a handle that represents this pending get-credential operation. Pass this handle to
- * {@link CredentialManager#getCredential(PendingGetCredentialHandle, Activity,
+ * {@link CredentialManager#getCredential(Context, PendingGetCredentialHandle,
* CancellationSignal, Executor, OutcomeReceiver)} to perform the remaining flows to officially
* retrieve a credential.
*/
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/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 746f2f2..b003e75 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -16,6 +16,7 @@
package android.database.sqlite;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -700,6 +701,7 @@
* }
* </pre>
*/
+ @FlaggedApi(Flags.FLAG_SQLITE_APIS_15)
public void beginTransactionReadOnly() {
beginTransactionWithListenerReadOnly(null);
}
@@ -783,6 +785,7 @@
* }
* </pre>
*/
+ @FlaggedApi(Flags.FLAG_SQLITE_APIS_15)
public void beginTransactionWithListenerReadOnly(
@Nullable SQLiteTransactionListener transactionListener) {
beginTransaction(transactionListener, SQLiteSession.TRANSACTION_MODE_DEFERRED);
@@ -2221,6 +2224,7 @@
* @throws IllegalStateException if a transaction is not in progress.
* @throws SQLiteException if the SQL cannot be compiled.
*/
+ @FlaggedApi(Flags.FLAG_SQLITE_APIS_15)
@NonNull
public SQLiteRawStatement createRawStatement(@NonNull String sql) {
Objects.requireNonNull(sql);
@@ -2240,6 +2244,7 @@
* @return The ROWID of the last row to be inserted under this connection.
* @throws IllegalStateException if there is no current transaction.
*/
+ @FlaggedApi(Flags.FLAG_SQLITE_APIS_15)
public long getLastInsertRowId() {
return getThreadSession().getLastInsertRowId();
}
@@ -2253,6 +2258,7 @@
* @return The number of rows changed by the most recent sql statement
* @throws IllegalStateException if there is no current transaction.
*/
+ @FlaggedApi(Flags.FLAG_SQLITE_APIS_15)
public long getLastChangedRowCount() {
return getThreadSession().getLastChangedRowCount();
}
@@ -2280,6 +2286,7 @@
* @return The number of rows changed on the current connection.
* @throws IllegalStateException if there is no current transaction.
*/
+ @FlaggedApi(Flags.FLAG_SQLITE_APIS_15)
public long getTotalChangedRowCount() {
return getThreadSession().getTotalChangedRowCount();
}
diff --git a/core/java/android/database/sqlite/SQLiteRawStatement.java b/core/java/android/database/sqlite/SQLiteRawStatement.java
index 165f181..827420f 100644
--- a/core/java/android/database/sqlite/SQLiteRawStatement.java
+++ b/core/java/android/database/sqlite/SQLiteRawStatement.java
@@ -16,6 +16,7 @@
package android.database.sqlite;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -70,6 +71,7 @@
*
* @see <a href="http://sqlite.org/c3ref/stmt.html">sqlite3_stmt</a>
*/
+@FlaggedApi(Flags.FLAG_SQLITE_APIS_15)
public final class SQLiteRawStatement implements Closeable {
private static final String TAG = "SQLiteRawStatement";
diff --git a/core/java/android/database/sqlite/flags.aconfig b/core/java/android/database/sqlite/flags.aconfig
new file mode 100644
index 0000000..564df03
--- /dev/null
+++ b/core/java/android/database/sqlite/flags.aconfig
@@ -0,0 +1,9 @@
+package: "android.database.sqlite"
+
+flag {
+ name: "sqlite_apis_15"
+ namespace: "system_performance"
+ is_fixed_read_only: true
+ description: "SQLite APIs held back for Android 15"
+ bug: "279043253"
+}
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 82694ee..9c05dfc 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -537,6 +537,24 @@
}
/**
+ * Listens for biometric prompt status, i.e., if it is being shown or idle.
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public void registerBiometricPromptStatusListener(
+ IBiometricPromptStatusListener callback) {
+ if (mService != null) {
+ try {
+ mService.registerBiometricPromptStatusListener(callback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ Slog.w(TAG, "registerBiometricPromptOnKeyguardCallback(): Service not connected");
+ }
+ }
+
+ /**
* Requests all {@link Authenticators.Types#BIOMETRIC_STRONG} sensors to have their
* authenticatorId invalidated for the specified user. This happens when enrollments have been
* added on devices with multiple biometric sensors.
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index af448f0..490ff64 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -20,8 +20,10 @@
import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.hardware.biometrics.BiometricManager.Authenticators;
+import static android.hardware.biometrics.Flags.FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -53,6 +55,7 @@
import java.util.concurrent.Executor;
import javax.crypto.Cipher;
+import javax.crypto.KeyAgreement;
import javax.crypto.Mac;
/**
@@ -748,7 +751,7 @@
* A wrapper class for the cryptographic operations supported by BiometricPrompt.
*
* <p>Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac},
- * {@link IdentityCredential}, and {@link PresentationSession}.
+ * {@link IdentityCredential}, {@link PresentationSession} and {@link KeyAgreement}.
*
* <p>Cryptographic operations in Android can be split into two categories: auth-per-use and
* time-based. This is specified during key creation via the timeout parameter of the
@@ -793,6 +796,11 @@
super(session);
}
+ @FlaggedApi(FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT)
+ public CryptoObject(@NonNull KeyAgreement keyAgreement) {
+ super(keyAgreement);
+ }
+
/**
* Get {@link Signature} object.
* @return {@link Signature} object or null if this doesn't contain one.
@@ -834,6 +842,15 @@
public @Nullable PresentationSession getPresentationSession() {
return super.getPresentationSession();
}
+
+ /**
+ * Get {@link KeyAgreement} object.
+ * @return {@link KeyAgreement} object or null if this doesn't contain one.
+ */
+ @FlaggedApi(FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT)
+ public @Nullable KeyAgreement getKeyAgreement() {
+ return super.getKeyAgreement();
+ }
}
/**
diff --git a/core/java/android/hardware/biometrics/CryptoObject.java b/core/java/android/hardware/biometrics/CryptoObject.java
index 267ef36..6ac1efb 100644
--- a/core/java/android/hardware/biometrics/CryptoObject.java
+++ b/core/java/android/hardware/biometrics/CryptoObject.java
@@ -16,6 +16,9 @@
package android.hardware.biometrics;
+import static android.hardware.biometrics.Flags.FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT;
+
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.security.identity.IdentityCredential;
import android.security.identity.PresentationSession;
@@ -24,6 +27,7 @@
import java.security.Signature;
import javax.crypto.Cipher;
+import javax.crypto.KeyAgreement;
import javax.crypto.Mac;
/**
@@ -62,6 +66,11 @@
mCrypto = session;
}
+ @FlaggedApi(FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT)
+ public CryptoObject(@NonNull KeyAgreement keyAgreement) {
+ mCrypto = keyAgreement;
+ }
+
/**
* Get {@link Signature} object.
* @return {@link Signature} object or null if this doesn't contain one.
@@ -105,6 +114,15 @@
}
/**
+ * 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() {
+ return mCrypto instanceof KeyAgreement ? (KeyAgreement) mCrypto : null;
+ }
+
+ /**
* @hide
* @return the opId associated with this object or 0 if none
*/
diff --git a/core/java/android/hardware/biometrics/IAuthService.aidl b/core/java/android/hardware/biometrics/IAuthService.aidl
index c2e5c0b..8eede47 100644
--- a/core/java/android/hardware/biometrics/IAuthService.aidl
+++ b/core/java/android/hardware/biometrics/IAuthService.aidl
@@ -17,6 +17,7 @@
package android.hardware.biometrics;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
+import android.hardware.biometrics.IBiometricPromptStatusListener;
import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
@@ -63,6 +64,9 @@
// Register callback for when keyguard biometric eligibility changes.
void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback);
+ // Register callback to check biometric prompt status.
+ void registerBiometricPromptStatusListener(IBiometricPromptStatusListener callback);
+
// Requests all BIOMETRIC_STRONG sensors to have their authenticatorId invalidated for the
// specified user. This happens when enrollments have been added on devices with multiple
// biometric sensors.
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptStatusListener.aidl b/core/java/android/hardware/biometrics/IBiometricPromptStatusListener.aidl
new file mode 100644
index 0000000..7a0f438
--- /dev/null
+++ b/core/java/android/hardware/biometrics/IBiometricPromptStatusListener.aidl
@@ -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 android.hardware.biometrics;
+
+/**
+ * Communication channel to propagate biometric prompt status. Implementation of this interface
+ * should be registered in BiometricService#registerBiometricPromptStatusListener.
+ * @hide
+ */
+oneway interface IBiometricPromptStatusListener {
+ void onBiometricPromptShowing();
+ void onBiometricPromptIdle();
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl
index 18c8d1b..36606a1 100644
--- a/core/java/android/hardware/biometrics/IBiometricService.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricService.aidl
@@ -17,6 +17,7 @@
package android.hardware.biometrics;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
+import android.hardware.biometrics.IBiometricPromptStatusListener;
import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IInvalidationCallback;
@@ -68,6 +69,10 @@
@EnforcePermission("USE_BIOMETRIC_INTERNAL")
void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback);
+ // Register a callback for biometric prompt status on keyguard.
+ @EnforcePermission("USE_BIOMETRIC_INTERNAL")
+ void registerBiometricPromptStatusListener(IBiometricPromptStatusListener callback);
+
// Notify BiometricService when <Biometric>Service is ready to start the prepared client.
// Client lifecycle is still managed in <Biometric>Service.
@EnforcePermission("USE_BIOMETRIC_INTERNAL")
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/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index f594377..5bfda70 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -24,12 +24,14 @@
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.Manifest.permission.USE_FINGERPRINT;
import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_NONE;
+import static android.hardware.biometrics.Flags.FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT;
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON;
import static com.android.internal.util.FrameworkStatsLog.AUTH_DEPRECATED_APIUSED__DEPRECATED_API__API_FINGERPRINT_MANAGER_AUTHENTICATE;
import static com.android.internal.util.FrameworkStatsLog.AUTH_DEPRECATED_APIUSED__DEPRECATED_API__API_FINGERPRINT_MANAGER_HAS_ENROLLED_FINGERPRINTS;
import static com.android.internal.util.FrameworkStatsLog.AUTH_DEPRECATED_APIUSED__DEPRECATED_API__API_FINGERPRINT_MANAGER_IS_HARDWARE_DETECTED;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -76,6 +78,7 @@
import java.util.concurrent.Executor;
import javax.crypto.Cipher;
+import javax.crypto.KeyAgreement;
import javax.crypto.Mac;
/**
@@ -312,6 +315,16 @@
public PresentationSession getPresentationSession() {
return super.getPresentationSession();
}
+
+ /**
+ * Get {@link KeyAgreement} object.
+ * @return {@link KeyAgreement} object or null if this doesn't contain one.
+ * @hide
+ */
+ @FlaggedApi(FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT)
+ public KeyAgreement getKeyAgreement() {
+ return super.getKeyAgreement();
+ }
}
/**
diff --git a/core/java/android/hardware/input/KeyboardLayout.java b/core/java/android/hardware/input/KeyboardLayout.java
index bbfed24..883f157 100644
--- a/core/java/android/hardware/input/KeyboardLayout.java
+++ b/core/java/android/hardware/input/KeyboardLayout.java
@@ -82,8 +82,8 @@
DVORAK(4, LAYOUT_TYPE_DVORAK),
COLEMAK(5, LAYOUT_TYPE_COLEMAK),
WORKMAN(6, LAYOUT_TYPE_WORKMAN),
- TURKISH_F(7, LAYOUT_TYPE_TURKISH_F),
- TURKISH_Q(8, LAYOUT_TYPE_TURKISH_Q),
+ TURKISH_Q(7, LAYOUT_TYPE_TURKISH_Q),
+ TURKISH_F(8, LAYOUT_TYPE_TURKISH_F),
EXTENDED(9, LAYOUT_TYPE_EXTENDED);
private final int mValue;
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/OomKillRecord.java b/core/java/android/os/OomKillRecord.java
new file mode 100644
index 0000000..151a65f
--- /dev/null
+++ b/core/java/android/os/OomKillRecord.java
@@ -0,0 +1,63 @@
+/*
+ * 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.os;
+
+
+/**
+ * Expected data to get back from the OOM event's file.
+ * Note that this should be equivalent to the struct <b>OomKill</b> inside
+ * <pre>
+ * system/memory/libmeminfo/libmemevents/include/memevents.h
+ * </pre>
+ *
+ * @hide
+ */
+public final class OomKillRecord {
+ private long mTimeStampInMillis;
+ private int mPid;
+ private int mUid;
+ private String mProcessName;
+ private short mOomScoreAdj;
+
+ public OomKillRecord(long timeStampInMillis, int pid, int uid,
+ String processName, short oomScoreAdj) {
+ this.mTimeStampInMillis = timeStampInMillis;
+ this.mPid = pid;
+ this.mUid = uid;
+ this.mProcessName = processName;
+ this.mOomScoreAdj = oomScoreAdj;
+ }
+
+ public long getTimestampMilli() {
+ return mTimeStampInMillis;
+ }
+
+ public int getPid() {
+ return mPid;
+ }
+
+ public int getUid() {
+ return mUid;
+ }
+
+ public String getProcessName() {
+ return mProcessName;
+ }
+
+ public short getOomScoreAdj() {
+ return mOomScoreAdj;
+ }
+}
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/os/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig
index d8e60c8..66ad12c 100644
--- a/core/java/android/os/vibrator/flags.aconfig
+++ b/core/java/android/os/vibrator/flags.aconfig
@@ -19,4 +19,21 @@
name: "haptics_customization_ringtone_v2_enabled"
description: "Enables the usage of the new RingtoneV2 class"
bug: "241918098"
-}
\ No newline at end of file
+}
+
+flag {
+ namespace: "haptics"
+ name: "enable_vibration_serialization_apis"
+ description: "Enables the APIs for vibration serialization/deserialization."
+ bug: "245129509"
+}
+
+flag {
+ namespace: "haptics"
+ name: "haptic_feedback_vibration_oem_customization_enabled"
+ description: "Enables OEMs/devices to customize vibrations for haptic feedback"
+ # Make read only. This is because the flag is used only once, and this could happen before
+ # the read-write flag values propagate to the device.
+ is_fixed_read_only: true
+ bug: "291128479"
+}
diff --git a/core/java/android/os/vibrator/persistence/ParsedVibration.java b/core/java/android/os/vibrator/persistence/ParsedVibration.java
index ded74ea..3d1deea 100644
--- a/core/java/android/os/vibrator/persistence/ParsedVibration.java
+++ b/core/java/android/os/vibrator/persistence/ParsedVibration.java
@@ -16,6 +16,7 @@
package android.os.vibrator.persistence;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
@@ -34,6 +35,7 @@
*
* @hide
*/
+@FlaggedApi(android.os.vibrator.Flags.FLAG_ENABLE_VIBRATION_SERIALIZATION_APIS)
@TestApi
public class ParsedVibration {
private final List<VibrationEffect> mEffects;
diff --git a/core/java/android/os/vibrator/persistence/VibrationXmlParser.java b/core/java/android/os/vibrator/persistence/VibrationXmlParser.java
index e08cc42..fed1053 100644
--- a/core/java/android/os/vibrator/persistence/VibrationXmlParser.java
+++ b/core/java/android/os/vibrator/persistence/VibrationXmlParser.java
@@ -16,6 +16,7 @@
package android.os.vibrator.persistence;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -115,6 +116,7 @@
*
* @hide
*/
+@FlaggedApi(android.os.vibrator.Flags.FLAG_ENABLE_VIBRATION_SERIALIZATION_APIS)
@TestApi
public final class VibrationXmlParser {
private static final String TAG = "VibrationXmlParser";
diff --git a/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java b/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java
index 1cdfa4f..2880454 100644
--- a/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java
+++ b/core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java
@@ -16,6 +16,7 @@
package android.os.vibrator.persistence;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.TestApi;
@@ -42,6 +43,7 @@
*
* @hide
*/
+@FlaggedApi(android.os.vibrator.Flags.FLAG_ENABLE_VIBRATION_SERIALIZATION_APIS)
@TestApi
public final class VibrationXmlSerializer {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b414ed4..b19a034 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4866,7 +4866,8 @@
"display_color_mode_vendor_hint";
/**
- * The user selected min refresh rate in frames per second.
+ * The user selected min refresh rate in frames per second. If infinite, the user wants
+ * the highest possible refresh rate.
*
* If this isn't set, 0 will be used.
* @hide
@@ -4875,7 +4876,8 @@
public static final String MIN_REFRESH_RATE = "min_refresh_rate";
/**
- * The user selected peak refresh rate in frames per second.
+ * The user selected peak refresh rate in frames per second. If infinite, the user wants
+ * the highest possible refresh rate.
*
* If this isn't set, the system falls back to a device specific default.
* @hide
@@ -5352,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
@@ -11178,6 +11211,12 @@
public static final String BLUETOOTH_ON_WHILE_DRIVING = "bluetooth_on_while_driving";
/**
+ * Volume dialog timeout in ms.
+ * @hide
+ */
+ public static final String VOLUME_DIALOG_DISMISS_TIMEOUT = "volume_dialog_dismiss_timeout";
+
+ /**
* What behavior should be invoked when the volume hush gesture is triggered
* One of VOLUME_HUSH_OFF, VOLUME_HUSH_VIBRATE, VOLUME_HUSH_MUTE.
*
@@ -11607,6 +11646,45 @@
"accessibility_magnification_joystick_enabled";
/**
+ * Controls magnification enable gesture. Accessibility magnification can have one or more
+ * enable gestures.
+ *
+ * @see #ACCESSIBILITY_MAGNIFICATION_GESTURE_NONE
+ * @see #ACCESSIBILITY_MAGNIFICATION_GESTURE_SINGLE_FINGER_TRIPLE_TAP
+ * @see #ACCESSIBILITY_MAGNIFICATION_GESTURE_TWO_FINGER_TRIPLE_TAP
+ * @hide
+ */
+ public static final String ACCESSIBILITY_MAGNIFICATION_GESTURE =
+ "accessibility_magnification_gesture";
+
+ /**
+ * Magnification enable gesture value that is a default value.
+ * @hide
+ */
+ public static final int ACCESSIBILITY_MAGNIFICATION_GESTURE_NONE = 0x0;
+
+ /**
+ * Magnification enable gesture value is single finger triple tap.
+ * @hide
+ */
+ public static final int ACCESSIBILITY_MAGNIFICATION_GESTURE_SINGLE_FINGER_TRIPLE_TAP = 0x1;
+
+ /**
+ * Magnification enable gesture value is two finger triple tap.
+ * @hide
+ */
+ public static final int ACCESSIBILITY_MAGNIFICATION_GESTURE_TWO_FINGER_TRIPLE_TAP = 0x2;
+
+ /**
+ * Magnification enable gesture values include single finger triple tap and two finger
+ * triple tap.
+ * @hide
+ */
+ public static final int ACCESSIBILITY_MAGNIFICATION_GESTURE_ALL =
+ ACCESSIBILITY_MAGNIFICATION_GESTURE_SINGLE_FINGER_TRIPLE_TAP
+ | ACCESSIBILITY_MAGNIFICATION_GESTURE_TWO_FINGER_TRIPLE_TAP;
+
+ /**
* Controls magnification capability. Accessibility magnification is capable of at least one
* of the magnification modes.
*
@@ -12424,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.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/Dataset.java b/core/java/android/service/autofill/Dataset.java
index 115894f..a29bf7a 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -18,6 +18,7 @@
import static android.view.autofill.Helper.sDebug;
+import android.annotation.Hide;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -26,6 +27,7 @@
import android.annotation.TestApi;
import android.content.ClipData;
import android.content.IntentSender;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
@@ -187,6 +189,9 @@
@Nullable private final InlinePresentation mInlinePresentation;
@Nullable private final InlinePresentation mInlineTooltipPresentation;
private final IntentSender mAuthentication;
+
+ @Nullable private final Bundle mAuthenticationExtras;
+
@Nullable String mId;
/**
@@ -224,6 +229,7 @@
mInlinePresentation = inlinePresentation;
mInlineTooltipPresentation = inlineTooltipPresentation;
mAuthentication = authentication;
+ mAuthenticationExtras = null;
mId = id;
}
@@ -246,6 +252,7 @@
mInlinePresentation = dataset.mInlinePresentation;
mInlineTooltipPresentation = dataset.mInlineTooltipPresentation;
mAuthentication = dataset.mAuthentication;
+ mAuthenticationExtras = dataset.mAuthenticationExtras;
mId = dataset.mId;
mAutofillDatatypes = dataset.mAutofillDatatypes;
}
@@ -264,6 +271,7 @@
mInlinePresentation = builder.mInlinePresentation;
mInlineTooltipPresentation = builder.mInlineTooltipPresentation;
mAuthentication = builder.mAuthentication;
+ mAuthenticationExtras = builder.mAuthenticationExtras;
mId = builder.mId;
mAutofillDatatypes = builder.mAutofillDatatypes;
}
@@ -345,6 +353,12 @@
}
/** @hide */
+ @Hide
+ public @Nullable Bundle getAuthenticationExtras() {
+ return mAuthenticationExtras;
+ }
+
+ /** @hide */
@TestApi
public boolean isEmpty() {
return mFieldIds == null || mFieldIds.isEmpty();
@@ -401,6 +415,9 @@
if (mAuthentication != null) {
builder.append(", hasAuthentication");
}
+ if (mAuthenticationExtras != null) {
+ builder.append(", hasAuthenticationExtras");
+ }
if (mAutofillDatatypes != null) {
builder.append(", autofillDatatypes=").append(mAutofillDatatypes);
}
@@ -454,6 +471,8 @@
@Nullable private InlinePresentation mInlinePresentation;
@Nullable private InlinePresentation mInlineTooltipPresentation;
private IntentSender mAuthentication;
+
+ private Bundle mAuthenticationExtras;
private boolean mDestroyed;
@Nullable private String mId;
@@ -624,6 +643,25 @@
}
/**
+ * Sets extras to be associated with the {@code authentication} intent sender, to be
+ * set on the intent that is fired through the intent sender.
+ *
+ * Autofill providers can set any extras they wish to receive directly on the intent
+ * that is used to create the {@code authentication}. This is an internal API, to be
+ * used by the platform to associate data with a given dataset. These extras will be
+ * merged with the {@code clientState} and sent as part of the fill in intent when
+ * the {@code authentication} intentSender is invoked.
+ *
+ * @hide
+ */
+ @Hide
+ public @NonNull Builder setAuthenticationExtras(@Nullable Bundle authenticationExtra) {
+ throwIfDestroyed();
+ mAuthenticationExtras = authenticationExtra;
+ return this;
+ }
+
+ /**
* Sets the id for the dataset so its usage can be tracked.
*
* <p>Dataset usage can be tracked for 2 purposes:
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/credentials/BeginCreateCredentialResponse.java b/core/java/android/service/credentials/BeginCreateCredentialResponse.java
index df93433..5d96f69 100644
--- a/core/java/android/service/credentials/BeginCreateCredentialResponse.java
+++ b/core/java/android/service/credentials/BeginCreateCredentialResponse.java
@@ -143,7 +143,7 @@
*
* <p> Note that as a provider service you will only be able to set a remote entry if :
* - Provider service possesses the
- * {@link Manifest.permission.PROVIDE_REMOTE_CREDENTIALS} permission.
+ * {@link Manifest.permission#PROVIDE_REMOTE_CREDENTIALS} permission.
* - Provider service is configured as the provider that can provide remote entries.
*
* If the above conditions are not met, setting back {@link BeginCreateCredentialResponse}
diff --git a/core/java/android/service/credentials/BeginGetCredentialResponse.java b/core/java/android/service/credentials/BeginGetCredentialResponse.java
index 5ed06ac..ae6ca25 100644
--- a/core/java/android/service/credentials/BeginGetCredentialResponse.java
+++ b/core/java/android/service/credentials/BeginGetCredentialResponse.java
@@ -160,7 +160,7 @@
*
* <p> Note that as a provider service you will only be able to set a remote entry if :
* - Provider service possesses the
- * {@link Manifest.permission.PROVIDE_REMOTE_CREDENTIALS} permission.
+ * {@link Manifest.permission#PROVIDE_REMOTE_CREDENTIALS} permission.
* - Provider service is configured as the provider that can provide remote entries.
*
* If the above conditions are not met, setting back {@link BeginGetCredentialResponse}
diff --git a/core/java/android/service/credentials/CallingAppInfo.java b/core/java/android/service/credentials/CallingAppInfo.java
index e755581..c3524c5 100644
--- a/core/java/android/service/credentials/CallingAppInfo.java
+++ b/core/java/android/service/credentials/CallingAppInfo.java
@@ -103,7 +103,7 @@
* of other applications.
*
* Android system makes sure that only applications that poses the permission
- * {@link android.Manifest.permission.CREDENTIAL_MANAGER_SET_ORIGIN} can set the origin on
+ * {@link android.Manifest.permission#CREDENTIAL_MANAGER_SET_ORIGIN} can set the origin on
* the incoming {@link android.credentials.GetCredentialRequest} or
* {@link android.credentials.CreateCredentialRequest}.
*/
diff --git a/core/java/android/service/credentials/CredentialProviderInfoFactory.java b/core/java/android/service/credentials/CredentialProviderInfoFactory.java
index 0aa6a23..514d722 100644
--- a/core/java/android/service/credentials/CredentialProviderInfoFactory.java
+++ b/core/java/android/service/credentials/CredentialProviderInfoFactory.java
@@ -45,8 +45,8 @@
import android.util.Slog;
import android.util.Xml;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -135,8 +135,8 @@
}
/**
- * Constructs an information instance of the credential provider for testing purposes. Does
- * not run any verifications and passes parameters as is.
+ * Constructs an information instance of the credential provider for testing purposes. Does not
+ * run any verifications and passes parameters as is.
*/
@VisibleForTesting
public static CredentialProviderInfo createForTests(
@@ -151,7 +151,6 @@
.setSystemProvider(isSystemProvider)
.addCapabilities(capabilities)
.build();
-
}
private static void verifyProviderPermission(ServiceInfo serviceInfo) throws SecurityException {
@@ -267,15 +266,21 @@
allAttributes,
com.android.internal.R.styleable.CredentialProvider);
builder.setSettingsSubtitle(
- afsAttributes.getString(
+ getAfsAttributeSafe(
+ afsAttributes,
R.styleable.CredentialProvider_settingsSubtitle));
+ builder.setSettingsActivity(
+ getAfsAttributeSafe(
+ afsAttributes,
+ R.styleable.CredentialProvider_settingsActivity));
} catch (Exception e) {
- Slog.e(TAG, "Failed to get XML attr", e);
+ Slog.w(TAG, "Failed to get XML attr for metadata", e);
} finally {
if (afsAttributes != null) {
afsAttributes.recycle();
}
}
+
builder.addCapabilities(parseXmlProviderOuterCapabilities(parser, resources));
} else {
Slog.w(TAG, "Meta-data does not start with credential-provider-service tag");
@@ -287,6 +292,21 @@
return builder;
}
+ private static @Nullable String getAfsAttributeSafe(
+ @Nullable TypedArray afsAttributes, int resId) {
+ if (afsAttributes == null) {
+ return null;
+ }
+
+ try {
+ return afsAttributes.getString(resId);
+ } catch (Exception e) {
+ Slog.w(TAG, "Failed to get XML attr from afs attributes", e);
+ }
+
+ return null;
+ }
+
private static List<String> parseXmlProviderOuterCapabilities(
XmlPullParser parser, Resources resources) throws IOException, XmlPullParserException {
final List<String> capabilities = new ArrayList<>();
diff --git a/core/java/android/service/credentials/CredentialProviderService.java b/core/java/android/service/credentials/CredentialProviderService.java
index be7b722..a2b0a66 100644
--- a/core/java/android/service/credentials/CredentialProviderService.java
+++ b/core/java/android/service/credentials/CredentialProviderService.java
@@ -153,6 +153,18 @@
public static final String EXTRA_BEGIN_GET_CREDENTIAL_REQUEST =
"android.service.credentials.extra.BEGIN_GET_CREDENTIAL_REQUEST";
+ /**
+ * The key to autofillId associated with the requested credential option and the corresponding
+ * credential entry. The associated autofillId will be contained inside the candidate query
+ * bundle of {@link android.credentials.CredentialOption} if requested through the
+ * {@link com.android.credentialmanager.autofill.CredentialAutofillService}. The resulting
+ * credential entry will contain the autofillId inside its framework extras intent.
+ *
+ * @hide
+ */
+ public static final String EXTRA_AUTOFILL_ID =
+ "android.service.credentials.extra.AUTOFILL_ID";
+
private static final String TAG = "CredProviderService";
/**
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 07f8aac..1cfff14 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -2007,6 +2007,20 @@
return mSmartActions == null ? Collections.emptyList() : mSmartActions;
}
+
+ /**
+ * Sets the smart {@link Notification.Action} objects.
+ *
+ * Should ONLY be used in cases where smartActions need to be removed from, then restored
+ * on, Ranking objects during Parceling, when they are transmitted between processes via
+ * Shared Memory.
+ *
+ * @hide
+ */
+ public void setSmartActions(@Nullable ArrayList<Notification.Action> smartActions) {
+ mSmartActions = smartActions;
+ }
+
/**
* Returns a list of smart replies that can be added by the
* {@link NotificationAssistantService}
@@ -2353,11 +2367,9 @@
/**
* Get a reference to the actual Ranking object corresponding to the key.
- * Used only by unit tests.
*
* @hide
*/
- @VisibleForTesting
public Ranking getRawRankingObject(String key) {
return mRankings.get(key);
}
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index f3b4c6d..c82a4ca 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -17,7 +17,8 @@
import android.annotation.Nullable;
import android.annotation.SuppressLint;
-import android.annotation.TestApi;
+import android.app.Notification;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SharedMemory;
@@ -25,17 +26,20 @@
import android.system.OsConstants;
import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
/**
* Represents an update to notification rankings.
+ *
* @hide
*/
@SuppressLint({"ParcelNotFinal", "ParcelCreator"})
-@TestApi
public class NotificationRankingUpdate implements Parcelable {
private final NotificationListenerService.RankingMap mRankingMap;
@@ -64,6 +68,7 @@
// The ranking map should be stored in shared memory when it is parceled, so we
// unwrap the SharedMemory object.
mRankingMapFd = in.readParcelable(getClass().getClassLoader(), SharedMemory.class);
+ Bundle smartActionsBundle = in.readBundle(getClass().getClassLoader());
// In the case that the ranking map can't be read, readParcelable may return null.
// In this case, we set mRankingMap to null;
@@ -82,15 +87,20 @@
mapParcel.unmarshall(payload, 0, payload.length);
mapParcel.setDataPosition(0);
- mRankingMap = mapParcel.readParcelable(getClass().getClassLoader(),
- android.service.notification.NotificationListenerService.RankingMap.class);
+ mRankingMap =
+ mapParcel.readParcelable(
+ getClass().getClassLoader(),
+ NotificationListenerService.RankingMap.class);
+
+ addSmartActionsFromBundleToRankingMap(smartActionsBundle);
+
} catch (ErrnoException e) {
// TODO(b/284297289): remove throw when associated flag is moved to droidfood, to
// avoid crashes; change to Log.wtf.
throw new RuntimeException(e);
} finally {
mapParcel.recycle();
- if (buffer != null) {
+ if (buffer != null && mRankingMapFd != null) {
mRankingMapFd.unmap(buffer);
mRankingMapFd.close();
}
@@ -102,10 +112,34 @@
}
/**
- * Confirms that the SharedMemory file descriptor is closed. Should only be used for testing.
+ * For each key in the rankingMap, extracts lists of smart actions stored in the provided
+ * bundle and adds them to the corresponding Ranking object in the provided ranking
+ * map, then returns the rankingMap.
+ *
* @hide
*/
- @TestApi
+ private void addSmartActionsFromBundleToRankingMap(Bundle smartActionsBundle) {
+ if (smartActionsBundle == null) {
+ return;
+ }
+
+ String[] rankingMapKeys = mRankingMap.getOrderedKeys();
+ for (int i = 0; i < rankingMapKeys.length; i++) {
+ String key = rankingMapKeys[i];
+ ArrayList<Notification.Action> smartActions =
+ smartActionsBundle.getParcelableArrayList(key, Notification.Action.class);
+ // Get the ranking object from the ranking map.
+ NotificationListenerService.Ranking ranking = mRankingMap.getRawRankingObject(key);
+ ranking.setSmartActions(smartActions);
+ }
+ }
+
+ /**
+ * Confirms that the SharedMemory file descriptor is closed. Should only be used for testing.
+ *
+ * @hide
+ */
+ @VisibleForTesting(otherwise = VisibleForTesting.NONE)
public final boolean isFdNotNullAndClosed() {
return mRankingMapFd != null && mRankingMapFd.getFd() == -1;
}
@@ -145,9 +179,45 @@
if (SystemUiSystemPropertiesFlags.getResolver().isEnabled(
SystemUiSystemPropertiesFlags.NotificationFlags.RANKING_UPDATE_ASHMEM)) {
final Parcel mapParcel = Parcel.obtain();
+ ArrayList<NotificationListenerService.Ranking> marshalableRankings = new ArrayList<>();
+ Bundle smartActionsBundle = new Bundle();
+
+ // We need to separate the SmartActions from the RankingUpdate objects.
+ // SmartActions can contain PendingIntents, which cannot be marshalled,
+ // so we extract them to send separately.
+ String[] rankingMapKeys = mRankingMap.getOrderedKeys();
+ for (int i = 0; i < rankingMapKeys.length; i++) {
+ String key = rankingMapKeys[i];
+ NotificationListenerService.Ranking ranking = mRankingMap.getRawRankingObject(key);
+
+ // Removes the SmartActions and stores them in a separate map.
+ // Note that getSmartActions returns a Collections.emptyList() if there are no
+ // smart actions, and we don't want to needlessly store an empty list object, so we
+ // check for null before storing.
+ List<Notification.Action> smartActions = ranking.getSmartActions();
+ if (!smartActions.isEmpty()) {
+ smartActionsBundle.putParcelableList(key, smartActions);
+ }
+
+ // Create a copy of the ranking object that doesn't have the smart actions.
+ NotificationListenerService.Ranking rankingCopy =
+ new NotificationListenerService.Ranking();
+ rankingCopy.populate(ranking);
+ rankingCopy.setSmartActions(null);
+ marshalableRankings.add(rankingCopy);
+ }
+
+ // Create a new marshalable RankingMap.
+ NotificationListenerService.RankingMap marshalableRankingMap =
+ new NotificationListenerService.RankingMap(
+ marshalableRankings.toArray(
+ new NotificationListenerService.Ranking[0]
+ )
+ );
+
try {
// Parcels the ranking map and measures its size.
- mapParcel.writeParcelable(mRankingMap, flags);
+ mapParcel.writeParcelable(marshalableRankingMap, flags);
int mapSize = mapParcel.dataSize();
// Creates a new SharedMemory object with enough space to hold the ranking map.
@@ -158,15 +228,14 @@
// Gets a read/write buffer mapping the entire shared memory region.
final ByteBuffer buffer = mRankingMapFd.mapReadWrite();
-
// Puts the ranking map into the shared memory region buffer.
buffer.put(mapParcel.marshall(), 0, mapSize);
-
// Protects the region from being written to, by setting it to be read-only.
mRankingMapFd.setProtect(OsConstants.PROT_READ);
-
// Puts the SharedMemory object in the parcel.
out.writeParcelable(mRankingMapFd, flags);
+ // Writes the Parceled smartActions separately.
+ out.writeBundle(smartActionsBundle);
} catch (ErrnoException e) {
// TODO(b/284297289): remove throw when associated flag is moved to droidfood, to
// avoid crashes; change to Log.wtf.
@@ -180,8 +249,8 @@
}
/**
- * @hide
- */
+ * @hide
+ */
public static final @android.annotation.NonNull Parcelable.Creator<NotificationRankingUpdate> CREATOR
= new Parcelable.Creator<NotificationRankingUpdate>() {
public NotificationRankingUpdate createFromParcel(Parcel parcel) {
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/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index f6309f2..cf1156d 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -401,7 +401,7 @@
/**
* Listen for call disconnect causes which contains {@link DisconnectCause} and
- * {@link PreciseDisconnectCause}.
+ * the precise disconnect cause.
*
* <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
* or the calling app has carrier privileges
@@ -851,8 +851,8 @@
* subId. Otherwise, this callback applies to
* {@link SubscriptionManager#getDefaultSubscriptionId()}.
*
- * @param disconnectCause {@link DisconnectCause}.
- * @param preciseDisconnectCause {@link PreciseDisconnectCause}.
+ * @param disconnectCause the disconnect cause
+ * @param preciseDisconnectCause the precise disconnect cause
* @deprecated Use {@link TelephonyCallback.CallDisconnectCauseListener} instead.
*/
@RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
diff --git a/core/java/android/telephony/SubscriptionPlan.java b/core/java/android/telephony/SubscriptionPlan.java
index fb2d771..7b48a16 100644
--- a/core/java/android/telephony/SubscriptionPlan.java
+++ b/core/java/android/telephony/SubscriptionPlan.java
@@ -221,7 +221,7 @@
}
/**
- * Return an array containing all {@link NetworkType}s this SubscriptionPlan applies to.
+ * Return an array containing all network types this SubscriptionPlan applies to.
* @see TelephonyManager for network types values
*/
public @NonNull @NetworkType int[] getNetworkTypes() {
@@ -365,7 +365,7 @@
* Set the network types this SubscriptionPlan applies to. By default the plan will apply
* to all network types. An empty array means this plan applies to no network types.
*
- * @param networkTypes an array of all {@link NetworkType}s that apply to this plan.
+ * @param networkTypes an array of all network types that apply to this plan.
* @see TelephonyManager for network type values
*/
public @NonNull Builder setNetworkTypes(@NonNull @NetworkType int[] networkTypes) {
diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java
index 7ada058..19bcf28 100644
--- a/core/java/android/telephony/TelephonyCallback.java
+++ b/core/java/android/telephony/TelephonyCallback.java
@@ -948,8 +948,8 @@
* subscription ID. Otherwise, this callback applies to
* {@link SubscriptionManager#getDefaultSubscriptionId()}.
*
- * @param disconnectCause {@link DisconnectCause}.
- * @param preciseDisconnectCause {@link PreciseDisconnectCause}.
+ * @param disconnectCause the disconnect cause
+ * @param preciseDisconnectCause the precise disconnect cause
*/
@RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
void onCallDisconnectCauseChanged(@Annotation.DisconnectCauses int disconnectCause,
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index e2c5539..0063d13 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -238,7 +238,7 @@
}
/**
- * To check the SDK version for {@link #listenFromListener}.
+ * To check the SDK version for {@code #listenFromListener}.
*/
@ChangeId
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.P)
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 2b3a081..8862f1d 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -16,6 +16,10 @@
package android.text;
+import static com.android.text.flags.Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN;
+import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH;
+
+import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -280,6 +284,7 @@
* @see android.widget.TextView#setLineBreakWordStyle
*/
@NonNull
+ @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
public Builder setLineBreakConfig(@NonNull LineBreakConfig lineBreakConfig) {
mLineBreakConfig = lineBreakConfig;
return this;
@@ -303,6 +308,7 @@
* @see Layout.Builder#setUseBoundsForWidth(boolean)
*/
@NonNull
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
public Builder setUseBoundsForWidth(boolean useBoundsForWidth) {
mUseBoundsForWidth = useBoundsForWidth;
return this;
diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java
index c585734..94c8eaf 100644
--- a/core/java/android/text/FontConfig.java
+++ b/core/java/android/text/FontConfig.java
@@ -29,6 +29,7 @@
import android.graphics.fonts.FontFamily.Builder.VariableFontFamilyType;
import android.graphics.fonts.FontStyle;
import android.graphics.fonts.FontVariationAxis;
+import android.icu.util.ULocale;
import android.os.Build;
import android.os.LocaleList;
import android.os.Parcel;
@@ -39,6 +40,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Locale;
import java.util.Objects;
@@ -58,6 +60,7 @@
private final @NonNull List<FontFamily> mFamilies;
private final @NonNull List<Alias> mAliases;
private final @NonNull List<NamedFamilyList> mNamedFamilyLists;
+ private final @NonNull List<Customization.LocaleFallback> mLocaleFallbackCustomizations;
private final long mLastModifiedTimeMillis;
private final int mConfigVersion;
@@ -71,10 +74,12 @@
*/
public FontConfig(@NonNull List<FontFamily> families, @NonNull List<Alias> aliases,
@NonNull List<NamedFamilyList> namedFamilyLists,
+ @NonNull List<Customization.LocaleFallback> localeFallbackCustomizations,
long lastModifiedTimeMillis, @IntRange(from = 0) int configVersion) {
mFamilies = families;
mAliases = aliases;
mNamedFamilyLists = namedFamilyLists;
+ mLocaleFallbackCustomizations = localeFallbackCustomizations;
mLastModifiedTimeMillis = lastModifiedTimeMillis;
mConfigVersion = configVersion;
}
@@ -84,7 +89,8 @@
*/
public FontConfig(@NonNull List<FontFamily> families, @NonNull List<Alias> aliases,
long lastModifiedTimeMillis, @IntRange(from = 0) int configVersion) {
- this(families, aliases, Collections.emptyList(), lastModifiedTimeMillis, configVersion);
+ this(families, aliases, Collections.emptyList(), Collections.emptyList(),
+ lastModifiedTimeMillis, configVersion);
}
@@ -113,6 +119,18 @@
}
/**
+ * Returns a locale fallback customizations.
+ *
+ * This field is used for creating the system fallback in the system server. This field is
+ * always empty in the application process.
+ *
+ * @hide
+ */
+ public @NonNull List<Customization.LocaleFallback> getLocaleFallbackCustomizations() {
+ return mLocaleFallbackCustomizations;
+ }
+
+ /**
* Returns the last modified time in milliseconds.
*
* This is a value of {@link System#currentTimeMillis()} when the system font configuration was
@@ -169,7 +187,9 @@
source.readTypedList(familyLists, NamedFamilyList.CREATOR);
long lastModifiedDate = source.readLong();
int configVersion = source.readInt();
- return new FontConfig(families, aliases, familyLists, lastModifiedDate, configVersion);
+ return new FontConfig(families, aliases, familyLists,
+ Collections.emptyList(), // Don't need to pass customization to API caller.
+ lastModifiedDate, configVersion);
}
@Override
@@ -813,4 +833,129 @@
+ '}';
}
}
+
+ /** @hide */
+ public static class Customization {
+ private Customization() {} // Singleton
+
+ /**
+ * A class that represents customization of locale fallback
+ *
+ * This class represents a vendor customization of new-locale-family.
+ *
+ * <pre>
+ * <family customizationType="new-locale-family" operation="prepend" lang="ja-JP">
+ * <font weight="400" style="normal">MyAlternativeFont.ttf
+ * <axis tag="wght" stylevalue="400"/>
+ * </font>
+ * </family>
+ * </pre>
+ *
+ * The operation can be one of prepend, replace or append. The operation prepend means that
+ * the new font family is inserted just before the original font family. The original font
+ * family is still in the fallback. The operation replace means that the original font
+ * family is replaced with new font family. The original font family is removed from the
+ * fallback. The operation append means that the new font family is inserted just after the
+ * original font family. The original font family is still in the fallback.
+ *
+ * The lang attribute is a BCP47 compliant language tag. The font fallback mainly uses ISO
+ * 15924 script code for matching. If the script code is missing, most likely script code
+ * will be used.
+ */
+ public static class LocaleFallback {
+ private final Locale mLocale;
+ private final int mOperation;
+ private final FontFamily mFamily;
+ private final String mScript;
+
+ public static final int OPERATION_PREPEND = 0;
+ public static final int OPERATION_APPEND = 1;
+ public static final int OPERATION_REPLACE = 2;
+
+ /** @hide */
+ @Retention(SOURCE)
+ @IntDef(prefix = { "OPERATION_" }, value = {
+ OPERATION_PREPEND,
+ OPERATION_APPEND,
+ OPERATION_REPLACE
+ })
+ public @interface Operation {}
+
+
+ public LocaleFallback(@NonNull Locale locale, @Operation int operation,
+ @NonNull FontFamily family) {
+ mLocale = locale;
+ mOperation = operation;
+ mFamily = family;
+ mScript = resolveScript(locale);
+ }
+
+ /**
+ * A customization target locale.
+ * @return a locale
+ */
+ public @NonNull Locale getLocale() {
+ return mLocale;
+ }
+
+ /**
+ * An operation to be applied to the original font family.
+ *
+ * The operation can be one of {@link #OPERATION_PREPEND}, {@link #OPERATION_REPLACE} or
+ * {@link #OPERATION_APPEND}.
+ *
+ * The operation prepend ({@link #OPERATION_PREPEND}) means that the new font family is
+ * inserted just before the original font family. The original font family is still in
+ * the fallback.
+ *
+ * The operation replace ({@link #OPERATION_REPLACE}) means that the original font
+ * family is replaced with new font family. The original font family is removed from the
+ * fallback.
+ *
+ * The operation append ({@link #OPERATION_APPEND}) means that the new font family is
+ * inserted just after the original font family. The original font family is still in
+ * the fallback.
+ *
+ * @return an operation.
+ */
+ public @Operation int getOperation() {
+ return mOperation;
+ }
+
+ /**
+ * Returns a family to be inserted or replaced to the fallback.
+ *
+ * @return a family
+ */
+ public @NonNull FontFamily getFamily() {
+ return mFamily;
+ }
+
+ /**
+ * Returns a script of the locale. If the script is missing in the given locale, the
+ * most likely locale is returned.
+ */
+ public @NonNull String getScript() {
+ return mScript;
+ }
+
+ @Override
+ public String toString() {
+ return "LocaleFallback{"
+ + "mLocale=" + mLocale
+ + ", mOperation=" + mOperation
+ + ", mFamily=" + mFamily
+ + '}';
+ }
+ }
+ }
+
+ /** @hide */
+ public static String resolveScript(Locale locale) {
+ String script = locale.getScript();
+ if (script != null && !script.isEmpty()) {
+ return script;
+ }
+ return ULocale.addLikelySubtags(ULocale.forLocale(locale)).getScript();
+ }
}
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index c1d0e9b9..ac5eb3c 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -16,6 +16,9 @@
package android.text;
+import static com.android.text.flags.Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN;
+
+import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -416,10 +419,12 @@
* @hide
*/
@TestApi
+ @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
public interface StyleRunCallback {
/**
* Called when a single style run is identified.
*/
+ @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
void onAppendStyleRun(@NonNull Paint paint,
@Nullable LineBreakConfig lineBreakConfig, @IntRange(from = 0) int length,
boolean isRtl);
@@ -427,6 +432,7 @@
/**
* Called when a single replacement run is identified.
*/
+ @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
void onAppendReplacementRun(@NonNull Paint paint,
@IntRange(from = 0) int length, @Px @FloatRange(from = 0) float width);
}
@@ -488,6 +494,7 @@
@SuppressLint("ExecutorRegistration")
@TestApi
@NonNull
+ @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
public static MeasuredParagraph buildForStaticLayoutTest(
@NonNull TextPaint paint,
@Nullable LineBreakConfig lineBreakConfig,
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index e3c72c9..01279ce 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -16,6 +16,9 @@
package android.text;
+import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH;
+
+import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -439,6 +442,7 @@
* @see Layout.Builder#setUseBoundsForWidth(boolean)
*/
@NonNull
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
public Builder setUseBoundsForWidth(boolean useBoundsForWidth) {
mUseBoundsForWidth = useBoundsForWidth;
return this;
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
new file mode 100644
index 0000000..52fe883
--- /dev/null
+++ b/core/java/android/text/flags/custom_locale_fallback.aconfig
@@ -0,0 +1,9 @@
+package: "com.android.text.flags"
+
+flag {
+ 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."
+ 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/text/style/LineBreakConfigSpan.java b/core/java/android/text/style/LineBreakConfigSpan.java
index 25c1db4d..682ffa1 100644
--- a/core/java/android/text/style/LineBreakConfigSpan.java
+++ b/core/java/android/text/style/LineBreakConfigSpan.java
@@ -71,6 +71,10 @@
.setHyphenation(LineBreakConfig.HYPHENATION_DISABLED)
.build();
+ private static final LineBreakConfig sNoBreakConfig = new LineBreakConfig.Builder()
+ .setLineBreakStyle(LineBreakConfig.LINE_BREAK_STYLE_NO_BREAK)
+ .build();
+
/**
* A specialized {@link LineBreakConfigSpan} that used for preventing hyphenation.
*/
@@ -84,4 +88,24 @@
super(sNoHyphenationConfig);
}
}
+
+ /**
+ * A specialized {@link LineBreakConfigSpan} that used for preventing line break.
+ *
+ * This is useful when you want to preserve some words in the same line.
+ * Note that even if this style is specified, the grapheme based line break is still performed
+ * for preventing clipping text.
+ *
+ * @see LineBreakConfigSpan
+ */
+ @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
+ public static final class NoBreakSpan extends LineBreakConfigSpan {
+ /**
+ * Construct a new {@link NoBreakSpan}.
+ */
+ @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
+ public NoBreakSpan() {
+ super(sNoBreakConfig);
+ }
+ }
}
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/IDecorViewGestureListener.aidl b/core/java/android/view/IDecorViewGestureListener.aidl
new file mode 100644
index 0000000..1022dbf
--- /dev/null
+++ b/core/java/android/view/IDecorViewGestureListener.aidl
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+/**
+ * Listener for changes to gesture interception detector running at DecorView.
+ *
+ * {@hide}
+ */
+oneway interface IDecorViewGestureListener {
+ /**
+ * Called when a DecorView has started intercepting gesture.
+ *
+ * @param windowToken Where did this gesture interception result comes from.
+ * @param intercepted Whether the gesture interception detector has started interception.
+ */
+ void onInterceptionChanged(in IBinder windowToken, in boolean intercepted);
+}
diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl
index c4d3070..a150187 100644
--- a/core/java/android/view/IRecentsAnimationController.aidl
+++ b/core/java/android/view/IRecentsAnimationController.aidl
@@ -23,6 +23,8 @@
import android.window.PictureInPictureSurfaceTransaction;
import android.window.TaskSnapshot;
+import com.android.internal.os.IResultReceiver;
+
/**
* Passed to the {@link IRecentsAnimationRunner} in order for the runner to control to let the
* runner control certain aspects of the recents animation, and to notify window manager when the
@@ -58,7 +60,7 @@
* top resumed app, false otherwise.
*/
@UnsupportedAppUsage
- void finish(boolean moveHomeToTop, boolean sendUserLeaveHint);
+ void finish(boolean moveHomeToTop, boolean sendUserLeaveHint, in IResultReceiver finishCb);
/**
* Called by the handler to indicate that the recents animation input consumer should be
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index d554514..11180ae 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -54,6 +54,10 @@
*/
void executeCommand(String command, String parameters, in ParcelFileDescriptor descriptor);
+ /**
+ * Please dispatch through WindowStateResizeItem instead of directly calling this method from
+ * the system server.
+ */
void resized(in ClientWindowFrames frames, boolean reportDraw,
in MergedConfiguration newMergedConfiguration, in InsetsState insetsState,
boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId,
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index cccac95..c10fc9f 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -48,6 +48,7 @@
import android.view.RemoteAnimationAdapter;
import android.view.IRotationWatcher;
import android.view.ISystemGestureExclusionListener;
+import android.view.IDecorViewGestureListener;
import android.view.IWallpaperVisibilityListener;
import android.view.IWindow;
import android.view.IWindowSession;
@@ -1062,4 +1063,18 @@
@JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+ ".permission.ACCESS_SURFACE_FLINGER)")
boolean replaceContentOnDisplay(int displayId, in SurfaceControl sc);
+
+ /**
+ * Registers a DecorView gesture listener for a given display.
+ */
+ @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+ + ".permission.MONITOR_INPUT)")
+ void registerDecorViewGestureListener(IDecorViewGestureListener listener, int displayId);
+
+ /**
+ * Unregisters a DecorView gesture listener for a given display.
+ */
+ @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+ + ".permission.MONITOR_INPUT)")
+ void unregisterDecorViewGestureListener(IDecorViewGestureListener listener, int displayId);
}
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 83de2a0..7acf2f8 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -284,6 +284,11 @@
oneway void reportSystemGestureExclusionChanged(IWindow window, in List<Rect> exclusionRects);
/**
+ * Called when the DecorView gesture interception state has changed.
+ */
+ oneway void reportDecorViewGestureInterceptionChanged(IWindow window, in boolean intercepted);
+
+ /**
* Called when the keep-clear areas for this window have changed.
*/
oneway void reportKeepClearAreasChanged(IWindow window, in List<Rect> restricted,
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index c35b690..9f886c8 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -778,7 +778,8 @@
* Each gamepad or joystick is given a unique, positive controller number when initially
* configured by the system. This number may change due to events such as device disconnects /
* reconnects or user initiated reassignment. Any change in number will trigger an event that
- * can be observed by registering an {@link InputManagerGlobal.InputDeviceListener}.
+ * can be observed by registering an
+ * {@link android.hardware.input.InputManager.InputDeviceListener}.
* </p>
* <p>
* All input devices which are not gamepads or joysticks will be assigned a controller number
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index 2761aae..45b3fdd 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -16,6 +16,8 @@
package android.view;
+import static com.android.window.flags.Flags.surfaceTrustedOverlay;
+
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.graphics.Matrix;
@@ -35,7 +37,6 @@
* @hide
*/
public final class InputWindowHandle {
-
/**
* An internal annotation for all the {@link android.os.InputConfig} flags that can be
* specified to {@link #inputConfig} to control the behavior of an input window. Only the
@@ -59,7 +60,6 @@
InputConfig.DUPLICATE_TOUCH_TO_WALLPAPER,
InputConfig.IS_WALLPAPER,
InputConfig.PAUSE_DISPATCHING,
- InputConfig.TRUSTED_OVERLAY,
InputConfig.WATCH_OUTSIDE_TOUCH,
InputConfig.SLIPPERY,
InputConfig.DISABLE_USER_ACTIVITY,
@@ -272,4 +272,13 @@
}
this.inputConfig &= ~inputConfig;
}
+
+ public void setTrustedOverlay(SurfaceControl.Transaction t, SurfaceControl sc,
+ boolean isTrusted) {
+ if (surfaceTrustedOverlay()) {
+ t.setTrustedOverlay(sc, isTrusted);
+ } else if (isTrusted) {
+ inputConfig |= InputConfig.TRUSTED_OVERLAY;
+ }
+ }
}
diff --git a/core/java/android/view/ScrollFeedbackProvider.java b/core/java/android/view/ScrollFeedbackProvider.java
index 78716f5..0ba4148 100644
--- a/core/java/android/view/ScrollFeedbackProvider.java
+++ b/core/java/android/view/ScrollFeedbackProvider.java
@@ -17,7 +17,6 @@
package android.view;
import android.annotation.FlaggedApi;
-import android.annotation.NonNull;
import android.view.flags.Flags;
/**
@@ -43,7 +42,8 @@
* the scroll event. If calling this method in response to a {@link MotionEvent}, use the device ID
* that is reported by the event, which can be obtained using {@link MotionEvent#getDeviceId()}.
* Otherwise, use a valid ID that is obtained from {@link InputDevice#getId()}, or from an
- * {@link InputManager} instance ({@link InputManager#getInputDeviceIds()} gives all the valid input
+ * {@link android.hardware.input.InputManager} instance
+ * ({@link android.hardware.input.InputManager#getInputDeviceIds()} gives all the valid input
* device IDs).
*
* <li><p><b>source</b>: should always be the {@link InputDevice} source that generated the scroll
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index be6fb31..2f3d73a 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -123,7 +123,8 @@
private static native long nativeMirrorSurface(long mirrorOfObject);
private static native long nativeCreateTransaction();
private static native long nativeGetNativeTransactionFinalizer();
- private static native void nativeApplyTransaction(long transactionObj, boolean sync);
+ private static native void nativeApplyTransaction(long transactionObj, boolean sync,
+ boolean oneWay);
private static native void nativeMergeTransaction(long transactionObj,
long otherTransactionObj);
private static native void nativeClearTransaction(long transactionObj);
@@ -262,7 +263,7 @@
private static native void nativeSetDefaultFrameRateCompatibility(long transactionObj,
long nativeObject, int compatibility);
private static native void nativeSetFrameRateCategory(
- long transactionObj, long nativeObject, int category);
+ long transactionObj, long nativeObject, int category, boolean smoothSwitchOnly);
private static native void nativeSetFrameRateSelectionStrategy(
long transactionObj, long nativeObject, int strategy);
private static native long nativeGetHandle(long nativeObject);
@@ -1789,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;
@@ -1810,6 +1816,7 @@
+ ", xDpi=" + xDpi
+ ", yDpi=" + yDpi
+ ", refreshRate=" + refreshRate
+ + ", vsyncRate=" + vsyncRate
+ ", appVsyncOffsetNanos=" + appVsyncOffsetNanos
+ ", presentationDeadlineNanos=" + presentationDeadlineNanos
+ ", supportedHdrTypes=" + Arrays.toString(supportedHdrTypes)
@@ -1827,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)
@@ -1835,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));
}
}
@@ -2785,10 +2794,22 @@
* as a new transaction.
*/
public void apply() {
- apply(false);
+ apply(/*sync*/ false);
}
/**
+ * Applies the transaction as a one way binder call. This transaction will be applied out
+ * of order with other transactions that are applied synchronously. This method is not
+ * safe. It should only be used when the order does not matter.
+ *
+ * @hide
+ */
+ public void applyAsyncUnsafe() {
+ apply(/*sync*/ false, /*oneWay*/ true);
+ }
+
+
+ /**
* Clear the transaction object, without applying it.
*
* @hide
@@ -2817,9 +2838,13 @@
* @hide
*/
public void apply(boolean sync) {
+ apply(sync, /*oneWay*/ false);
+ }
+
+ private void apply(boolean sync, boolean oneWay) {
applyResizedSurfaces();
notifyReparentedSurfaces();
- nativeApplyTransaction(mNativeObject, sync);
+ nativeApplyTransaction(mNativeObject, sync, oneWay);
}
/**
@@ -3684,6 +3709,10 @@
* @param sc The SurfaceControl to specify the frame rate category of.
* @param category The frame rate category of this surface. The category value may influence
* the system's choice of display frame rate.
+ * @param smoothSwitchOnly Set to {@code true} to indicate the display frame rate should not
+ * change if changing it would cause jank. Else {@code false}.
+ * This parameter is ignored when {@code category} is
+ * {@link Surface#FRAME_RATE_CATEGORY_DEFAULT}.
*
* @return This transaction object.
*
@@ -3692,10 +3721,10 @@
* @hide
*/
@NonNull
- public Transaction setFrameRateCategory(
- @NonNull SurfaceControl sc, @Surface.FrameRateCategory int category) {
+ public Transaction setFrameRateCategory(@NonNull SurfaceControl sc,
+ @Surface.FrameRateCategory int category, boolean smoothSwitchOnly) {
checkPreconditions(sc);
- nativeSetFrameRateCategory(mNativeObject, sc.mNativeObject, category);
+ nativeSetFrameRateCategory(mNativeObject, sc.mNativeObject, category, smoothSwitchOnly);
return this;
}
@@ -4241,8 +4270,7 @@
* be somewhat arbitrary, and so there are some somewhat arbitrary decisions in
* this API as well.
* <p>
- * @param sc The {@link SurfaceControl} to set the
- * {@link TrustedPresentationCallback} on
+ * @param sc The {@link SurfaceControl} to set the callback on
* @param thresholds The {@link TrustedPresentationThresholds} that will specify when the to
* invoke the callback.
* @param executor The {@link Executor} where the callback will be invoked on.
@@ -4275,10 +4303,9 @@
}
/**
- * Clears the {@link TrustedPresentationCallback} for a specific {@link SurfaceControl}
+ * Clears the callback for a specific {@link SurfaceControl}
*
- * @param sc The SurfaceControl that the {@link TrustedPresentationCallback} should be
- * cleared from
+ * @param sc The SurfaceControl that the callback should be cleared from
* @return This transaction
*/
@NonNull
@@ -4373,7 +4400,7 @@
void applyGlobalTransaction(boolean sync) {
applyResizedSurfaces();
notifyReparentedSurfaces();
- nativeApplyTransaction(mNativeObject, sync);
+ nativeApplyTransaction(mNativeObject, sync, /*oneWay*/ false);
}
@Override
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index effc127..57b19a8 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -407,7 +407,7 @@
public @Nullable SurfacePackage getSurfacePackage() {
if (mSurfaceControl != null && mAccessibilityEmbeddedConnection != null) {
return new SurfacePackage(new SurfaceControl(mSurfaceControl, "getSurfacePackage"),
- mAccessibilityEmbeddedConnection, getFocusGrantToken(), mRemoteInterface);
+ mAccessibilityEmbeddedConnection, getInputTransferToken(), mRemoteInterface);
} else {
return null;
}
@@ -526,10 +526,12 @@
}
/**
+ * Returns an input token used which can be used to request focus on the embedded surface.
+ *
* @hide
*/
- public IBinder getFocusGrantToken() {
- return mWm.getFocusGrantToken(getWindowToken().asBinder());
+ public IBinder getInputTransferToken() {
+ return mWm.getInputTransferToken(getWindowToken().asBinder());
}
private void addWindowToken(WindowManager.LayoutParams attrs) {
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 cf46bcc..b5648cc 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1730,7 +1730,7 @@
attrs.getTitle().toString());
mAttachInfo.mThreadedRenderer = renderer;
renderer.setSurfaceControl(mSurfaceControl, mBlastBufferQueue);
- updateColorModeIfNeeded(attrs.getColorMode());
+ updateColorModeIfNeeded(attrs.getColorMode(), attrs.getDesiredHdrHeadroom());
updateRenderHdrSdrRatio();
updateForceDarkMode();
mAttachInfo.mHardwareAccelerated = true;
@@ -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) {
@@ -3351,7 +3349,7 @@
}
final boolean alwaysConsumeSystemBarsChanged =
mPendingAlwaysConsumeSystemBars != mAttachInfo.mAlwaysConsumeSystemBars;
- updateColorModeIfNeeded(lp.getColorMode());
+ updateColorModeIfNeeded(lp.getColorMode(), lp.getDesiredHdrHeadroom());
surfaceCreated = !hadSurface && mSurface.isValid();
surfaceDestroyed = hadSurface && !mSurface.isValid();
@@ -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) {
@@ -5320,6 +5319,29 @@
}
/**
+ * Called from DecorView when gesture interception state has changed.
+ *
+ * @param intercepted If DecorView is intercepting touch events
+ */
+ public void updateDecorViewGestureInterception(boolean intercepted) {
+ mHandler.sendMessage(
+ mHandler.obtainMessage(
+ MSG_DECOR_VIEW_GESTURE_INTERCEPTION,
+ /* arg1= */ intercepted ? 1 : 0,
+ /* arg2= */ 0));
+ }
+
+ void decorViewInterceptionChanged(boolean intercepted) {
+ if (mView != null) {
+ try {
+ mWindowSession.reportDecorViewGestureInterceptionChanged(mWindow, intercepted);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
* Set the root-level system gesture exclusion rects. These are added to those provided by
* the root's view hierarchy.
*/
@@ -5630,7 +5652,8 @@
mUpdateHdrSdrRatioInfo = true;
}
- private void updateColorModeIfNeeded(@ActivityInfo.ColorMode int colorMode) {
+ private void updateColorModeIfNeeded(@ActivityInfo.ColorMode int colorMode,
+ float desiredRatio) {
if (mAttachInfo.mThreadedRenderer == null) {
return;
}
@@ -5644,7 +5667,10 @@
&& !getConfiguration().isScreenWideColorGamut()) {
colorMode = ActivityInfo.COLOR_MODE_DEFAULT;
}
- float desiredRatio = mAttachInfo.mThreadedRenderer.setColorMode(colorMode);
+ float automaticRatio = mAttachInfo.mThreadedRenderer.setColorMode(colorMode);
+ if (desiredRatio == 0 || desiredRatio > automaticRatio) {
+ desiredRatio = automaticRatio;
+ }
if (desiredRatio != mDesiredHdrSdrRatio) {
mDesiredHdrSdrRatio = desiredRatio;
updateRenderHdrSdrRatio();
@@ -5942,6 +5968,7 @@
private static final int MSG_KEEP_CLEAR_RECTS_CHANGED = 35;
private static final int MSG_REPORT_KEEP_CLEAR_RECTS = 36;
private static final int MSG_PAUSED_FOR_SYNC_TIMEOUT = 37;
+ private static final int MSG_DECOR_VIEW_GESTURE_INTERCEPTION = 38;
final class ViewRootHandler extends Handler {
@Override
@@ -6220,6 +6247,9 @@
case MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED: {
systemGestureExclusionChanged();
} break;
+ case MSG_DECOR_VIEW_GESTURE_INTERCEPTION: {
+ decorViewInterceptionChanged(/* intercepted= */ msg.arg1 == 1);
+ } break;
case MSG_KEEP_CLEAR_RECTS_CHANGED: {
keepClearRectsChanged(/* accessibilityFocusRectChanged= */ msg.arg1 == 1);
} break;
@@ -8579,10 +8609,6 @@
mLastLayoutFrame.set(frame);
}
- if (mOnBackInvokedDispatcher.isSystemGestureExclusionNeeded()) {
- setRootSystemGestureExclusionRects(List.of(frame));
- }
-
final WindowConfiguration winConfig = getCompatWindowConfiguration();
mPendingBackDropFrame.set(mPendingDragResizing && !winConfig.useWindowFrameForBackdrop()
? winConfig.getMaxBounds()
@@ -9027,8 +9053,8 @@
mAdded = false;
AnimationHandler.removeRequestor(this);
}
- handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mPendingTransaction,
- "shutting down VRI");
+ handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mHasPendingTransactions,
+ mPendingTransaction, "shutting down VRI");
WindowManagerGlobal.getInstance().doRemoveView(this);
}
@@ -10521,6 +10547,8 @@
MergedConfiguration mergedConfiguration, InsetsState insetsState,
boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId, int syncSeqId,
boolean dragResizing) {
+ // Although this is a AIDL method, it will only be triggered in local process through
+ // either WindowStateResizeItem or WindowlessWindowManager.
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.dispatchResized(frames, reportDraw, mergedConfiguration, insetsState,
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 2f04b0c..87537fbc 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -24,6 +24,8 @@
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
import android.annotation.IdRes;
import android.annotation.LayoutRes;
import android.annotation.NonNull;
@@ -1330,6 +1332,47 @@
}
/**
+ * <p>Sets the desired about of HDR headroom to be used when rendering as a ratio of
+ * targetHdrPeakBrightnessInNits / targetSdrWhitePointInNits. Only applies when
+ * {@link #setColorMode(int)} is {@link ActivityInfo#COLOR_MODE_HDR}</p>
+ *
+ * <p>By default the system will choose an amount of HDR headroom that is appropriate
+ * for the underlying device capabilities & bit-depth of the panel. However, for some types
+ * of content this can end up being more headroom than necessary or desired. An example
+ * would be a messaging app or gallery thumbnail view where some amount of HDR pop is desired
+ * without overly influencing the perceived brightness of the majority SDR content. This can
+ * also be used to animate in/out of an HDR range for smoother transitions.</p>
+ *
+ * <p>Note: The actual amount of HDR headroom that will be given is subject to a variety
+ * of factors such as ambient conditions, display capabilities, or bit-depth limitations.
+ * See {@link Display#getHdrSdrRatio()} for more information as well as how to query the
+ * current value.</p>
+ *
+ * @param desiredHeadroom The amount of HDR headroom that is desired. Must be >= 1.0 (no HDR)
+ * and <= 10,000.0. Passing 0.0 will reset to the default, automatically
+ * chosen value.
+ * @see #getDesiredHdrHeadroom()
+ * @see Display#getHdrSdrRatio()
+ */
+ @FlaggedApi(com.android.graphics.hwui.flags.Flags.FLAG_LIMITED_HDR)
+ public void setDesiredHdrHeadroom(
+ @FloatRange(from = 0.0f, to = 10000.0) float desiredHeadroom) {
+ final WindowManager.LayoutParams attrs = getAttributes();
+ attrs.setDesiredHdrHeadroom(desiredHeadroom);
+ dispatchWindowAttributesChanged(attrs);
+ }
+
+ /**
+ * Get the desired amount of HDR headroom as set by {@link #setDesiredHdrHeadroom(float)}
+ * @return The amount of HDR headroom set, or 0 for automatic/default behavior.
+ * @see #setDesiredHdrHeadroom(float)
+ */
+ @FlaggedApi(com.android.graphics.hwui.flags.Flags.FLAG_LIMITED_HDR)
+ public float getDesiredHdrHeadroom() {
+ return getAttributes().getDesiredHdrHeadroom();
+ }
+
+ /**
* If {@code isPreferred} is true, this method requests that the connected display does minimal
* post processing when this window is visible on the screen. Otherwise, it requests that the
* display switches back to standard image processing.
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index e64274e..a3b93b4 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -82,6 +82,8 @@
import android.Manifest.permission;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -1683,7 +1685,7 @@
* orientation (e.g. with {@link android.app.Activity#setRequestedOrientation(int)}). This
* listener gives application an opportunity to selectively react to device orientation changes.
* The newly added listener will be called with current proposed rotation. Note that the context
- * of this window manager instance must be a {@link android.annotation.UiContext}.
+ * of this window manager instance must be a {@code UiContext}.
*
* @param executor The executor on which callback method will be invoked.
* @param listener Called when the proposed rotation for the context is being delivered.
@@ -1691,7 +1693,7 @@
* {@link Surface#ROTATION_90}, {@link Surface#ROTATION_180} and
* {@link Surface#ROTATION_270}.
* @throws UnsupportedOperationException if this method is called on an instance that is not
- * associated with a {@link android.annotation.UiContext}.
+ * associated with a {@code UiContext}.
*/
default void addProposedRotationListener(@NonNull @CallbackExecutor Executor executor,
@NonNull IntConsumer listener) {
@@ -1850,6 +1852,8 @@
to = "PHONE"),
@ViewDebug.IntToString(from = TYPE_SYSTEM_ALERT,
to = "SYSTEM_ALERT"),
+ @ViewDebug.IntToString(from = TYPE_KEYGUARD,
+ to = "KEYGUARD"),
@ViewDebug.IntToString(from = TYPE_TOAST,
to = "TOAST"),
@ViewDebug.IntToString(from = TYPE_SYSTEM_OVERLAY,
@@ -1898,6 +1902,8 @@
to = "PRIVATE_PRESENTATION"),
@ViewDebug.IntToString(from = TYPE_VOICE_INTERACTION,
to = "VOICE_INTERACTION"),
+ @ViewDebug.IntToString(from = TYPE_ACCESSIBILITY_OVERLAY,
+ to = "ACCESSIBILITY_OVERLAY"),
@ViewDebug.IntToString(from = TYPE_VOICE_INTERACTION_STARTING,
to = "VOICE_INTERACTION_STARTING"),
@ViewDebug.IntToString(from = TYPE_DOCK_DIVIDER,
@@ -1907,7 +1913,13 @@
@ViewDebug.IntToString(from = TYPE_SCREENSHOT,
to = "SCREENSHOT"),
@ViewDebug.IntToString(from = TYPE_APPLICATION_OVERLAY,
- to = "APPLICATION_OVERLAY")
+ to = "APPLICATION_OVERLAY"),
+ @ViewDebug.IntToString(from = TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY,
+ to = "ACCESSIBILITY_MAGNIFICATION_OVERLAY"),
+ @ViewDebug.IntToString(from = TYPE_NOTIFICATION_SHADE,
+ to = "NOTIFICATION_SHADE"),
+ @ViewDebug.IntToString(from = TYPE_STATUS_BAR_ADDITIONAL,
+ to = "STATUS_BAR_ADDITIONAL")
})
@WindowType
public int type;
@@ -3103,7 +3115,7 @@
/**
* Never animate position changes of the window.
*
- * @see android.R.attr#Window_windowNoMoveAnimation
+ * @see android.R.styleable#Window_windowNoMoveAnimation
* {@hide}
*/
@UnsupportedAppUsage
@@ -4304,6 +4316,9 @@
@ActivityInfo.ColorMode
private int mColorMode = COLOR_MODE_DEFAULT;
+ /** @hide */
+ private float mDesiredHdrHeadroom = 0;
+
/**
* Carries the requests about {@link WindowInsetsController.Appearance} and
* {@link WindowInsetsController.Behavior} to the system windows which can produce insets.
@@ -4516,7 +4531,7 @@
* Set whether animations can be played for position changes on this window. If disabled,
* the window will move to its new position instantly without animating.
*
- * @attr ref android.R.attr#Window_windowNoMoveAnimation
+ * @attr ref android.R.styleable#Window_windowNoMoveAnimation
*/
public void setCanPlayMoveAnimation(boolean enable) {
if (enable) {
@@ -4531,7 +4546,7 @@
* This does not guarantee that an animation will be played in all such situations. For
* example, drag-resizing may move the window but not play an animation.
*
- * @attr ref android.R.attr#Window_windowNoMoveAnimation
+ * @attr ref android.R.styleable#Window_windowNoMoveAnimation
*/
public boolean canPlayMoveAnimation() {
return (privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0;
@@ -4706,6 +4721,39 @@
}
/**
+ * <p>Sets the desired about of HDR headroom to be used when rendering as a ratio of
+ * targetHdrPeakBrightnessInNits / targetSdrWhitePointInNits. Only applies when
+ * {@link #setColorMode(int)} is {@link ActivityInfo#COLOR_MODE_HDR}</p>
+ *
+ * @see Window#setDesiredHdrHeadroom(float)
+ * @param desiredHeadroom Desired amount of HDR headroom. Must be in the range of 1.0 (SDR)
+ * to 10,000.0, or 0.0 to reset to default.
+ */
+ @FlaggedApi(com.android.graphics.hwui.flags.Flags.FLAG_LIMITED_HDR)
+ public void setDesiredHdrHeadroom(
+ @FloatRange(from = 0.0f, to = 10000.0f) float desiredHeadroom) {
+ if (!Float.isFinite(desiredHeadroom)) {
+ throw new IllegalArgumentException("desiredHeadroom must be finite: "
+ + desiredHeadroom);
+ }
+ if (desiredHeadroom != 0 && (desiredHeadroom < 1.0f || desiredHeadroom > 10000.0f)) {
+ throw new IllegalArgumentException(
+ "desiredHeadroom must be 0.0 or in the range [1.0, 10000.0f], received: "
+ + desiredHeadroom);
+ }
+ mDesiredHdrHeadroom = desiredHeadroom;
+ }
+
+ /**
+ * Get the desired amount of HDR headroom as set by {@link #setDesiredHdrHeadroom(float)}
+ * @return The amount of HDR headroom set, or 0 for automatic/default behavior.
+ */
+ @FlaggedApi(com.android.graphics.hwui.flags.Flags.FLAG_LIMITED_HDR)
+ public float getDesiredHdrHeadroom() {
+ return mDesiredHdrHeadroom;
+ }
+
+ /**
* <p>
* Blurs the screen behind the window. The effect is similar to that of {@link #dimAmount},
* but instead of dimmed, the content behind the window will be blurred (or combined with
@@ -4855,6 +4903,7 @@
checkNonRecursiveParams();
out.writeTypedArray(paramsForRotation, 0 /* parcelableFlags */);
out.writeInt(mDisplayFlags);
+ out.writeFloat(mDesiredHdrHeadroom);
}
public static final @android.annotation.NonNull Parcelable.Creator<LayoutParams> CREATOR
@@ -4926,6 +4975,7 @@
forciblyShownTypes = in.readInt();
paramsForRotation = in.createTypedArray(LayoutParams.CREATOR);
mDisplayFlags = in.readInt();
+ mDesiredHdrHeadroom = in.readFloat();
}
@SuppressWarnings({"PointlessBitwiseExpression"})
@@ -5186,6 +5236,11 @@
changes |= COLOR_MODE_CHANGED;
}
+ if (mDesiredHdrHeadroom != o.mDesiredHdrHeadroom) {
+ mDesiredHdrHeadroom = o.mDesiredHdrHeadroom;
+ changes |= COLOR_MODE_CHANGED;
+ }
+
if (preferMinimalPostProcessing != o.preferMinimalPostProcessing) {
preferMinimalPostProcessing = o.preferMinimalPostProcessing;
changes |= MINIMAL_POST_PROCESSING_PREFERENCE_CHANGED;
@@ -5413,6 +5468,9 @@
if (mColorMode != COLOR_MODE_DEFAULT) {
sb.append(" colorMode=").append(ActivityInfo.colorModeToString(mColorMode));
}
+ if (mDesiredHdrHeadroom != 0) {
+ sb.append(" desiredHdrHeadroom=").append(mDesiredHdrHeadroom);
+ }
if (preferMinimalPostProcessing) {
sb.append(" preferMinimalPostProcessing=");
sb.append(preferMinimalPostProcessing);
@@ -5811,6 +5869,7 @@
*
* @hide
*/
+ @FlaggedApi("REPLACE_CONTENT_WITH_MIRROR")
@TestApi
@RequiresPermission(permission.ACCESS_SURFACE_FLINGER)
default boolean replaceContentOnDisplayWithMirror(int displayId, @NonNull Window window) {
@@ -5826,6 +5885,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/WindowMetrics.java b/core/java/android/view/WindowMetrics.java
index 7ad43c7..26298bc 100644
--- a/core/java/android/view/WindowMetrics.java
+++ b/core/java/android/view/WindowMetrics.java
@@ -47,7 +47,6 @@
* @see WindowInsets#getInsets(int)
* @see WindowManager#getCurrentWindowMetrics()
* @see WindowManager#getMaximumWindowMetrics()
- * @see android.annotation.UiContext
*/
public final class WindowMetrics {
@NonNull
@@ -99,8 +98,7 @@
}
/**
- * Returns the bounds of the area associated with this window or
- * {@link android.annotation.UiContext}.
+ * Returns the bounds of the area associated with this window or {@code UiContext}.
* <p>
* <b>Note that the size of the reported bounds can have different size than
* {@link Display#getSize(Point)}.</b> This method reports the window size including all system
@@ -133,7 +131,7 @@
/**
* Returns the {@link WindowInsets} of the area associated with this window or
- * {@link android.annotation.UiContext}.
+ * {@code UiContext}.
*
* @return the {@link WindowInsets} of the visual area.
*/
@@ -146,9 +144,8 @@
}
/**
- * Returns the density of the area associated with this window or
- * {@link android.annotation.UiContext}, which uses the same units as
- * {@link android.util.DisplayMetrics#density}.
+ * Returns the density of the area associated with this window or {@code UiContext},
+ * which uses the same units as {@link android.util.DisplayMetrics#density}.
*
* @see android.util.DisplayMetrics#DENSITY_DEFAULT
* @see android.util.DisplayMetrics#density
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 7d3d283..7c3b6ae 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -58,7 +58,7 @@
SurfaceControl mLeash;
Rect mFrame;
Rect mAttachedFrame;
- IBinder mFocusGrantToken;
+ IBinder mInputTransferToken;
State(SurfaceControl sc, WindowManager.LayoutParams p, int displayId, IWindow client,
SurfaceControl leash, Rect frame) {
@@ -89,7 +89,7 @@
private final Configuration mConfiguration;
private final IWindowSession mRealWm;
private final IBinder mHostInputToken;
- private final IBinder mFocusGrantToken = new Binder();
+ private final IBinder mInputTransferToken = new Binder();
private InsetsState mInsetsState;
private final ClientWindowFrames mTmpFrames = new ClientWindowFrames();
private final MergedConfiguration mTmpConfig = new MergedConfiguration();
@@ -109,17 +109,17 @@
mConfiguration.setTo(configuration);
}
- IBinder getFocusGrantToken(IBinder window) {
+ IBinder getInputTransferToken(IBinder window) {
synchronized (this) {
// This can only happen if someone requested the focusGrantToken before setView was
// called for the SCVH. In that case, use the root focusGrantToken since this will be
// the same token sent to WMS for the root window once setView is called.
if (mStateForWindow.isEmpty()) {
- return mFocusGrantToken;
+ return mInputTransferToken;
}
State state = mStateForWindow.get(window);
if (state != null) {
- return state.mFocusGrantToken;
+ return state.mInputTransferToken;
}
}
@@ -207,9 +207,9 @@
// Give the first window the mFocusGrantToken since that's the token the host can use
// to give focus to the embedded.
if (mStateForWindow.isEmpty()) {
- state.mFocusGrantToken = mFocusGrantToken;
+ state.mInputTransferToken = mInputTransferToken;
} else {
- state.mFocusGrantToken = new Binder();
+ state.mInputTransferToken = new Binder();
}
mStateForWindow.put(window.asBinder(), state);
@@ -230,12 +230,13 @@
new SurfaceControl(sc, "WindowlessWindowManager.addToDisplay"),
window, mHostInputToken, attrs.flags, attrs.privateFlags,
attrs.inputFeatures, attrs.type,
- attrs.token, state.mFocusGrantToken, attrs.getTitle().toString(),
+ attrs.token, state.mInputTransferToken, attrs.getTitle().toString(),
outInputChannel);
} else {
mRealWm.grantInputChannel(displayId, sc, window, mHostInputToken, attrs.flags,
attrs.privateFlags, attrs.inputFeatures, attrs.type, attrs.token,
- state.mFocusGrantToken, attrs.getTitle().toString(), outInputChannel);
+ state.mInputTransferToken, attrs.getTitle().toString(),
+ outInputChannel);
}
state.mInputChannelToken =
outInputChannel != null ? outInputChannel.getToken() : null;
@@ -583,9 +584,13 @@
}
@Override
- public void reportKeepClearAreasChanged(android.view.IWindow window, List<Rect> restrictedRects,
- List<Rect> unrestrictedRects) {
- }
+ public void reportDecorViewGestureInterceptionChanged(IWindow window, boolean intercepted) {}
+
+ @Override
+ public void reportKeepClearAreasChanged(
+ android.view.IWindow window,
+ List<Rect> restrictedRects,
+ List<Rect> unrestrictedRects) {}
@Override
public void grantInputChannel(int displayId, SurfaceControl surface, IWindow window,
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index e6a8b78..43bfe13 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -1562,9 +1562,8 @@
* describes the action.
* </p>
* <p>
- * Use {@link androidx.core.view.ViewCompat#addAccessibilityAction(View,
- * AccessibilityNodeInfoCompat.AccessibilityActionCompat)} to register an action directly on the
- * view.
+ * Use {@link androidx.core.view.ViewCompat#addAccessibilityAction(View, CharSequence,
+ * AccessibilityViewCommand)} to register an action directly on the view.
* <p>
* <strong>Note:</strong> Cannot be called from an
* {@link android.accessibilityservice.AccessibilityService}.
@@ -5167,8 +5166,7 @@
* </p>
* <aside class="note">
* <b>Note:</b> Use {@link androidx.core.view.ViewCompat#addAccessibilityAction(View,
- * AccessibilityNodeInfoCompat.AccessibilityActionCompat)} to register an action directly on the
- * view.
+ * CharSequence, AccessibilityViewCommand)} to register an action directly on the view.
* </p>
*/
public static final class AccessibilityAction implements Parcelable {
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index a0d5800..85dadd4 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -4,5 +4,12 @@
namespace: "accessibility"
name: "force_invert_color"
description: "Enable force force-dark for smart inversion and dark theme everywhere"
- bug: "239594271"
-}
\ No newline at end of file
+ bug: "282821643"
+}
+
+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..32256b9 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -56,7 +56,7 @@
private static final int SEQUENTIALLY = 1;
/**
- * For apps targeting {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} and above,
+ * For apps targeting {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM} and above,
* this change ID enables to use expectedPresentationTime instead of the frameTime
* for the frame start time .
*
@@ -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 89fa83e..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,20 +50,17 @@
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;
import android.service.autofill.UserData;
import android.text.TextUtils;
@@ -88,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;
@@ -118,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;
@@ -188,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.
@@ -277,6 +263,12 @@
"android.view.autofill.extra.CLIENT_STATE";
/**
+ * @hide
+ */
+ public static final String EXTRA_AUTH_STATE =
+ "android.view.autofill.extra.AUTH_STATE";
+
+ /**
* Intent extra: the {@link android.view.inputmethod.InlineSuggestionsRequest} in the
* autofill request.
*
@@ -327,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;
@@ -428,6 +419,14 @@
public static final int STATE_UNKNOWN_FAILED = 6;
/**
+ * Same as {@link #STATE_ACTIVE}, but when pending authentication after
+ * {@link AutofillManagerClient#authenticate(int, int, IntentSender, Intent, boolean)}
+ *
+ * @hide
+ */
+ public static final int STATE_PENDING_AUTHENTICATION = 7;
+
+ /**
* Timeout in ms for calls to the field classification service.
* @hide
*/
@@ -661,11 +660,6 @@
@GuardedBy("mLock")
private boolean mEnabledForAugmentedAutofillOnly;
- @GuardedBy("mLock")
- @Nullable private AutofillRequestCallback mAutofillRequestCallback;
- @GuardedBy("mLock")
- @Nullable private Executor mRequestCallbackExecutor;
-
private boolean mScreenHasCredmanField;
/**
@@ -731,6 +725,10 @@
// Indicate whether WebView should always be included in the assist structure
private boolean mShouldAlwaysIncludeWebviewInAssistStructure;
+ // Controls logic around apps changing some properties of their views when activity loses
+ // focus due to autofill showing biometric activity, password manager, or password breach check.
+ private boolean mRelayoutFix;
+
// Indicates whether called the showAutofillDialog() method.
private boolean mShowAutofillDialogCalled = false;
@@ -952,6 +950,8 @@
mShouldAlwaysIncludeWebviewInAssistStructure =
AutofillFeatureFlags.shouldAlwaysIncludeWebviewInAssistStructure();
+
+ mRelayoutFix = Flags.relayout();
}
/**
@@ -1715,7 +1715,13 @@
synchronized (mLock) {
if (mForAugmentedAutofillOnly) {
if (sVerbose) {
- Log.v(TAG, "notifyViewVisibilityChanged(): ignoring on augmented only mode");
+ Log.v(TAG, "notifyViewVisibilityChanged(): ignoring on augmented only mode");
+ }
+ return;
+ }
+ if (mRelayoutFix && mState == STATE_PENDING_AUTHENTICATION) {
+ if (sVerbose) {
+ Log.v(TAG, "notifyViewVisibilityChanged(): ignoring in auth pending mode");
}
return;
}
@@ -2342,6 +2348,7 @@
if (!isActiveLocked()) {
return;
}
+ mState = STATE_ACTIVE;
// If authenticate activity closes itself during onCreate(), there is no onStop/onStart
// of app activity. We enforce enter event to re-show fill ui in such case.
// CTS example:
@@ -2406,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) {
@@ -2498,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,
@@ -2830,6 +2798,9 @@
Intent fillInIntent, boolean authenticateInline) {
synchronized (mLock) {
if (sessionId == mSessionId) {
+ if (mRelayoutFix) {
+ mState = STATE_PENDING_AUTHENTICATION;
+ }
final AutofillClient client = getClient();
if (client != null) {
// clear mOnInvisibleCalled and we will see if receive onInvisibleForAutofill()
@@ -2859,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 */
@@ -3563,6 +3512,8 @@
return "UNKNOWN";
case STATE_ACTIVE:
return "ACTIVE";
+ case STATE_PENDING_AUTHENTICATION:
+ return "PENDING_AUTHENTICATION";
case STATE_FINISHED:
return "FINISHED";
case STATE_SHOWING_SAVE_UI:
@@ -3592,7 +3543,12 @@
@GuardedBy("mLock")
private boolean isActiveLocked() {
- return mState == STATE_ACTIVE;
+ return mState == STATE_ACTIVE || isPendingAuthenticationLocked();
+ }
+
+ @GuardedBy("mLock")
+ private boolean isPendingAuthenticationLocked() {
+ return mRelayoutFix && mState == STATE_PENDING_AUTHENTICATION;
}
@GuardedBy("mLock")
@@ -4457,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/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index dc3d323..bb815c0 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -183,7 +183,8 @@
public static final int FLUSH_REASON_VIEW_TREE_APPEARED = 10;
/**
- * After {@link UPSIDE_DOWN_CAKE}, {@link #notifyViewsDisappeared(AutofillId, long[])} wraps
+ * After {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
+ * {@link #notifyViewsDisappeared(AutofillId, long[])} wraps
* the virtual children with a pair of view tree appearing and view tree appeared events.
*/
@ChangeId
diff --git a/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig b/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig
index 7e06f87..5c86feb 100644
--- a/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig
+++ b/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig
@@ -6,3 +6,10 @@
description: "If true, content protection blocklist is mutable and can be updated."
bug: "301658008"
}
+
+flag {
+ name: "parse_groups_config_enabled"
+ namespace: "content_protection"
+ description: "If true, content protection groups config will be parsed."
+ bug: "302187922"
+}
diff --git a/core/java/android/view/displayhash/DisplayHashResultCallback.java b/core/java/android/view/displayhash/DisplayHashResultCallback.java
index 6e3d9a8..927874f 100644
--- a/core/java/android/view/displayhash/DisplayHashResultCallback.java
+++ b/core/java/android/view/displayhash/DisplayHashResultCallback.java
@@ -106,7 +106,7 @@
* {@link android.view.View#generateDisplayHash(String, Rect, Executor,
* DisplayHashResultCallback)} results in an error and cannot generate a display hash.
*
- * @param errorCode One of the values in {@link DisplayHashErrorCode}
+ * @param errorCode the error code
*/
void onDisplayHashError(@DisplayHashErrorCode int errorCode);
}
diff --git a/core/java/android/view/flags/variable_refresh_rate_flags.aconfig b/core/java/android/view/flags/refresh_rate_flags.aconfig
similarity index 78%
rename from core/java/android/view/flags/variable_refresh_rate_flags.aconfig
rename to core/java/android/view/flags/refresh_rate_flags.aconfig
index 13a6f8d..56b5fac 100644
--- a/core/java/android/view/flags/variable_refresh_rate_flags.aconfig
+++ b/core/java/android/view/flags/refresh_rate_flags.aconfig
@@ -19,4 +19,11 @@
namespace: "toolkit"
description: "Feature flag for using expected presentation time of the Choreographer"
bug: "278730197"
+}
+
+flag {
+ name: "set_frame_rate_callback"
+ namespace: "core_graphics"
+ description: "Enable the `setFrameRate` callback"
+ bug: "299946220"
}
\ No newline at end of file
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index a92420a..c5114b9 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -47,7 +47,6 @@
import android.view.MotionEvent.ToolType;
import android.view.View;
import android.view.autofill.AutofillId;
-import android.widget.Editor;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.inputmethod.InputMethodDebug;
@@ -722,9 +721,9 @@
private boolean mIsStylusHandwritingEnabled;
/**
- * Set {@code true} if the {@link Editor} has
+ * Set {@code true} if the {@code Editor} has
* {@link InputMethodManager#startStylusHandwriting stylus handwriting} enabled.
- * {@code false} by default, {@link Editor} must set it {@code true} to indicate that
+ * {@code false} by default, {@code Editor} must set it {@code true} to indicate that
* it supports stylus handwriting.
*
* @param enabled {@code true} if stylus handwriting is enabled.
@@ -736,7 +735,7 @@
}
/**
- * Returns {@code true} when an {@link Editor} has stylus handwriting enabled.
+ * Returns {@code true} when an {@code Editor} has stylus handwriting enabled.
* {@code false} by default.
* @see #setStylusHandwritingEnabled(boolean)
* @see InputMethodManager#isStylusHandwritingAvailable()
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/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 5bb1e93..8159af3 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -100,7 +100,6 @@
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
-import android.widget.Editor;
import android.window.ImeOnBackInvokedDispatcher;
import android.window.WindowOnBackInvokedDispatcher;
@@ -2374,16 +2373,16 @@
* Prepares delegation of starting stylus handwriting session to a different editor in same
* or different window than the view on which initial handwriting stroke was detected.
*
- * Delegation can be used to start stylus handwriting session before the {@link Editor} view or
+ * Delegation can be used to start stylus handwriting session before the {@code Editor} view or
* its {@link InputConnection} is started. Calling this method starts buffering of stylus
* motion events until {@link #acceptStylusHandwritingDelegation(View)} is called, at which
* point the handwriting session can be started and the buffered stylus motion events will be
* delivered to the IME.
* e.g. Delegation can be used when initial handwriting stroke is
- * on a pseudo {@link Editor} like widget (with no {@link InputConnection}) but actual
- * {@link Editor} is on a different window.
+ * on a pseudo {@code Editor} like widget (with no {@link InputConnection}) but actual
+ * {@code Editor} is on a different window.
*
- * <p> Note: If an actual {@link Editor} capable of {@link InputConnection} is being scribbled
+ * <p> Note: If an actual {@code Editor} capable of {@link InputConnection} is being scribbled
* upon using stylus, use {@link #startStylusHandwriting(View)} instead.</p>
*
* @param delegatorView the view that receives initial stylus stroke and delegates it to the
@@ -2402,21 +2401,21 @@
* different window in a different package than the view on which initial handwriting stroke
* was detected.
*
- * Delegation can be used to start stylus handwriting session before the {@link Editor} view or
+ * Delegation can be used to start stylus handwriting session before the {@code Editor} view or
* its {@link InputConnection} is started. Calling this method starts buffering of stylus
* motion events until {@link #acceptStylusHandwritingDelegation(View, String)} is called, at
* which point the handwriting session can be started and the buffered stylus motion events will
* be delivered to the IME.
* e.g. Delegation can be used when initial handwriting stroke is
- * on a pseudo {@link Editor} like widget (with no {@link InputConnection}) but actual
- * {@link Editor} is on a different window in the given package.
+ * on a pseudo {@code Editor} like widget (with no {@link InputConnection}) but actual
+ * {@code Editor} is on a different window in the given package.
*
* <p>Note: If delegator and delegate are in same package use
* {@link #prepareStylusHandwritingDelegation(View)} instead.</p>
*
* @param delegatorView the view that receives initial stylus stroke and delegates it to the
* actual editor. Its window must {@link View#hasWindowFocus have focus}.
- * @param delegatePackageName package name that contains actual {@link Editor} which should
+ * @param delegatePackageName package name that contains actual {@code Editor} which should
* start stylus handwriting session by calling {@link #acceptStylusHandwritingDelegation}.
* @see #prepareStylusHandwritingDelegation(View)
* @see #acceptStylusHandwritingDelegation(View, String)
diff --git a/core/java/android/view/inspector/PropertyReader.java b/core/java/android/view/inspector/PropertyReader.java
index 5be0e3f..78de7e1 100644
--- a/core/java/android/view/inspector/PropertyReader.java
+++ b/core/java/android/view/inspector/PropertyReader.java
@@ -124,7 +124,7 @@
void readObject(int id, @Nullable Object value);
/**
- * Read a color packed into a {@link ColorInt} as a property.
+ * Read a color packed into an int as a property.
*
* @param id Identifier of the property from a {@link PropertyMapper}
* @param value Value of the property
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 26ceea6..1fdd1a5 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -80,6 +80,7 @@
import android.view.autofill.AutofillId;
import android.view.contentcapture.ContentCaptureManager;
import android.view.contentcapture.ContentCaptureSession;
+import android.view.flags.Flags;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
@@ -92,7 +93,6 @@
import android.view.inputmethod.SurroundingText;
import android.view.inspector.InspectableProperty;
import android.view.inspector.InspectableProperty.EnumEntry;
-import android.widget.flags.Flags;
import android.widget.RemoteViews.InteractionHandler;
import com.android.internal.R;
@@ -4518,7 +4518,7 @@
final int overscrollMode = getOverScrollMode();
if (!trackMotionScroll(delta, delta)) {
- if (Flags.platformWidgetHapticScrollFeedback()) {
+ if (Flags.scrollFeedbackApi()) {
initHapticScrollFeedbackProviderIfNotExists();
mHapticScrollFeedbackProvider.onScrollProgress(
event.getDeviceId(), event.getSource(), axis, delta);
@@ -4534,7 +4534,7 @@
float overscroll = (delta - (motionViewRealTop - motionViewPrevTop))
/ ((float) getHeight());
boolean hitTopLimit = delta > 0;
- if (Flags.platformWidgetHapticScrollFeedback()) {
+ if (Flags.scrollFeedbackApi()) {
initHapticScrollFeedbackProviderIfNotExists();
mHapticScrollFeedbackProvider.onScrollLimit(
event.getDeviceId(), event.getSource(), axis,
diff --git a/core/java/android/widget/DifferentialMotionFlingHelper.java b/core/java/android/widget/DifferentialMotionFlingHelper.java
index 95d24ec..ef01c3b 100644
--- a/core/java/android/widget/DifferentialMotionFlingHelper.java
+++ b/core/java/android/widget/DifferentialMotionFlingHelper.java
@@ -21,6 +21,8 @@
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
+import android.widget.flags.FeatureFlags;
+import android.widget.flags.FeatureFlagsImpl;
import com.android.internal.annotations.VisibleForTesting;
@@ -50,6 +52,8 @@
private final FlingVelocityThresholdCalculator mVelocityThresholdCalculator;
private final DifferentialVelocityProvider mVelocityProvider;
+ private final FeatureFlags mWidgetFeatureFlags;
+
@Nullable private VelocityTracker mVelocityTracker;
private float mLastFlingVelocity;
@@ -134,7 +138,8 @@
this(context,
target,
DifferentialMotionFlingHelper::calculateFlingVelocityThresholds,
- DifferentialMotionFlingHelper::getCurrentVelocity);
+ DifferentialMotionFlingHelper::getCurrentVelocity,
+ /* widgetFeatureFlags= */ new FeatureFlagsImpl());
}
@VisibleForTesting
@@ -142,11 +147,13 @@
Context context,
DifferentialMotionFlingTarget target,
FlingVelocityThresholdCalculator velocityThresholdCalculator,
- DifferentialVelocityProvider velocityProvider) {
+ DifferentialVelocityProvider velocityProvider,
+ FeatureFlags widgetFeatureFlags) {
mContext = context;
mTarget = target;
mVelocityThresholdCalculator = velocityThresholdCalculator;
mVelocityProvider = velocityProvider;
+ mWidgetFeatureFlags = widgetFeatureFlags;
}
/**
@@ -156,6 +163,9 @@
* @param axis the axis being processed by the target View.
*/
public void onMotionEvent(MotionEvent event, int axis) {
+ if (!mWidgetFeatureFlags.enablePlatformWidgetDifferentialMotionFling()) {
+ return;
+ }
boolean flingParamsChanged = calculateFlingVelocityThresholds(event, axis);
if (mFlingVelocityThresholds[0] == Integer.MAX_VALUE) {
// Integer.MAX_VALUE means that the device does not support fling for the current
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index e0e72ba..a1ebde7 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -47,8 +47,8 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.AnimationUtils;
+import android.view.flags.Flags;
import android.view.inspector.InspectableProperty;
-import android.widget.flags.Flags;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -1011,14 +1011,14 @@
if (newScrollY != oldScrollY) {
super.scrollTo(mScrollX, newScrollY);
if (hitLimit) {
- if (Flags.platformWidgetHapticScrollFeedback()) {
+ if (Flags.scrollFeedbackApi()) {
initHapticScrollFeedbackProviderIfNotExists();
mHapticScrollFeedbackProvider.onScrollLimit(
event.getDeviceId(), event.getSource(), axis,
/* isStart= */ newScrollY == 0);
}
} else {
- if (Flags.platformWidgetHapticScrollFeedback()) {
+ if (Flags.scrollFeedbackApi()) {
initHapticScrollFeedbackProviderIfNotExists();
mHapticScrollFeedbackProvider.onScrollProgress(
event.getDeviceId(), event.getSource(), axis, delta);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 2c41330..e8281ea 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -567,6 +567,8 @@
private float mShadowDy;
private int mShadowColor;
+ private int mLastOrientation;
+
private boolean mPreDrawRegistered;
private boolean mPreDrawListenerDetached;
@@ -1193,6 +1195,7 @@
mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE;
mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE;
mJustificationMode = Layout.JUSTIFICATION_MODE_NONE;
+ mLastOrientation = getResources().getConfiguration().orientation;
final Resources.Theme theme = context.getTheme();
@@ -4591,6 +4594,15 @@
mFontWeightAdjustment = newConfig.fontWeightAdjustment;
setTypeface(getTypeface());
}
+
+ InputMethodManager imm = getInputMethodManager();
+ // if orientation changed and this TextView is currently served.
+ if (mLastOrientation != newConfig.orientation
+ && imm != null && imm.hasActiveInputConnection(this)) {
+ // EditorInfo.internalImeOptions are out of date.
+ imm.restartInput(this);
+ }
+ mLastOrientation = newConfig.orientation;
}
/**
diff --git a/core/java/android/widget/flags/differential_motion_fling_flags.aconfig b/core/java/android/widget/flags/differential_motion_fling_flags.aconfig
new file mode 100644
index 0000000..79cfe56
--- /dev/null
+++ b/core/java/android/widget/flags/differential_motion_fling_flags.aconfig
@@ -0,0 +1,8 @@
+package: "android.widget.flags"
+
+flag {
+ namespace: "toolkit"
+ name: "enable_platform_widget_differential_motion_fling"
+ description: "Enables differential motion fling in platform widgets"
+ bug: "293332089"
+}
\ No newline at end of file
diff --git a/core/java/android/widget/flags/scroll_view_flags.aconfig b/core/java/android/widget/flags/scroll_view_flags.aconfig
deleted file mode 100644
index f93ade2..0000000
--- a/core/java/android/widget/flags/scroll_view_flags.aconfig
+++ /dev/null
@@ -1,8 +0,0 @@
-package: "android.widget.flags"
-
-flag {
- namespace: "widget"
- name: "platform_widget_haptic_scroll_feedback"
- description: "Enables haptic scroll feedback in platform widgets"
- bug: "287914819"
-}
\ No newline at end of file
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/SystemPerformanceHinter.java b/core/java/android/window/SystemPerformanceHinter.java
index b2c977b..2736b68 100644
--- a/core/java/android/window/SystemPerformanceHinter.java
+++ b/core/java/android/window/SystemPerformanceHinter.java
@@ -17,6 +17,8 @@
package android.window;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.view.Surface.FRAME_RATE_CATEGORY_DEFAULT;
+import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH;
import static android.view.SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN;
import static android.view.SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_SELF;
@@ -29,8 +31,6 @@
import android.util.Log;
import android.view.SurfaceControl;
-import com.android.internal.annotations.VisibleForTesting;
-
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Random;
@@ -148,7 +148,6 @@
* Constructor for the hinter.
* @hide
*/
- @VisibleForTesting
public SystemPerformanceHinter(@NonNull Context context,
@Nullable DisplayRootProvider displayRootProvider,
@Nullable Supplier<SurfaceControl.Transaction> transactionSupplier) {
@@ -208,11 +207,18 @@
boolean transactionChanged = false;
// Per-display flags
if (nowEnabled(oldPerDisplayFlags, newPerDisplayFlags, HINT_SF_FRAME_RATE)) {
- mTransaction.setFrameRateSelectionStrategy(
- mDisplayRootProvider.getRootForDisplay(session.displayId),
+ SurfaceControl displaySurfaceControl = mDisplayRootProvider.getRootForDisplay(
+ session.displayId);
+ mTransaction.setFrameRateSelectionStrategy(displaySurfaceControl,
FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN);
+ // smoothSwitchOnly is false to request a higher framerate, even if it means switching
+ // the display mode will cause would jank on non-VRR devices because keeping a lower
+ // refresh rate would mean a poorer user experience.
+ mTransaction.setFrameRateCategory(
+ displaySurfaceControl, FRAME_RATE_CATEGORY_HIGH, /* smoothSwitchOnly= */ false);
transactionChanged = true;
- Trace.beginAsyncSection("PerfHint-framerate-" + session.reason, session.traceCookie);
+ Trace.beginAsyncSection("PerfHint-framerate-" + session.displayId + "-"
+ + session.reason, session.traceCookie);
}
// Global flags
@@ -226,7 +232,7 @@
Trace.beginAsyncSection("PerfHint-adpf-" + session.reason, session.traceCookie);
}
if (transactionChanged) {
- mTransaction.apply();
+ mTransaction.applyAsyncUnsafe();
}
}
@@ -245,25 +251,32 @@
boolean transactionChanged = false;
// Per-display flags
if (nowDisabled(oldPerDisplayFlags, newPerDisplayFlags, HINT_SF_FRAME_RATE)) {
- mTransaction.setFrameRateSelectionStrategy(
- mDisplayRootProvider.getRootForDisplay(session.displayId),
+ SurfaceControl displaySurfaceControl = mDisplayRootProvider.getRootForDisplay(
+ session.displayId);
+ mTransaction.setFrameRateSelectionStrategy(displaySurfaceControl,
FRAME_RATE_SELECTION_STRATEGY_SELF);
+ // smoothSwitchOnly is false to request a higher framerate, even if it means switching
+ // the display mode will cause would jank on non-VRR devices because keeping a lower
+ // refresh rate would mean a poorer user experience.
+ mTransaction.setFrameRateCategory(displaySurfaceControl, FRAME_RATE_CATEGORY_DEFAULT,
+ /* smoothSwitchOnly= */ false);
transactionChanged = true;
- Trace.endAsyncSection("PerfHint-framerate-" + session.reason, session.traceCookie);
+ Trace.endAsyncSection("PerfHint-framerate-" + session.displayId + "-" + session.reason,
+ session.traceCookie);
}
// Global flags
if (nowDisabled(oldGlobalFlags, newGlobalFlags, HINT_SF_EARLY_WAKEUP)) {
mTransaction.setEarlyWakeupEnd();
transactionChanged = true;
- Trace.endAsyncSection("PerfHint-early_wakeup" + session.reason, session.traceCookie);
+ Trace.endAsyncSection("PerfHint-early_wakeup-" + session.reason, session.traceCookie);
}
if (nowDisabled(oldGlobalFlags, newGlobalFlags, HINT_ADPF)) {
mAdpfSession.sendHint(PerformanceHintManager.Session.CPU_LOAD_RESET);
Trace.endAsyncSection("PerfHint-adpf-" + session.reason, session.traceCookie);
}
if (transactionChanged) {
- mTransaction.apply();
+ mTransaction.applyAsyncUnsafe();
}
}
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index d2a16a3..61f340a 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -29,6 +29,7 @@
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_APPEARING;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
@@ -361,6 +362,15 @@
}
/**
+ * Whether this transition contains any changes to the window hierarchy,
+ * including keyguard visibility.
+ */
+ public boolean hasChangesOrSideEffects() {
+ return !mChanges.isEmpty() || isKeyguardGoingAway()
+ || (mFlags & TRANSIT_FLAG_KEYGUARD_APPEARING) != 0;
+ }
+
+ /**
* Whether this transition includes keyguard going away.
*/
public boolean isKeyguardGoingAway() {
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
new file mode 100644
index 0000000..ccbf4a9
--- /dev/null
+++ b/core/java/android/window/flags/window_surfaces.aconfig
@@ -0,0 +1,19 @@
+package: "com.android.window.flags"
+
+# Project link: https://gantry.corp.google.com/projects/android_platform_window_surfaces/changes
+
+flag {
+ namespace: "window_surfaces"
+ name: "surface_trusted_overlay"
+ description: "Whether to add trusted overlay flag on the SurfaceControl or the InputWindow"
+ 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/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index ec5d4ff..24dc6db 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -22,3 +22,10 @@
description: "Whether the TaskFragment system organizer feature is enabled"
bug: "284050041"
}
+
+flag {
+ namespace: "windowing_sdk"
+ name: "window_state_resize_item_flag"
+ description: "Whether to dispatch window resize through ClientTransaction is enabled"
+ bug: "301870955"
+}
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/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index 58376a7..0801dd8 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -62,16 +62,14 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
+ import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Deque;
-import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
+import java.util.Queue;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.function.Predicate;
-import java.util.regex.Pattern;
/**
* A helper class for {@link android.provider.DocumentsProvider} to perform file operations on local
@@ -89,6 +87,8 @@
DocumentsContract.QUERY_ARG_LAST_MODIFIED_AFTER,
DocumentsContract.QUERY_ARG_MIME_TYPES);
+ private static final int MAX_RESULTS_NUMBER = 23;
+
private static String joinNewline(String... args) {
return TextUtils.join("\n", args);
}
@@ -375,62 +375,53 @@
}
/**
- * This method is similar to
- * {@link DocumentsProvider#queryChildDocuments(String, String[], String)}. This method returns
- * all children documents including hidden directories/files.
- *
- * <p>
- * In a scoped storage world, access to "Android/data" style directories are hidden for privacy
- * reasons. This method may show privacy sensitive data, so its usage should only be in
- * restricted modes.
- *
- * @param parentDocumentId the directory to return children for.
- * @param projection list of {@link Document} columns to put into the
- * cursor. If {@code null} all supported columns should be
- * included.
- * @param sortOrder how to order the rows, formatted as an SQL
- * {@code ORDER BY} clause (excluding the ORDER BY itself).
- * Passing {@code null} will use the default sort order, which
- * may be unordered. This ordering is a hint that can be used to
- * prioritize how data is fetched from the network, but UI may
- * always enforce a specific ordering
- * @throws FileNotFoundException when parent document doesn't exist or query fails
+ * WARNING: this method should really be {@code final}, but for the backward compatibility it's
+ * not; new classes that extend {@link FileSystemProvider} should override
+ * {@link #queryChildDocuments(String, String[], String, boolean)}, not this method.
*/
- protected Cursor queryChildDocumentsShowAll(
- String parentDocumentId, String[] projection, String sortOrder)
- throws FileNotFoundException {
- return queryChildDocuments(parentDocumentId, projection, sortOrder, File -> true);
- }
-
@Override
- public Cursor queryChildDocuments(
- String parentDocumentId, String[] projection, String sortOrder)
+ public Cursor queryChildDocuments(String documentId, String[] projection, String sortOrder)
throws FileNotFoundException {
- // Access to some directories is hidden for privacy reasons.
- return queryChildDocuments(parentDocumentId, projection, sortOrder, this::shouldShow);
+ return queryChildDocuments(documentId, projection, sortOrder, /* includeHidden */ false);
}
- private Cursor queryChildDocuments(
- String parentDocumentId, String[] projection, String sortOrder,
- @NonNull Predicate<File> filter) throws FileNotFoundException {
- final File parent = getFileForDocId(parentDocumentId);
- final MatrixCursor result = new DirectoryCursor(
- resolveProjection(projection), parentDocumentId, parent);
+ /**
+ * This method is similar to {@link #queryChildDocuments(String, String[], String)}, however, it
+ * could return <b>all</b> content of the directory, <b>including restricted (hidden)
+ * directories and files</b>.
+ * <p>
+ * In the scoped storage world, some directories and files (e.g. {@code Android/data/} and
+ * {@code Android/obb/} on the external storage) are hidden for privacy reasons.
+ * Hence, this method may reveal privacy-sensitive data, thus should be used with extra care.
+ */
+ @Override
+ public final Cursor queryChildDocumentsForManage(String documentId, String[] projection,
+ String sortOrder) throws FileNotFoundException {
+ return queryChildDocuments(documentId, projection, sortOrder, /* includeHidden */ true);
+ }
- if (!filter.test(parent)) {
- Log.w(TAG, "No permission to access parentDocumentId: " + parentDocumentId);
+ protected Cursor queryChildDocuments(String documentId, String[] projection, String sortOrder,
+ boolean includeHidden) throws FileNotFoundException {
+ final File parent = getFileForDocId(documentId);
+ final MatrixCursor result = new DirectoryCursor(
+ resolveProjection(projection), documentId, parent);
+
+ if (!parent.isDirectory()) {
+ Log.w(TAG, '"' + documentId + "\" is not a directory");
return result;
}
- if (parent.isDirectory()) {
- for (File file : FileUtils.listFilesOrEmpty(parent)) {
- if (filter.test(file)) {
- includeFile(result, null, file);
- }
- }
- } else {
- Log.w(TAG, "parentDocumentId '" + parentDocumentId + "' is not Directory");
+ if (!includeHidden && shouldHideDocument(documentId)) {
+ Log.w(TAG, "Queried directory \"" + documentId + "\" is hidden");
+ return result;
}
+
+ for (File file : FileUtils.listFilesOrEmpty(parent)) {
+ if (!includeHidden && shouldHideDocument(file)) continue;
+
+ includeFile(result, null, file);
+ }
+
return result;
}
@@ -452,23 +443,29 @@
*
* @see ContentResolver#EXTRA_HONORED_ARGS
*/
- protected final Cursor querySearchDocuments(
- File folder, String[] projection, Set<String> exclusion, Bundle queryArgs)
- throws FileNotFoundException {
+ protected final Cursor querySearchDocuments(File folder, String[] projection,
+ Set<String> exclusion, Bundle queryArgs) throws FileNotFoundException {
final MatrixCursor result = new MatrixCursor(resolveProjection(projection));
- final List<File> pending = new ArrayList<>();
- pending.add(folder);
- while (!pending.isEmpty() && result.getCount() < 24) {
- final File file = pending.remove(0);
- if (shouldHide(file)) continue;
+
+ // We'll be a running a BFS here.
+ final Queue<File> pending = new ArrayDeque<>();
+ pending.offer(folder);
+
+ while (!pending.isEmpty() && result.getCount() < MAX_RESULTS_NUMBER) {
+ final File file = pending.poll();
+
+ // Skip hidden documents (both files and directories)
+ if (shouldHideDocument(file)) continue;
if (file.isDirectory()) {
for (File child : FileUtils.listFilesOrEmpty(file)) {
- pending.add(child);
+ pending.offer(child);
}
}
- if (!exclusion.contains(file.getAbsolutePath()) && matchSearchQueryArguments(file,
- queryArgs)) {
+
+ if (exclusion.contains(file.getAbsolutePath())) continue;
+
+ if (matchSearchQueryArguments(file, queryArgs)) {
includeFile(result, null, file);
}
}
@@ -612,26 +609,23 @@
final int flagIndex = ArrayUtils.indexOf(columns, Document.COLUMN_FLAGS);
if (flagIndex != -1) {
+ final boolean isDir = mimeType.equals(Document.MIME_TYPE_DIR);
int flags = 0;
if (file.canWrite()) {
- if (mimeType.equals(Document.MIME_TYPE_DIR)) {
+ flags |= Document.FLAG_SUPPORTS_DELETE;
+ flags |= Document.FLAG_SUPPORTS_RENAME;
+ flags |= Document.FLAG_SUPPORTS_MOVE;
+ if (isDir) {
flags |= Document.FLAG_DIR_SUPPORTS_CREATE;
- flags |= Document.FLAG_SUPPORTS_DELETE;
- flags |= Document.FLAG_SUPPORTS_RENAME;
- flags |= Document.FLAG_SUPPORTS_MOVE;
-
- if (shouldBlockFromTree(docId)) {
- flags |= Document.FLAG_DIR_BLOCKS_OPEN_DOCUMENT_TREE;
- }
-
} else {
flags |= Document.FLAG_SUPPORTS_WRITE;
- flags |= Document.FLAG_SUPPORTS_DELETE;
- flags |= Document.FLAG_SUPPORTS_RENAME;
- flags |= Document.FLAG_SUPPORTS_MOVE;
}
}
+ if (isDir && shouldBlockDirectoryFromTree(docId)) {
+ flags |= Document.FLAG_DIR_BLOCKS_OPEN_DOCUMENT_TREE;
+ }
+
if (mimeType.startsWith("image/")) {
flags |= Document.FLAG_SUPPORTS_THUMBNAIL;
}
@@ -664,22 +658,36 @@
return row;
}
- private static final Pattern PATTERN_HIDDEN_PATH = Pattern.compile(
- "(?i)^/storage/[^/]+/(?:[0-9]+/)?Android/(?:data|obb|sandbox)$");
+ /**
+ * Some providers may want to restrict access to certain directories and files,
+ * e.g. <i>"Android/data"</i> and <i>"Android/obb"</i> on the shared storage for
+ * privacy reasons.
+ * Such providers should override this method.
+ */
+ protected boolean shouldHideDocument(@NonNull String documentId)
+ throws FileNotFoundException {
+ return false;
+ }
/**
- * In a scoped storage world, access to "Android/data" style directories are
- * hidden for privacy reasons.
+ * A variant of the {@link #shouldHideDocument(String)} that takes a {@link File} instead of
+ * a {@link String} {@code documentId}.
+ *
+ * @see #shouldHideDocument(String)
*/
- protected boolean shouldHide(@NonNull File file) {
- return (PATTERN_HIDDEN_PATH.matcher(file.getAbsolutePath()).matches());
+ protected final boolean shouldHideDocument(@NonNull File document)
+ throws FileNotFoundException {
+ return shouldHideDocument(getDocIdForFile(document));
}
- private boolean shouldShow(@NonNull File file) {
- return !shouldHide(file);
- }
-
- protected boolean shouldBlockFromTree(@NonNull String docId) {
+ /**
+ * @return if the directory that should be blocked from being selected when the user launches
+ * an {@link Intent#ACTION_OPEN_DOCUMENT_TREE} intent.
+ *
+ * @see Document#FLAG_DIR_BLOCKS_OPEN_DOCUMENT_TREE
+ */
+ protected boolean shouldBlockDirectoryFromTree(@NonNull String documentId)
+ throws FileNotFoundException {
return false;
}
diff --git a/core/java/com/android/internal/display/RefreshRateSettingsUtils.java b/core/java/com/android/internal/display/RefreshRateSettingsUtils.java
new file mode 100644
index 0000000..e55c641
--- /dev/null
+++ b/core/java/com/android/internal/display/RefreshRateSettingsUtils.java
@@ -0,0 +1,56 @@
+/*
+ * 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.internal.display;
+
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.util.Log;
+import android.view.Display;
+
+/**
+ * Constants and utility methods for refresh rate settings.
+ */
+public class RefreshRateSettingsUtils {
+
+ private static final String TAG = "RefreshRateSettingsUtils";
+
+ public static final float DEFAULT_REFRESH_RATE = 60f;
+
+ /**
+ * Find the highest refresh rate among all the modes of the default display.
+ *
+ * @param context The context
+ * @return The highest refresh rate
+ */
+ public static float findHighestRefreshRateForDefaultDisplay(Context context) {
+ final DisplayManager dm = context.getSystemService(DisplayManager.class);
+ final Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
+
+ if (display == null) {
+ Log.w(TAG, "No valid default display device");
+ return DEFAULT_REFRESH_RATE;
+ }
+
+ float maxRefreshRate = DEFAULT_REFRESH_RATE;
+ for (Display.Mode mode : display.getSupportedModes()) {
+ if (mode.getRefreshRate() > maxRefreshRate) {
+ maxRefreshRate = mode.getRefreshRate();
+ }
+ }
+ return maxRefreshRate;
+ }
+}
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/OWNERS b/core/java/com/android/internal/foldables/OWNERS
new file mode 100644
index 0000000..6ce1ee4
--- /dev/null
+++ b/core/java/com/android/internal/foldables/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/display/OWNERS
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/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 1bfb51c..6e836e0 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -837,25 +837,41 @@
@WorkerThread
private void updateProperties(DeviceConfig.Properties properties) {
- mSamplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY,
- DEFAULT_SAMPLING_INTERVAL);
- mTraceThresholdMissedFrames = properties.getInt(SETTINGS_THRESHOLD_MISSED_FRAMES_KEY,
- DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES);
- mTraceThresholdFrameTimeMillis = properties.getInt(
- SETTINGS_THRESHOLD_FRAME_TIME_MILLIS_KEY,
- DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS);
- // Never allow the debug overlay to be used on user builds
- boolean debugOverlayEnabled = Build.IS_DEBUGGABLE && properties.getBoolean(
- SETTINGS_DEBUG_OVERLAY_ENABLED_KEY,
- DEFAULT_DEBUG_OVERLAY_ENABLED);
- if (debugOverlayEnabled && mDebugOverlay == null) {
- mDebugOverlay = new InteractionMonitorDebugOverlay(mLock, mDebugBgColor, mDebugYOffset);
- } else if (!debugOverlayEnabled && mDebugOverlay != null) {
- mDebugOverlay.dispose();
- mDebugOverlay = null;
+ for (String property : properties.getKeyset()) {
+ switch (property) {
+ case SETTINGS_SAMPLING_INTERVAL_KEY:
+ mSamplingInterval = properties.getInt(property, DEFAULT_SAMPLING_INTERVAL);
+ break;
+ case SETTINGS_THRESHOLD_MISSED_FRAMES_KEY:
+ mTraceThresholdMissedFrames =
+ properties.getInt(property, DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES);
+ break;
+ case SETTINGS_THRESHOLD_FRAME_TIME_MILLIS_KEY:
+ mTraceThresholdFrameTimeMillis =
+ properties.getInt(property, DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS);
+ break;
+ case SETTINGS_ENABLED_KEY:
+ mEnabled = properties.getBoolean(property, DEFAULT_ENABLED);
+ break;
+ case SETTINGS_DEBUG_OVERLAY_ENABLED_KEY:
+ // Never allow the debug overlay to be used on user builds
+ boolean debugOverlayEnabled = Build.IS_DEBUGGABLE
+ && properties.getBoolean(property, DEFAULT_DEBUG_OVERLAY_ENABLED);
+ if (debugOverlayEnabled && mDebugOverlay == null) {
+ mDebugOverlay = new InteractionMonitorDebugOverlay(
+ mLock, mDebugBgColor, mDebugYOffset);
+ } else if (!debugOverlayEnabled && mDebugOverlay != null) {
+ mDebugOverlay.dispose();
+ mDebugOverlay = null;
+ }
+ break;
+ default:
+ if (DEBUG) {
+ Log.d(TAG, "Got a change event for an unknown property: "
+ + property + " => " + properties.getString(property, ""));
+ }
+ }
}
- // The memory visibility is powered by the volatile field, mEnabled.
- mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED);
}
@VisibleForTesting
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 1be916f..8566263 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -290,11 +290,12 @@
};
private Consumer<Boolean> mCrossWindowBlurEnabledListener;
+ private final WearGestureInterceptionDetector mWearGestureInterceptionDetector;
+
DecorView(Context context, int featureId, PhoneWindow window,
WindowManager.LayoutParams params) {
super(context);
mFeatureId = featureId;
-
mShowInterpolator = AnimationUtils.loadInterpolator(context,
android.R.interpolator.linear_out_slow_in);
mHideInterpolator = AnimationUtils.loadInterpolator(context,
@@ -314,6 +315,11 @@
updateLogTag(params);
mLegacyNavigationBarBackgroundPaint.setColor(Color.BLACK);
+
+ mWearGestureInterceptionDetector =
+ WearGestureInterceptionDetector.isEnabled(context)
+ ? new WearGestureInterceptionDetector(context, this)
+ : null;
}
void setBackgroundFallback(@Nullable Drawable fallbackDrawable) {
@@ -544,6 +550,18 @@
}
}
+ ViewRootImpl viewRootImpl = getViewRootImpl();
+ if (viewRootImpl != null && mWearGestureInterceptionDetector != null) {
+ boolean wasIntercepting = mWearGestureInterceptionDetector.isIntercepting();
+ boolean intercepting = mWearGestureInterceptionDetector.onInterceptTouchEvent(event);
+ if (wasIntercepting != intercepting) {
+ viewRootImpl.updateDecorViewGestureInterception(intercepting);
+ }
+ if (intercepting) {
+ return true;
+ }
+ }
+
if (!SWEEP_OPEN_MENU) {
return false;
}
diff --git a/core/java/com/android/internal/policy/WearGestureInterceptionDetector.java b/core/java/com/android/internal/policy/WearGestureInterceptionDetector.java
new file mode 100644
index 0000000..6fd5018
--- /dev/null
+++ b/core/java/com/android/internal/policy/WearGestureInterceptionDetector.java
@@ -0,0 +1,211 @@
+/*
+ * 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.internal.policy;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.TypedArray;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+
+/**
+ * Wear-specific gesture interception detector to be installed at DecorView, for compatibility of
+ * apps depending on legacy SwipeDismissLayout behavior.
+ *
+ * <p>Results of the detector will be used by {@code DecorView} to intercept motion events. The
+ * interception state will also be sent to {@code android.view.ViewRootImpl} and {@code
+ * com.android.server.wm.DisplayContent} through {@code android.view.IWindowSession}.
+ *
+ * <p>SystemUI can register {@code android.view.IDecorViewGestureListener} to listen for the result
+ * of the detector. The result will be valid for between a pair of touch down/up events.
+ */
+public class WearGestureInterceptionDetector {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "WearGestureInterceptionDetector";
+
+ private final DecorView mInstalledDecorView;
+ private final float mTouchSlop;
+ private final float mSwipingStartThreshold;
+ private boolean mSwiping;
+
+ private float mDownX;
+ private float mDownY;
+ private int mActivePointerId;
+ private boolean mDiscardIntercept;
+
+ WearGestureInterceptionDetector(Context context, DecorView installedDecorView) {
+ mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+ mInstalledDecorView = installedDecorView;
+ mSwipingStartThreshold = mTouchSlop * 2;
+ }
+
+ /** Check if this gesture interception detector should be enabled. */
+ public static boolean isEnabled(Context context) {
+ PackageManager pm = context.getPackageManager();
+ if (!pm.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+ return false;
+ }
+
+ // Compatibility check for flag that disables legacy SwipeDismissLayout.
+ TypedArray windowAttr =
+ context.obtainStyledAttributes(new int[] {android.R.attr.windowSwipeToDismiss});
+ boolean windowSwipeToDismiss = true;
+ if (windowAttr.getIndexCount() > 0) {
+ windowSwipeToDismiss = windowAttr.getBoolean(0, true);
+ }
+ windowAttr.recycle();
+ return windowSwipeToDismiss;
+ }
+
+ private boolean isPointerIndexValid(MotionEvent ev) {
+ int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ if (pointerIndex == -1) {
+ if (DEBUG) {
+ Log.e(TAG, "Invalid pointer index: ignoring.");
+ }
+ mDiscardIntercept = true;
+ return false;
+ }
+ return true;
+ }
+
+ private void updateSwiping(MotionEvent ev) {
+ if (mSwiping) {
+ return;
+ }
+ float deltaX = ev.getRawX() - mDownX;
+ float deltaY = ev.getRawY() - mDownY;
+ // Check if we have left the touch slop area.
+ if ((deltaX * deltaX) + (deltaY * deltaY) > (mTouchSlop * mTouchSlop)) {
+ mSwiping = deltaX > mSwipingStartThreshold && Math.abs(deltaY) < Math.abs(deltaX);
+ }
+ }
+
+ private void updateDiscardIntercept(MotionEvent ev) {
+ if (!mSwiping) {
+ // Don't look at canScroll until we have passed the touch slop
+ return;
+ }
+ if (mDiscardIntercept) {
+ return;
+ }
+ final boolean checkLeft = mDownX < ev.getRawX();
+ final float x = ev.getX(mActivePointerId);
+ final float y = ev.getY(mActivePointerId);
+ if (canScroll(mInstalledDecorView, false, checkLeft, x, y)) {
+ mDiscardIntercept = true;
+ }
+ }
+
+ /** Resets internal members when canceling. */
+ private void resetMembers() {
+ mDownX = 0;
+ mDownY = 0;
+ mSwiping = false;
+ mDiscardIntercept = false;
+ }
+
+ /** Should we intercept the MotionEvent for system gesture? */
+ public boolean isIntercepting() {
+ return !mDiscardIntercept && mSwiping;
+ }
+
+ /** Tests if the MotionEvent should be intercepted */
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ switch (ev.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ resetMembers();
+ mDownX = ev.getRawX();
+ mDownY = ev.getRawY();
+ mActivePointerId = ev.getPointerId(0);
+ break;
+ case MotionEvent.ACTION_POINTER_DOWN:
+ mActivePointerId = ev.getPointerId(ev.getActionIndex());
+ break;
+ case MotionEvent.ACTION_POINTER_UP:
+ int associatedPointerIndex = ev.getActionIndex();
+ if (ev.getPointerId(associatedPointerIndex) == mActivePointerId) {
+ // This was our active pointer going up.
+ // Choose the first available pointer index.
+ int newActionIndex = associatedPointerIndex == 0 ? 1 : 0;
+ mActivePointerId = ev.getPointerId(newActionIndex);
+ }
+ break;
+ case MotionEvent.ACTION_MOVE:
+ if (mDiscardIntercept) {
+ break;
+ }
+ if (!isPointerIndexValid(ev)) {
+ break;
+ }
+ updateSwiping(ev);
+ updateDiscardIntercept(ev);
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ resetMembers();
+ break;
+ }
+ return isIntercepting();
+ }
+
+ /**
+ * Tests scroll-ability within child views of v in the direction of dx.
+ *
+ * @param v View to test for horizontal scroll-ability
+ * @param checkSelf Whether the view v passed should itself be checked for scroll-ability
+ * (true), or just its children (false).
+ * @param checkLeft Which direction to check? Left = true, right = false.
+ * @param x X coordinate of the active touch point
+ * @param y Y coordinate of the active touch point
+ * @return true if child views of v can be scrolled by delta of dx.
+ */
+ private boolean canScroll(View v, boolean checkSelf, boolean checkLeft, float x, float y) {
+ if (v instanceof ViewGroup) {
+ final ViewGroup group = (ViewGroup) v;
+ final int scrollX = v.getScrollX();
+ final int scrollY = v.getScrollY();
+ final int count = group.getChildCount();
+ for (int i = count - 1; i >= 0; i--) {
+ final View child = group.getChildAt(i);
+
+ if (x + scrollX < child.getLeft()
+ || x + scrollX >= child.getRight()
+ || y + scrollY < child.getTop()
+ || y + scrollY >= child.getBottom()) {
+ // This child is out of bound, don't bother checking.
+ continue;
+ }
+
+ // Recursively check until finding the first scrollable or none is scrollable.
+ if (canScroll(
+ /* view= */ child,
+ /* checkSelf= */ true,
+ /* checkLeft= */ checkLeft,
+ /* x= */ x + scrollX - child.getLeft(),
+ /* y= */ y + scrollY - child.getTop())) {
+ return true;
+ }
+ }
+ }
+
+ return checkSelf && v.canScrollHorizontally(checkLeft ? -1 : 1);
+ }
+}
diff --git a/core/java/com/android/internal/view/FloatingActionMode.java b/core/java/com/android/internal/view/FloatingActionMode.java
index 06e69f2..fd435d0 100644
--- a/core/java/com/android/internal/view/FloatingActionMode.java
+++ b/core/java/com/android/internal/view/FloatingActionMode.java
@@ -222,6 +222,7 @@
private boolean isContentRectWithinBounds() {
mContext.getDisplayNoVerify().getRealSize(mDisplaySize);
mScreenRect.set(0, 0, mDisplaySize.x, mDisplaySize.y);
+ mScreenRect.offset(mRootViewPositionOnScreen[0], mRootViewPositionOnScreen[1]);
return intersectsClosed(mContentRectOnScreen, mScreenRect)
&& intersectsClosed(mContentRectOnScreen, mViewRectOnScreen);
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index b5d70d3..50253cf 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -1315,7 +1315,16 @@
ALOGI("VM exiting with result code %d.", code);
onExit(code);
}
+
+#ifdef __ANDROID_CLANG_COVERAGE__
+ // When compiled with coverage, a function is registered with atexit to call
+ // `__llvm_profile_write_file` when the process exit.
+ // For Clang code coverage to work, call exit instead of _exit to run hooks
+ // registered with atexit.
+ ::exit(code);
+#else
::_exit(code);
+#endif
}
void AndroidRuntime::onVmCreated(JNIEnv* env)
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 9384f41..178c0d0 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;
@@ -469,9 +470,10 @@
}
}
-static void nativeApplyTransaction(JNIEnv* env, jclass clazz, jlong transactionObj, jboolean sync) {
+static void nativeApplyTransaction(JNIEnv* env, jclass clazz, jlong transactionObj, jboolean sync,
+ jboolean oneWay) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
- transaction->apply(sync);
+ transaction->apply(sync, oneWay);
}
static void nativeMergeTransaction(JNIEnv* env, jclass clazz,
@@ -961,10 +963,11 @@
}
static void nativeSetFrameRateCategory(JNIEnv* env, jclass clazz, jlong transactionObj,
- jlong nativeObject, jint category) {
+ jlong nativeObject, jint category,
+ jboolean smoothSwitchOnly) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
const auto ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
- transaction->setFrameRateCategory(ctrl, static_cast<int8_t>(category));
+ transaction->setFrameRateCategory(ctrl, static_cast<int8_t>(category), smoothSwitchOnly);
}
static void nativeSetFrameRateSelectionStrategy(JNIEnv* env, jclass clazz, jlong transactionObj,
@@ -1230,6 +1233,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);
@@ -2119,7 +2123,7 @@
(void*)nativeSetDefaultBufferSize},
{"nativeCreateTransaction", "()J",
(void*)nativeCreateTransaction },
- {"nativeApplyTransaction", "(JZ)V",
+ {"nativeApplyTransaction", "(JZZ)V",
(void*)nativeApplyTransaction },
{"nativeGetNativeTransactionFinalizer", "()J",
(void*)nativeGetNativeTransactionFinalizer },
@@ -2178,7 +2182,7 @@
(void*)nativeSetFrameRate },
{"nativeSetDefaultFrameRateCompatibility", "(JJI)V",
(void*)nativeSetDefaultFrameRateCompatibility},
- {"nativeSetFrameRateCategory", "(JJI)V",
+ {"nativeSetFrameRateCategory", "(JJIZ)V",
(void*)nativeSetFrameRateCategory},
{"nativeSetFrameRateSelectionStrategy", "(JJI)V",
(void*)nativeSetFrameRateSelectionStrategy},
@@ -2393,6 +2397,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/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 6c93680..4732702 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -98,6 +98,7 @@
// Settings for font scaling
optional SettingProto accessibility_font_scaling_has_been_changed = 51 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto accessibility_force_invert_color_enabled = 52 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto accessibility_magnification_gesture = 53 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional Accessibility accessibility = 2;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e34e423..a54a563 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -48,6 +48,7 @@
<protected-broadcast android:name="android.intent.action.CANCEL_ENABLE_ROLLBACK" />
<protected-broadcast android:name="android.intent.action.ROLLBACK_COMMITTED" />
<protected-broadcast android:name="android.intent.action.PACKAGE_RESTARTED" />
+ <protected-broadcast android:name="android.intent.action.PACKAGE_UNSTOPPED" />
<protected-broadcast android:name="android.intent.action.PACKAGE_DATA_CLEARED" />
<protected-broadcast android:name="android.intent.action.PACKAGE_FIRST_LAUNCH" />
<protected-broadcast android:name="android.intent.action.PACKAGE_NEEDS_INTEGRITY_VERIFICATION" />
@@ -2107,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" />
@@ -7232,6 +7233,25 @@
android:description="@string/permdesc_fullScreenIntent"
android:protectionLevel="normal|appop" />
+ <!-- @SystemApi Required for the privileged assistant apps targeting
+ {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}
+ that receive voice trigger from a 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" />
+
+ <!-- @SystemApi Required for the privileged assistant apps targeting
+ {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}
+ that receive training data from the sandboxed hotword detection service or visual
+ query detection service.
+ <p>Protection level: internal|appop
+ @FlaggedApi("android.permission.flags.voice_activation_permission_apis")
+ @hide -->
+ <permission android:name="android.permission.RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA"
+ android:protectionLevel="internal|appop" />
+
<!-- @SystemApi Allows requesting the framework broadcast the
{@link Intent#ACTION_DEVICE_CUSTOMIZATION_READY} intent.
@hide -->
diff --git a/core/res/res/drawable-nodpi/usb_cable_unknown_issue.xml b/core/res/res/drawable-nodpi/usb_cable_unknown_issue.xml
new file mode 100644
index 0000000..dddad81
--- /dev/null
+++ b/core/res/res/drawable-nodpi/usb_cable_unknown_issue.xml
@@ -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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="20"
+ android:viewportHeight="20">
+ <path
+ android:pathData="M15.333,5.333V4.667C15.333,4.3 15.033,4 14.667,4L13.333,4C12.967,4 12.667,4.3 12.667,4.667V5.333H12V8C12,8.367 12.3,8.667 12.667,8.667H13.333L13.333,13.333C13.333,14.067 12.733,14.667 12,14.667C11.267,14.667 10.667,14.067 10.667,13.333L10.667,11.333V6.667C10.667,5.193 9.473,4 8,4C6.527,4 5.333,5.193 5.333,6.667L5.333,11.333H4.667C4.3,11.333 4,11.633 4,12L4,14.667H4.667V15.333C4.667,15.7 4.967,16 5.333,16H6.667C7.033,16 7.333,15.7 7.333,15.333V14.667H8L8,12C8,11.633 7.7,11.333 7.333,11.333H6.667L6.667,6.667C6.667,5.933 7.267,5.333 8,5.333C8.733,5.333 9.333,5.933 9.333,6.667V11.333L9.333,13.333C9.333,14.807 10.527,16 12,16C13.473,16 14.667,14.807 14.667,13.333L14.667,8.667H15.333C15.7,8.667 16,8.367 16,8V5.333H15.333Z"
+ android:fillColor="#FFFFFFFF"
+ android:fillType="evenOdd"/>
+</vector>
+
+
diff --git a/core/res/res/layout/autofill_save.xml b/core/res/res/layout/autofill_save.xml
index 8b6c901..27f8138 100644
--- a/core/res/res/layout/autofill_save.xml
+++ b/core/res/res/layout/autofill_save.xml
@@ -60,10 +60,11 @@
android:gravity="center"
android:textAppearance="@style/AutofillSaveUiTitle">
</TextView>
- <LinearLayout
+ <FrameLayout
android:id="@+id/autofill_save_custom_subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:minHeight="0dp"
android:visibility="gone"/>
</LinearLayout>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index a49e547..6f879e4 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -674,7 +674,7 @@
<string name="device_unlock_notification_name" msgid="2632928999862915709">"ডিভাইস আনলক করুন"</string>
<string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"অন্য কোনওভাবে আনলক করার চেষ্টা করুন"</string>
<string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"আপনার \'ফিঙ্গারপ্রিন্ট\' শনাক্ত করা না গেলে \'ফেস আনলক\' ব্যবহার করুন, যেমন যখন আপনার আঙুল ভিজে থাকে"</string>
- <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"আপনার মুখ শনাক্ত করা না গেলে \'ফিঙ্গারপ্রিন্ট আনলক\' ব্যবহার করুন, যেমন যখন পর্যাপ্ত আলো নেই"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"পর্যাপ্ত আলো না থাকার পরিস্থিতিতে, আপনার মুখ শনাক্ত করা না গেলে \'ফিঙ্গারপ্রিন্ট আনলক\' ব্যবহার করুন"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"ফেস আনলক"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"\'ফেস আনলক\' ফিচার ব্যবহার করার ক্ষেত্রে হওয়া সমস্যা"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"আপনার ফেস মডেল মুছে দেওয়ার জন্য ট্যাপ করুন এবং তারপরে আবার ফেস যোগ করুন"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index a897170..2150324 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -476,7 +476,7 @@
<string name="permlab_accessLocationExtraCommands" msgid="5162339812057983988">"atzitu kokapen-hornitzaileen komando gehigarriak"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"Kokapen-hornitzailearen agindu gehigarriak erabiltzeko baimena ematen die aplikazioei. Horrela, agian aplikazioek GPSaren edo bestelako kokapenaren iturburuen funtzionamenduan eragina izan dezakete."</string>
<string name="permlab_accessFineLocation" msgid="6426318438195622966">"lortu kokapen zehatza aurreko planoan bakarrik"</string>
- <string name="permdesc_accessFineLocation" msgid="6732174080240016335">"Abian denean, aplikazioak kokapen zehatza lor dezake kokapen-zerbitzuen bidez. Aplikazioak kokapena lortu ahal izateko, kokapen-zerbitzuek aktibatuta egon behar dute gailuan. Bateria-erabilera areagotzen du horrek."</string>
+ <string name="permdesc_accessFineLocation" msgid="6732174080240016335">"Abian denean, aplikazioak kokapen zehatza lor dezake kokapen-zerbitzuen bidez. Aplikazioak kokapena lortu ahal izateko, kokapen-zerbitzuek aktibatuta egon behar dute gailuan. Agian bateria gehiago erabiliko du."</string>
<string name="permlab_accessCoarseLocation" msgid="1561042925407799741">"atzitu gutxi gorabeherako kokapena aurreko planoan bakarrik"</string>
<string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"Abian denean, aplikazioak gutxi gorabeherako kokapena lor dezake kokapen-zerbitzuen bidez. Aplikazioak kokapena lortu ahal izateko, kokapen-zerbitzuek aktibatuta egon behar dute gailuan."</string>
<string name="permlab_accessBackgroundLocation" msgid="1721164702777366138">"atzitu kokapena atzeko planoan"</string>
@@ -2137,7 +2137,7 @@
<string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"Aplikazioak ez du grabatzeko baimenik, baina baliteke audioa grabatzea USB bidezko gailu horren bidez."</string>
<string name="accessibility_system_action_home_label" msgid="3234748160850301870">"Pantaila nagusia"</string>
<string name="accessibility_system_action_back_label" msgid="4205361367345537608">"Atzera"</string>
- <string name="accessibility_system_action_recents_label" msgid="4782875610281649728">"Erabilitako azken aplikazioak"</string>
+ <string name="accessibility_system_action_recents_label" msgid="4782875610281649728">"Azkenaldian erabilitako aplikazioak"</string>
<string name="accessibility_system_action_notifications_label" msgid="6083767351772162010">"Jakinarazpenak"</string>
<string name="accessibility_system_action_quick_settings_label" msgid="4583900123506773783">"Ezarpen bizkorrak"</string>
<string name="accessibility_system_action_power_dialog_label" msgid="8095341821683910781">"Piztu edo itzaltzeko leihoa"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 2867516..d7d29a4 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -299,8 +299,8 @@
<string name="android_system_label" msgid="5974767339591067210">"Système Android"</string>
<string name="user_owner_label" msgid="8628726904184471211">"Passer au profil personnel"</string>
<string name="managed_profile_label" msgid="7316778766973512382">"Passer au profil pro"</string>
- <string name="user_owner_app_label" msgid="1553595155465750298">"Passer au <xliff:g id="APP_NAME">%1$s</xliff:g> personnel"</string>
- <string name="managed_profile_app_label" msgid="367401088383965725">"Passer au <xliff:g id="APP_NAME">%1$s</xliff:g> professionnel"</string>
+ <string name="user_owner_app_label" msgid="1553595155465750298">"Passer à l\'application <xliff:g id="APP_NAME">%1$s</xliff:g> personnelle"</string>
+ <string name="managed_profile_app_label" msgid="367401088383965725">"Passer à l\'application <xliff:g id="APP_NAME">%1$s</xliff:g> professionnelle"</string>
<string name="permgrouplab_contacts" msgid="4254143639307316920">"Contacts"</string>
<string name="permgroupdesc_contacts" msgid="9163927941244182567">"accéder à vos contacts"</string>
<string name="permgrouplab_location" msgid="1858277002233964394">"Position"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 5a98ef8..d2b6dc8 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -674,7 +674,7 @@
<string name="device_unlock_notification_name" msgid="2632928999862915709">"기기 잠금 해제"</string>
<string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"다른 잠금 해제 방법 사용"</string>
<string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"손가락에 물기가 있는 등 지문이 인식되지 않을 때는 얼굴 인식 잠금 해제를 사용하세요."</string>
- <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"충분히 밝지 않은 경우 등 얼굴이 인식되지 않을 때는 지문 잠금 해제를 사용하세요."</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"주변이 어두운 경우 등 얼굴이 인식되지 않을 때는 지문 잠금 해제를 사용해 보세요."</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"얼굴 인식 잠금 해제"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"얼굴 인식 잠금 해제 문제"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"탭하여 얼굴 모델을 삭제한 후 다시 얼굴을 추가하세요"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 5f3b315..485e9b6 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -674,7 +674,7 @@
<string name="device_unlock_notification_name" msgid="2632928999862915709">"डिव्हाइस अनलॉक"</string>
<string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"अनलॉक करण्याची दुसरी पद्धत वापरून पहा"</string>
<string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"तुमचे फिंगरप्रिंट ओळखले जात नाही, तेव्हा फेस अनलॉक वापरा, जसे की तुमची बोटे ओली असताना"</string>
- <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"तुमचा चेहरा ओळखला जात नाही, तेव्हा फिंगरप्रिंट अनलॉक वापरा, जसे की पुरेसा प्रकाश नसताना"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"पुरेसा प्रकाश नसणे इत्यादिमुळे तुमचा चेहरा ओळखला जात नाही, अशावेळी फिंगरप्रिंट अनलॉक वापरा"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"फेस अनलॉक"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"फेस अनलॉकसंबंधित समस्या"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"फेस मॉडेल हटवण्यासाठी टॅप करा, त्यानंतर तुमचा चेहरा पुन्हा जोडा"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 9583c0e5..628627d 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -675,7 +675,7 @@
<string name="device_unlock_notification_name" msgid="2632928999862915709">"Desbloqueio do dispositivo"</string>
<string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Tente desbloquear de outra maneira"</string>
<string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Use o Desbloqueio facial quando sua impressão digital não for reconhecida, como quando seus dedos estiverem molhados"</string>
- <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Use o Desbloqueio por impressão digital quando seu rosto não for reconhecido, como quando não houver luz suficiente"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Use o Desbloqueio por impressão digital quando seu rosto não for reconhecido, se estiver escuro, por exemplo"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Desbloqueio facial"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Problema com o Desbloqueio facial"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Toque para excluir seu modelo de rosto e crie um novo"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 9583c0e5..628627d 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -675,7 +675,7 @@
<string name="device_unlock_notification_name" msgid="2632928999862915709">"Desbloqueio do dispositivo"</string>
<string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Tente desbloquear de outra maneira"</string>
<string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Use o Desbloqueio facial quando sua impressão digital não for reconhecida, como quando seus dedos estiverem molhados"</string>
- <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Use o Desbloqueio por impressão digital quando seu rosto não for reconhecido, como quando não houver luz suficiente"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Use o Desbloqueio por impressão digital quando seu rosto não for reconhecido, se estiver escuro, por exemplo"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Desbloqueio facial"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Problema com o Desbloqueio facial"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Toque para excluir seu modelo de rosto e crie um novo"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 8573c20c..aa22aea 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -673,8 +673,8 @@
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"వేలిముద్ర చిహ్నం"</string>
<string name="device_unlock_notification_name" msgid="2632928999862915709">"పరికర అన్లాక్"</string>
<string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"అన్లాక్ చేయడానికి మరొక మార్గాన్ని ట్రై చేయండి"</string>
- <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"మీ వేలిముద్ర గుర్తించబడనప్పుడు, మీ వేళ్లు తడిగా ఉన్నప్పుడు ఫేస్ అన్లాక్ను ఉపయోగించండి"</string>
- <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"మీ ఫేస్ గుర్తించబడనప్పుడు, తగినంత వెలుతురు లేనప్పుడు వేలిముద్ర అన్లాక్ను ఉపయోగించండి"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"మీ వేళ్లు తడిగా ఉండటం లేక ఇతరత్రా కారణాల వల్ల మీ వేలిముద్రను గుర్తించకపోతే, ఫేస్ అన్లాక్ను ఉపయోగించండి"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"తగినంత వెలుతురు లేకపోవడం లేక ఇతరత్రా కారణాల వల్ల మీ ఫేస్ గుర్తించబడనప్పుడు, వేలిముద్ర అన్లాక్ను ఉపయోగించండి"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"ఫేస్ అన్లాక్"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"ఫేస్ అన్లాక్తో సమస్య"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"ఫేస్ మోడల్ను తొలగించడానికి నొక్కండి, ఆపై మీ ముఖాన్ని మళ్లీ జోడించండి"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 936c978b..d34e976 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -675,8 +675,8 @@
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Значок відбитка пальця"</string>
<string name="device_unlock_notification_name" msgid="2632928999862915709">"Розблокування пристрою"</string>
<string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Спробуйте інший спосіб розблокування"</string>
- <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Розблоковуйте пристрій за допомогою фейс-контролю, коли не вдається розпізнати ваш відбиток пальця (наприклад, коли у вас мокрі пальці)"</string>
- <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Розблоковуйте пристрій відбитком пальця, коли не вдається розпізнати ваше обличчя (наприклад, коли недостатньо світла)"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Якщо пристрій не розпізнає ваш відбиток пальця (наприклад, коли у вас мокрі руки), використовуйте фейс-контроль"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Якщо пристрій не розпізнає ваше обличчя (наприклад, коли освітлення погане), використовуйте відбиток пальця"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Фейс-контроль"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Сталася помилка з фейсконтролем"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Натисніть, щоб видалити свою модель обличчя, а потім знову додайте її"</string>
diff --git a/core/res/res/values-watch/dimens_material.xml b/core/res/res/values-watch/dimens_material.xml
index 2ab2d91..8becb08 100644
--- a/core/res/res/values-watch/dimens_material.xml
+++ b/core/res/res/values-watch/dimens_material.xml
@@ -47,11 +47,12 @@
<dimen name="progress_bar_height">24dp</dimen>
<!-- Progress bar message dimens -->
- <dimen name="message_progress_dialog_text_size">18sp</dimen>
+ <dimen name="message_progress_dialog_text_size">14sp</dimen>
<dimen name="message_progress_dialog_bottom_padding">80px</dimen>
<dimen name="message_progress_dialog_top_padding">0dp</dimen>
<dimen name="message_progress_dialog_start_padding">0dp</dimen>
<dimen name="message_progress_dialog_end_padding">0dp</dimen>
+ <item name="message_progress_dialog_letter_spacing" format="float" type="dimen">0.021</item>
<!-- fallback for screen percentage widths -->
<dimen name="screen_percentage_05">0dp</dimen>
diff --git a/core/res/res/values-watch/styles_material.xml b/core/res/res/values-watch/styles_material.xml
index 8698e86..f3e412d 100644
--- a/core/res/res/values-watch/styles_material.xml
+++ b/core/res/res/values-watch/styles_material.xml
@@ -100,7 +100,9 @@
<style name="ProgressDialogMessage">
<item name="android:textAlignment">center</item>
- <item name="textAppearance">@android:style/TextAppearance.DeviceDefault.Medium</item>
+ <item name="android:fontFamily">google-sans-text</item>
+ <item name="android:letterSpacing">@dimen/message_progress_dialog_letter_spacing</item>
+ <item name="textColor">?attr/textColorPrimary</item>
<item name="textSize">@dimen/message_progress_dialog_text_size</item>
<item name="paddingBottom">@dimen/message_progress_dialog_bottom_padding</item>
<item name="paddingEnd">@dimen/message_progress_dialog_end_padding</item>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index ccdd945..06ec1dd 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1891,7 +1891,7 @@
<string name="zen_mode_duration_hours" msgid="7841806065034711849">"{count,plural, =1{1 小時}other{# 小時}}"</string>
<string name="zen_mode_duration_hours_short" msgid="3666949653933099065">"{count,plural, =1{1 小時}other{# 小時}}"</string>
<string name="zen_mode_until_next_day" msgid="1403042784161725038">"直至<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
- <string name="zen_mode_until" msgid="2250286190237669079">"完成時間:<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_until" msgid="2250286190237669079">"結束時間:<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"直至<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (下一次響鬧)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"直至你關閉為止"</string>
<string name="zen_mode_forever_dnd" msgid="3423201955704180067">"直至你關閉「請勿騷擾」功能"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index e54347f..04fd70a 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -10118,6 +10118,9 @@
screen that can be used to provide more information about a provider. For
longer strings it will be truncated. -->
<attr name="settingsSubtitle" format="string" />
+ <!-- Fully qualified class name of an activity that allows the user to modify
+ the settings for this service. -->
+ <attr name="settingsActivity" />
</declare-styleable>
<!-- A list of capabilities that indicates to the OS what kinds of credentials
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 3c296de..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.-->
@@ -1151,6 +1157,14 @@
<!-- Allows activities to be launched on a long press on power during device setup. -->
<bool name="config_allowStartActivityForLongPressOnPowerInSetup">false</bool>
+ <!-- Control the behavior when the user short presses the settings button.
+ 0 - Nothing
+ 1 - Launch notification panel
+ This needs to match the constants in
+ com/android/server/policy/PhoneWindowManager.java
+ -->
+ <integer name="config_shortPressOnSettingsBehavior">0</integer>
+
<!-- Control the behavior when the user short presses the power button.
0 - Nothing
1 - Go to sleep (doze)
@@ -5394,6 +5408,7 @@
<item>1,1,1.0,.4,1</item>
<item>1,1,1.0,.15,15</item>
<item>0,0,0.7,0,1</item>
+ <item>0,0,0.83333,0,1</item>
</string-array>
<!-- The integer index of the selected option in config_udfps_touch_detection_options -->
diff --git a/core/res/res/values/config_device_idle.xml b/core/res/res/values/config_device_idle.xml
index 98a5ff9..bc9ca3d 100644
--- a/core/res/res/values/config_device_idle.xml
+++ b/core/res/res/values/config_device_idle.xml
@@ -42,6 +42,15 @@
<!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_FACTOR -->
<item name="device_idle_light_idle_factor" format="float" type="integer">2.0</item>
+ <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_INCREASE_LINEARLY -->
+ <bool name="device_idle_light_idle_increase_linearly">false</bool>
+
+ <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS -->
+ <integer name="device_idle_light_idle_linear_increase_factor_ms">300000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_FLEX_LINEAR_INCREASE_FACTOR_MS -->
+ <integer name="device_idle_light_idle_flex_linear_increase_factor_ms">60000</integer>
+
<!-- Default for DeviceIdleController.Constants.LIGHT_MAX_IDLE_TIMEOUT -->
<integer name="device_idle_light_max_idle_to_ms">900000</integer>
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index 71d696e..3ba150b 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -73,7 +73,7 @@
CarrierConfigManager#KEY_AUTO_DATA_SWITCH_RAT_SIGNAL_SCORE_STRING_ARRAY.
If 0, the device always switch to the higher score SIM.
If < 0, the network type and signal strength based auto switch is disabled. -->
- <integer name="auto_data_switch_score_tolerance">-1</integer>
+ <integer name="auto_data_switch_score_tolerance">4000</integer>
<java-symbol type="integer" name="auto_data_switch_score_tolerance" />
<!-- Boolean indicating whether the Iwlan data service supports persistence of iwlan ipsec
@@ -219,4 +219,8 @@
<bool name="telephony_analytics_switch">true</bool>
<java-symbol type="bool" name="telephony_analytics_switch" />
+ <!-- Whether to enable modem on boot if behavior is not defined -->
+ <bool name="config_enable_cellular_on_boot_default">true</bool>
+ <java-symbol type="bool" name="config_enable_cellular_on_boot_default" />
+
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index fac6aac..41356bd 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -826,6 +826,9 @@
security policy. [CHAR_LIMIT=NONE]-->
<string name="notification_channel_accessibility_security_policy">Accessibility usage</string>
+ <!-- Text shown when viewing channel settings for notifications related to displays -->
+ <string name="notification_channel_display">Display</string>
+
<!-- Label for foreground service notification when one app is running.
[CHAR LIMIT=NONE BACKUP_MESSAGE_ID=6826789589341671842] -->
<string name="foreground_service_app_in_background"><xliff:g id="app_name">%1$s</xliff:g> is
@@ -6290,6 +6293,11 @@
<!-- Toast message that is shown when the user mutes the microphone from the keyboard. [CHAR LIMIT=TOAST] -->
<string name="mic_access_off_toast">Microphone is blocked</string>
+ <!-- Title of connected display unavailable notifications. [CHAR LIMIT=NONE] -->
+ <string name="connected_display_unavailable_notification_title">Can\'t mirror to display</string>
+ <!-- Content of connected display unavailable notification. [CHAR LIMIT=NONE] -->
+ <string name="connected_display_unavailable_notification_content">Use a different cable and try again</string>
+
<!-- Name of concurrent display notifications. [CHAR LIMIT=NONE] -->
<string name="concurrent_display_notification_name">Dual screen</string>
<!-- Title of concurrent display active notification. [CHAR LIMIT=NONE] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 193f3ad..efe5c3b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1816,6 +1816,7 @@
<java-symbol type="integer" name="config_lidNavigationAccessibility" />
<java-symbol type="integer" name="config_lidOpenRotation" />
<java-symbol type="integer" name="config_longPressOnHomeBehavior" />
+ <java-symbol type="integer" name="config_shortPressOnSettingsBehavior" />
<java-symbol type="layout" name="global_actions" />
<java-symbol type="layout" name="global_actions_item" />
<java-symbol type="layout" name="global_actions_silent_mode" />
@@ -1996,6 +1997,7 @@
<java-symbol type="drawable" name="stat_sys_throttled" />
<java-symbol type="drawable" name="vpn_connected" />
<java-symbol type="drawable" name="vpn_disconnected" />
+ <java-symbol type="drawable" name="usb_cable_unknown_issue" />
<java-symbol type="id" name="ask_checkbox" />
<java-symbol type="id" name="compat_checkbox" />
<java-symbol type="id" name="original_app_icon" />
@@ -3485,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.
@@ -3810,6 +3814,7 @@
<java-symbol type="string" name="notification_channel_do_not_disturb" />
<java-symbol type="string" name="notification_channel_accessibility_magnification" />
<java-symbol type="string" name="notification_channel_accessibility_security_policy" />
+ <java-symbol type="string" name="notification_channel_display" />
<java-symbol type="string" name="config_defaultAutofillService" />
<java-symbol type="string" name="config_defaultFieldClassificationService" />
<java-symbol type="string" name="config_defaultOnDeviceSpeechRecognitionService" />
@@ -4537,7 +4542,10 @@
<java-symbol type="integer" name="device_idle_light_idle_to_init_flex_ms" />
<java-symbol type="integer" name="device_idle_light_idle_to_max_flex_ms" />
<java-symbol type="integer" name="device_idle_light_idle_factor" />
+ <java-symbol type="bool" name="device_idle_light_idle_increase_linearly" />
<java-symbol type="integer" name="device_idle_light_max_idle_to_ms" />
+ <java-symbol type="integer" name="device_idle_light_idle_linear_increase_factor_ms" />
+ <java-symbol type="integer" name="device_idle_light_idle_flex_linear_increase_factor_ms" />
<java-symbol type="integer" name="device_idle_light_idle_maintenance_min_budget_ms" />
<java-symbol type="integer" name="device_idle_light_idle_maintenance_max_budget_ms" />
<java-symbol type="integer" name="device_idle_min_light_maintenance_time_ms" />
@@ -5060,6 +5068,8 @@
<java-symbol type="array" name="device_state_notification_thermal_contents"/>
<java-symbol type="array" name="device_state_notification_power_save_titles"/>
<java-symbol type="array" name="device_state_notification_power_save_contents"/>
+ <java-symbol type="string" name="connected_display_unavailable_notification_title"/>
+ <java-symbol type="string" name="connected_display_unavailable_notification_content"/>
<java-symbol type="string" name="concurrent_display_notification_name"/>
<java-symbol type="string" name="concurrent_display_notification_active_title"/>
<java-symbol type="string" name="concurrent_display_notification_active_content"/>
diff --git a/core/tests/BroadcastRadioTests/Android.bp b/core/tests/BroadcastRadioTests/Android.bp
index 85d54e0..054d10c 100644
--- a/core/tests/BroadcastRadioTests/Android.bp
+++ b/core/tests/BroadcastRadioTests/Android.bp
@@ -38,7 +38,7 @@
static_libs: [
"services.core",
"androidx.test.rules",
- "truth-prebuilt",
+ "truth",
"testng",
"mockito-target-extended",
],
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 a195228..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
@@ -16,20 +16,20 @@
package com.android.server.broadcastradio.aidl;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.after;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doThrow;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.timeout;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static org.junit.Assert.assertThrows;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.app.compat.CompatChanges;
import android.graphics.Bitmap;
@@ -81,8 +81,8 @@
private static final int USER_ID_1 = 11;
private static final int USER_ID_2 = 12;
- private static final VerificationWithTimeout CALLBACK_TIMEOUT =
- timeout(/* millis= */ 200);
+ private static final int CALLBACK_TIMEOUT_MS = 200;
+ private static final VerificationWithTimeout CALLBACK_TIMEOUT = timeout(CALLBACK_TIMEOUT_MS);
private static final int SIGNAL_QUALITY = 90;
private static final long AM_FM_FREQUENCY_SPACING = 500;
private static final long[] AM_FM_FREQUENCY_LIST = {97_500, 98_100, 99_100};
@@ -166,12 +166,12 @@
@Before
public void setup() throws Exception {
- when(mUserHandleMock.getIdentifier()).thenReturn(USER_ID_1);
doReturn(true).when(() -> CompatChanges.isChangeEnabled(
eq(ConversionUtils.RADIO_U_VERSION_REQUIRED), anyInt()));
+ doReturn(USER_ID_1).when(mUserHandleMock).getIdentifier();
+ doReturn(mUserHandleMock).when(() -> Binder.getCallingUserHandle());
doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
doReturn(USER_ID_1).when(() -> RadioServiceUserController.getCurrentUser());
- doReturn(mUserHandleMock).when(() -> Binder.getCallingUserHandle());
mRadioModule = new RadioModule(mBroadcastRadioMock,
AidlTestUtils.makeDefaultModuleProperties());
@@ -222,7 +222,7 @@
return Result.OK;
}).when(mBroadcastRadioMock).seek(anyBoolean(), anyBoolean());
- when(mBroadcastRadioMock.getImage(anyInt())).thenReturn(null);
+ doReturn(null).when(mBroadcastRadioMock).getImage(anyInt());
doAnswer(invocation -> {
int configFlag = (int) invocation.getArguments()[0];
@@ -275,7 +275,7 @@
mTunerSessions[0].setConfiguration(FM_BAND_CONFIG);
- verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0))
+ verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0))
.onConfigurationChanged(FM_BAND_CONFIG);
}
@@ -446,26 +446,11 @@
mTunerSessions[0].tune(initialSel);
- verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0))
+ verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0))
.onCurrentProgramInfoChanged(tuneInfo);
}
@Test
- public void tune_forSystemUser() throws Exception {
- when(mUserHandleMock.getIdentifier()).thenReturn(UserHandle.USER_SYSTEM);
- doReturn(mUserHandleMock).when(() -> Binder.getCallingUserHandle());
- doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
- ProgramSelector initialSel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
- RadioManager.ProgramInfo tuneInfo =
- AidlTestUtils.makeProgramInfo(initialSel, SIGNAL_QUALITY);
- openAidlClients(/* numClients= */ 1);
-
- mTunerSessions[0].tune(initialSel);
-
- verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo);
- }
-
- @Test
public void tune_withUnknownErrorFromHal_fails() throws Exception {
openAidlClients(/* numClients= */ 1);
ProgramSelector sel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
@@ -525,7 +510,7 @@
mTunerSessions[0].step(/* directionDown= */ true, /* skipSubChannel= */ false);
- verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0))
+ verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0))
.onCurrentProgramInfoChanged(any());
}
@@ -604,7 +589,7 @@
mTunerSessions[0].seek(/* directionDown= */ true, /* skipSubChannel= */ false);
- verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0))
+ verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0))
.onCurrentProgramInfoChanged(seekUpInfo);
}
@@ -625,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();
@@ -636,8 +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);
doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
mTunerSessions[0].cancel();
@@ -686,8 +667,8 @@
public void getImage_whenHalThrowsException_fails() throws Exception {
openAidlClients(/* numClients= */ 1);
String exceptionMessage = "HAL service died.";
- when(mBroadcastRadioMock.getImage(anyInt()))
- .thenThrow(new RemoteException(exceptionMessage));
+ doThrow(new RemoteException(exceptionMessage)).when(mBroadcastRadioMock)
+ .getImage(anyInt());
RuntimeException thrown = assertThrows(RuntimeException.class, () -> {
mTunerSessions[0].getImage(/* id= */ 1);
@@ -713,7 +694,8 @@
mTunerSessions[0].startBackgroundScan();
- verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)).onBackgroundScanComplete();
+ verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0))
+ .onBackgroundScanComplete();
}
@Test
@@ -905,7 +887,8 @@
mHalTunerCallback.onProgramListUpdated(AidlTestUtils.makeHalChunk(/* purge= */ false,
/* complete= */ true, List.of(TEST_FM_INFO), new ArrayList<>()));
- verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)).onProgramListUpdated(any());
+ verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0))
+ .onProgramListUpdated(any());
}
@Test
@@ -1160,8 +1143,8 @@
Map<String, String> parametersSet = Map.of("mockParam1", "mockValue1",
"mockParam2", "mockValue2");
String exceptionMessage = "HAL service died.";
- when(mBroadcastRadioMock.setParameters(any()))
- .thenThrow(new RemoteException(exceptionMessage));
+ doThrow(new RemoteException(exceptionMessage)).when(mBroadcastRadioMock)
+ .setParameters(any());
RuntimeException thrown = assertThrows(RuntimeException.class, () -> {
mTunerSessions[0].setParameters(parametersSet);
@@ -1186,8 +1169,8 @@
openAidlClients(/* numClients= */ 1);
List<String> parameterKeys = List.of("mockKey1", "mockKey2");
String exceptionMessage = "HAL service died.";
- when(mBroadcastRadioMock.getParameters(any()))
- .thenThrow(new RemoteException(exceptionMessage));
+ doThrow(new RemoteException(exceptionMessage)).when(mBroadcastRadioMock)
+ .getParameters(any());
RuntimeException thrown = assertThrows(RuntimeException.class, () -> {
mTunerSessions[0].getParameters(parameterKeys);
@@ -1198,7 +1181,7 @@
}
@Test
- public void onCurrentProgramInfoChanged_withNoncurrentUser_doesNotInvokeCallback()
+ public void onCurrentProgramInfoChanged_withNonCurrentUser_doesNotInvokeCallback()
throws Exception {
openAidlClients(1);
doReturn(USER_ID_2).when(() -> RadioServiceUserController.getCurrentUser());
@@ -1206,7 +1189,7 @@
mHalTunerCallback.onCurrentProgramInfoChanged(AidlTestUtils.makeHalProgramInfo(
AidlTestUtils.makeHalFmSelector(AM_FM_FREQUENCY_LIST[1]), SIGNAL_QUALITY));
- verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0))
+ verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0))
.onCurrentProgramInfoChanged(any());
}
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 fac9eaa..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
@@ -16,22 +16,22 @@
package com.android.server.broadcastradio.hal2;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.after;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doThrow;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.timeout;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertThrows;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.graphics.Bitmap;
import android.hardware.broadcastradio.V2_0.Constants;
@@ -78,8 +78,8 @@
private static final int USER_ID_1 = 11;
private static final int USER_ID_2 = 12;
- private static final VerificationWithTimeout CALLBACK_TIMEOUT =
- timeout(/* millis= */ 200);
+ private static final int CALLBACK_TIMEOUT_MS = 200;
+ private static final VerificationWithTimeout CALLBACK_TIMEOUT = timeout(CALLBACK_TIMEOUT_MS);
private static final int SIGNAL_QUALITY = 1;
private static final long AM_FM_FREQUENCY_SPACING = 500;
private static final long[] AM_FM_FREQUENCY_LIST = {97_500, 98_100, 99_100};
@@ -113,7 +113,7 @@
@Before
public void setup() throws Exception {
- when(mUserHandleMock.getIdentifier()).thenReturn(USER_ID_1);
+ doReturn(USER_ID_1).when(mUserHandleMock).getIdentifier();
doReturn(mUserHandleMock).when(() -> Binder.getCallingUserHandle());
doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
doReturn(USER_ID_1).when(() -> RadioServiceUserController.getCurrentUser());
@@ -170,7 +170,7 @@
return Result.OK;
}).when(mHalTunerSessionMock).scan(anyBoolean(), anyBoolean());
- when(mBroadcastRadioMock.getImage(anyInt())).thenReturn(new ArrayList<Byte>(0));
+ doReturn(new ArrayList<Byte>(0)).when(mBroadcastRadioMock).getImage(anyInt());
doAnswer(invocation -> {
int configFlag = (int) invocation.getArguments()[0];
@@ -227,7 +227,7 @@
mTunerSessions[0].setConfiguration(FM_BAND_CONFIG);
- verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0))
+ verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0))
.onConfigurationChanged(FM_BAND_CONFIG);
}
@@ -379,7 +379,7 @@
mTunerSessions[0].tune(initialSel);
- verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0))
+ verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0))
.onCurrentProgramInfoChanged(tuneInfo);
}
@@ -398,20 +398,6 @@
}
@Test
- public void tune_forSystemUser() throws Exception {
- when(mUserHandleMock.getIdentifier()).thenReturn(UserHandle.USER_SYSTEM);
- doReturn(mUserHandleMock).when(() -> Binder.getCallingUserHandle());
- doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
- ProgramSelector initialSel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
- RadioManager.ProgramInfo tuneInfo = TestUtils.makeProgramInfo(initialSel, SIGNAL_QUALITY);
- openAidlClients(/* numClients= */ 1);
-
- mTunerSessions[0].tune(initialSel);
-
- verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo);
- }
-
- @Test
public void step_withDirectionUp() throws Exception {
long initFreq = AM_FM_FREQUENCY_LIST[1];
ProgramSelector initialSel = TestUtils.makeFmSelector(initFreq);
@@ -455,7 +441,7 @@
mTunerSessions[0].step(/* directionDown= */ true, /* skipSubChannel= */ false);
- verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0))
+ verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0))
.onCurrentProgramInfoChanged(any());
}
@@ -533,7 +519,7 @@
mTunerSessions[0].seek(/* directionDown= */ true, /* skipSubChannel= */ false);
- verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0))
+ verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0))
.onCurrentProgramInfoChanged(seekUpInfo);
}
@@ -554,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();
@@ -563,22 +547,8 @@
}
@Test
- public void cancel_forNonCurrentUser() throws Exception {
- openAidlClients(/* numClients= */ 1);
- ProgramSelector initialSel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
- mTunerSessions[0].tune(initialSel);
- doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
-
- mTunerSessions[0].cancel();
-
- verify(mHalTunerSessionMock, never()).cancel();
- }
-
- @Test
public void cancel_forNonCurrentUser_doesNotCancel() throws Exception {
openAidlClients(/* numClients= */ 1);
- ProgramSelector initialSel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
- mTunerSessions[0].tune(initialSel);
doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
mTunerSessions[0].cancel();
@@ -627,8 +597,7 @@
public void getImage_whenHalThrowsException_fails() throws Exception {
openAidlClients(/* numClients= */ 1);
String exceptionMessage = "HAL service died.";
- when(mBroadcastRadioMock.getImage(anyInt()))
- .thenThrow(new RemoteException(exceptionMessage));
+ doThrow(new RemoteException(exceptionMessage)).when(mBroadcastRadioMock).getImage(anyInt());
RuntimeException thrown = assertThrows(RuntimeException.class, () -> {
mTunerSessions[0].getImage(/* id= */ 1);
@@ -654,7 +623,8 @@
mTunerSessions[0].startBackgroundScan();
- verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)).onBackgroundScanComplete();
+ verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0))
+ .onBackgroundScanComplete();
}
@Test
@@ -845,8 +815,8 @@
Map<String, String> parametersSet = Map.of("mockParam1", "mockValue1",
"mockParam2", "mockValue2");
String exceptionMessage = "HAL service died.";
- when(mHalTunerSessionMock.setParameters(any()))
- .thenThrow(new RemoteException(exceptionMessage));
+ doThrow(new RemoteException(exceptionMessage)).when(mHalTunerSessionMock)
+ .setParameters(any());
RuntimeException thrown = assertThrows(RuntimeException.class, () -> {
mTunerSessions[0].setParameters(parametersSet);
@@ -871,8 +841,8 @@
openAidlClients(/* numClients= */ 1);
List<String> parameterKeys = List.of("mockKey1", "mockKey2");
String exceptionMessage = "HAL service died.";
- when(mHalTunerSessionMock.getParameters(any()))
- .thenThrow(new RemoteException(exceptionMessage));
+ doThrow(new RemoteException(exceptionMessage)).when(mHalTunerSessionMock)
+ .getParameters(any());
RuntimeException thrown = assertThrows(RuntimeException.class, () -> {
mTunerSessions[0].getParameters(parameterKeys);
@@ -883,7 +853,7 @@
}
@Test
- public void onCurrentProgramInfoChanged_withNoncurrentUser_doesNotInvokeCallback()
+ public void onCurrentProgramInfoChanged_withNonCurrentUser_doesNotInvokeCallback()
throws Exception {
openAidlClients(1);
doReturn(USER_ID_2).when(() -> RadioServiceUserController.getCurrentUser());
@@ -891,7 +861,7 @@
mHalTunerCallback.onCurrentProgramInfoChanged(TestUtils.makeHalProgramInfo(
TestUtils.makeHalFmSelector(/* freq= */ 97300), SIGNAL_QUALITY));
- verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0))
+ verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).times(0))
.onCurrentProgramInfoChanged(any());
}
diff --git a/core/tests/GameManagerTests/Android.bp b/core/tests/GameManagerTests/Android.bp
index 8c5d6d5..0e3bc65 100644
--- a/core/tests/GameManagerTests/Android.bp
+++ b/core/tests/GameManagerTests/Android.bp
@@ -30,7 +30,7 @@
"frameworks-base-testutils",
"junit",
"platform-test-annotations",
- "truth-prebuilt",
+ "truth",
],
libs: ["android.test.runner"],
platform_apis: true,
diff --git a/core/tests/PackageInstallerSessions/Android.bp b/core/tests/PackageInstallerSessions/Android.bp
index 6f2366e..b631df1 100644
--- a/core/tests/PackageInstallerSessions/Android.bp
+++ b/core/tests/PackageInstallerSessions/Android.bp
@@ -35,7 +35,7 @@
"frameworks-base-testutils",
"platform-test-annotations",
"testng",
- "truth-prebuilt",
+ "truth",
],
libs: [
diff --git a/core/tests/batterystatstests/BatteryUsageStatsProtoTests/Android.bp b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/Android.bp
index 5260835..1fb5f2c 100644
--- a/core/tests/batterystatstests/BatteryUsageStatsProtoTests/Android.bp
+++ b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/Android.bp
@@ -18,7 +18,7 @@
"platform-test-annotations",
"platformprotosnano",
"statsdprotolite",
- "truth-prebuilt",
+ "truth",
],
libs: ["android.test.runner"],
diff --git a/core/tests/bugreports/Android.bp b/core/tests/bugreports/Android.bp
index 2b34ee2..7c1ac48 100644
--- a/core/tests/bugreports/Android.bp
+++ b/core/tests/bugreports/Android.bp
@@ -32,7 +32,7 @@
static_libs: [
"androidx.test.rules",
"androidx.test.uiautomator_uiautomator",
- "truth-prebuilt",
+ "truth",
],
test_suites: ["general-tests"],
sdk_version: "test_current",
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 04622fd..2993a0e 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -58,12 +58,13 @@
"androidx.test.uiautomator_uiautomator",
"platform-test-annotations",
"platform-compat-test-rules",
- "truth-prebuilt",
+ "truth",
"print-test-util-lib",
"testng",
"servicestests-utils",
"device-time-shell-utils",
"testables",
+ "com.android.text.flags-aconfig-java",
],
libs: [
@@ -149,7 +150,7 @@
"androidx.test.runner",
"androidx.test.rules",
"mockito-target-minus-junit4",
- "truth-prebuilt",
+ "truth",
],
libs: [
diff --git a/core/tests/coretests/src/android/app/servertransaction/WindowStateResizeItemTest.java b/core/tests/coretests/src/android/app/servertransaction/WindowStateResizeItemTest.java
new file mode 100644
index 0000000..c00eb91
--- /dev/null
+++ b/core/tests/coretests/src/android/app/servertransaction/WindowStateResizeItemTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.app.servertransaction;
+
+import static org.mockito.Mockito.verify;
+
+import android.app.ClientTransactionHandler;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.util.MergedConfiguration;
+import android.view.IWindow;
+import android.view.InsetsState;
+import android.window.ClientWindowFrames;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link WindowStateResizeItem}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:WindowStateResizeItemTest
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class WindowStateResizeItemTest {
+
+ @Mock
+ private ClientTransactionHandler mHandler;
+ @Mock
+ private PendingTransactionActions mPendingActions;
+ @Mock
+ private IWindow mWindow;
+ @Mock
+ private ClientWindowFrames mFrames;
+ @Mock
+ private MergedConfiguration mConfiguration;
+ @Mock
+ private InsetsState mInsetsState;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testExecute() throws RemoteException {
+ final WindowStateResizeItem item = WindowStateResizeItem.obtain(mWindow, mFrames,
+ true /* reportDraw */, mConfiguration, mInsetsState, true /* forceLayout */,
+ true /* alwaysConsumeSystemBars */, 123 /* displayId */, 321 /* syncSeqId */,
+ true /* dragResizing */);
+ item.execute(mHandler, mPendingActions);
+
+ verify(mWindow).resized(mFrames,
+ true /* reportDraw */, mConfiguration, mInsetsState, true /* forceLayout */,
+ true /* alwaysConsumeSystemBars */, 123 /* displayId */, 321 /* syncSeqId */,
+ true /* dragResizing */);
+ }
+}
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/content/res/FontScaleConverterActivityTest.java b/core/tests/coretests/src/android/content/res/FontScaleConverterActivityTest.java
index 0941a2b..6c14ee3 100644
--- a/core/tests/coretests/src/android/content/res/FontScaleConverterActivityTest.java
+++ b/core/tests/coretests/src/android/content/res/FontScaleConverterActivityTest.java
@@ -137,7 +137,7 @@
);
});
- PollingCheck.waitFor(/* timeout= */ 7000, () -> {
+ PollingCheck.waitFor(/* timeout= */ 10000, () -> {
AtomicBoolean isActivityAtCorrectScale = new AtomicBoolean(false);
rule.getScenario().onActivity(activity ->
isActivityAtCorrectScale.set(
@@ -163,7 +163,7 @@
});
PollingCheck.waitFor(
- /* timeout= */ 5000,
+ /* timeout= */ 10000,
() -> InstrumentationRegistry.getInstrumentation()
.getContext()
.getResources()
diff --git a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
index a8a5059..3f78396 100644
--- a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
+++ b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
@@ -16,6 +16,8 @@
package android.graphics;
+import static com.android.text.flags.Flags.FLAG_CUSTOM_LOCALE_FALLBACK;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -30,6 +32,9 @@
import android.graphics.fonts.SystemFonts;
import android.graphics.text.PositionedGlyphs;
import android.graphics.text.TextRunShaper;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.text.FontConfig;
import android.util.ArrayMap;
@@ -39,6 +44,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.xmlpull.v1.XmlPullParserException;
@@ -107,6 +113,10 @@
GLYPH_2EM_WIDTH = paint.measureText("a");
}
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+
@Before
public void setUp() {
final AssetManager am =
@@ -877,6 +887,130 @@
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
}
+ private static void assertA3emFontIsUsed(Typeface typeface) {
+ final Paint paint = new Paint();
+ assertNotNull(typeface);
+ paint.setTypeface(typeface);
+ assertTrue("a3em font must be used", GLYPH_3EM_WIDTH == paint.measureText("a")
+ && GLYPH_1EM_WIDTH == paint.measureText("b")
+ && GLYPH_1EM_WIDTH == paint.measureText("c"));
+ }
+
+ private static void assertB3emFontIsUsed(Typeface typeface) {
+ final Paint paint = new Paint();
+ assertNotNull(typeface);
+ paint.setTypeface(typeface);
+ assertTrue("b3em font must be used", GLYPH_1EM_WIDTH == paint.measureText("a")
+ && GLYPH_3EM_WIDTH == paint.measureText("b")
+ && GLYPH_1EM_WIDTH == paint.measureText("c"));
+ }
+
+ private static String getBaseXml(String font, String lang) {
+ final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<familyset>"
+ + " <family>"
+ + " <font weight='400' style='normal'>no_coverage.ttf</font>"
+ + " </family>"
+ + " <family name='named-family'>"
+ + " <font weight='400' style='normal'>no_coverage.ttf</font>"
+ + " </family>"
+ + " <family lang='%s'>"
+ + " <font weight='400' style='normal'>%s</font>"
+ + " </family>"
+ + "</familyset>";
+ return String.format(xml, lang, font);
+ }
+
+ private static String getCustomizationXml(String font, String op, String lang) {
+ final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<fonts-modification version='1'>"
+ + " <family customizationType='new-locale-family' operation='%s' lang='%s'>"
+ + " <font weight='400' style='normal' fallbackFor='named-family'>%s</font>"
+ + " </family>"
+ + "</fonts-modification>";
+ return String.format(xml, op, lang, font);
+ }
+
+ @RequiresFlagsEnabled(FLAG_CUSTOM_LOCALE_FALLBACK)
+ @Test
+ public void testBuildSystemFallback__Customization_locale_prepend() {
+ final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
+ final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+
+ buildSystemFallback(
+ getBaseXml("a3em.ttf", "ja-JP"),
+ getCustomizationXml("b3em.ttf", "prepend", "ja-JP"),
+ fontMap, fallbackMap);
+ Typeface typeface = fontMap.get("named-family");
+
+ // operation "prepend" places font before the original font, thus b3em is used.
+ assertB3emFontIsUsed(typeface);
+ }
+
+ @RequiresFlagsEnabled(FLAG_CUSTOM_LOCALE_FALLBACK)
+ @Test
+ public void testBuildSystemFallback__Customization_locale_replace() {
+ final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
+ final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+
+ buildSystemFallback(
+ getBaseXml("a3em.ttf", "ja-JP"),
+ getCustomizationXml("b3em.ttf", "replace", "ja-JP"),
+ fontMap, fallbackMap);
+ Typeface typeface = fontMap.get("named-family");
+
+ // operation "replace" removes the original font, thus b3em font is used.
+ assertB3emFontIsUsed(typeface);
+ }
+
+ @RequiresFlagsEnabled(FLAG_CUSTOM_LOCALE_FALLBACK)
+ @Test
+ public void testBuildSystemFallback__Customization_locale_append() {
+ final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
+ final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+
+ buildSystemFallback(
+ getBaseXml("a3em.ttf", "ja-JP"),
+ getCustomizationXml("b3em.ttf", "append", "ja-JP"),
+ fontMap, fallbackMap);
+ Typeface typeface = fontMap.get("named-family");
+
+ // operation "append" comes next to the original font, so the original "a3em" is used.
+ assertA3emFontIsUsed(typeface);
+ }
+
+ @RequiresFlagsEnabled(FLAG_CUSTOM_LOCALE_FALLBACK)
+ @Test
+ public void testBuildSystemFallback__Customization_locale_ScriptMismatch() {
+ final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
+ final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+
+ buildSystemFallback(
+ getBaseXml("a3em.ttf", "ja-JP"),
+ getCustomizationXml("b3em.ttf", "replace", "ko-KR"),
+ fontMap, fallbackMap);
+ Typeface typeface = fontMap.get("named-family");
+
+ // Since the script doesn't match, the customization is ignored.
+ assertA3emFontIsUsed(typeface);
+ }
+
+ @RequiresFlagsEnabled(FLAG_CUSTOM_LOCALE_FALLBACK)
+ @Test
+ public void testBuildSystemFallback__Customization_locale_SubscriptMatch() {
+ final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
+ final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+
+ buildSystemFallback(
+ getBaseXml("a3em.ttf", "ja-JP"),
+ getCustomizationXml("b3em.ttf", "replace", "ko-Hani-KR"),
+ fontMap, fallbackMap);
+ Typeface typeface = fontMap.get("named-family");
+
+ // Hani script is supported by Japanese, Jpan.
+ assertB3emFontIsUsed(typeface);
+ }
+
@Test(expected = IllegalArgumentException.class)
public void testBuildSystemFallback__Customization_new_named_family_no_name_exception() {
final String oemXml = "<?xml version='1.0' encoding='UTF-8'?>"
@@ -902,7 +1036,6 @@
readFontCustomization(oemXml);
}
-
@Test
public void testBuildSystemFallback_UpdatableFont() {
final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
diff --git a/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java b/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
index 55ded9c..517aeae 100644
--- a/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
+++ b/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java
@@ -16,6 +16,10 @@
package android.service.notification;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE;
+
import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.RANKING_UPDATE_ASHMEM;
import static junit.framework.Assert.assertEquals;
@@ -24,20 +28,40 @@
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.mockito.Mockito.spy;
+
+import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ShortcutInfo;
+import android.os.Bundle;
import android.os.Parcel;
+import android.os.SharedMemory;
+import android.testing.TestableContext;
+import androidx.test.InstrumentationRegistry;
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;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
+import java.util.ArrayList;
+import java.util.List;
+
@SmallTest
@RunWith(Parameterized.class)
public class NotificationRankingUpdateTest {
@@ -56,16 +80,368 @@
@Parameterized.Parameter
public boolean mRankingUpdateAshmem;
+ @Rule
+ public TestableContext mContext =
+ spy(new TestableContext(InstrumentationRegistry.getContext(), null));
+
+ protected TestableContext getContext() {
+ return mContext;
+ }
+
+ public static String[] mKeys = new String[] { "key", "key1", "key2", "key3", "key4"};
+
+ /**
+ * Creates a NotificationRankingUpdate with prepopulated Ranking entries
+ * @param context A testable context, used for PendingIntent creation
+ * @return The NotificationRankingUpdate to be used as test data
+ */
+ public static NotificationRankingUpdate generateUpdate(TestableContext context) {
+ NotificationListenerService.Ranking[] rankings =
+ new NotificationListenerService.Ranking[mKeys.length];
+ for (int i = 0; i < mKeys.length; i++) {
+ final String key = mKeys[i];
+ NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking();
+ ranking.populate(
+ key,
+ i,
+ !isIntercepted(i),
+ getVisibilityOverride(i),
+ getSuppressedVisualEffects(i),
+ getImportance(i),
+ getExplanation(key),
+ getOverrideGroupKey(key),
+ getChannel(key, i),
+ getPeople(key, i),
+ getSnoozeCriteria(key, i),
+ getShowBadge(i),
+ getUserSentiment(i),
+ getHidden(i),
+ lastAudiblyAlerted(i),
+ getNoisy(i),
+ getSmartActions(key, i, context),
+ getSmartReplies(key, i),
+ canBubble(i),
+ isTextChanged(i),
+ isConversation(i),
+ getShortcutInfo(i),
+ getRankingAdjustment(i),
+ isBubble(i),
+ getProposedImportance(i),
+ hasSensitiveContent(i)
+ );
+ rankings[i] = ranking;
+ }
+ return new NotificationRankingUpdate(rankings);
+ }
+
+ /**
+ * Produces a visibility override value based on the provided index.
+ */
+ public static int getVisibilityOverride(int index) {
+ return index * 9;
+ }
+
+ /**
+ * Produces a group key based on the provided key.
+ */
+ public static String getOverrideGroupKey(String key) {
+ return key + key;
+ }
+
+ /**
+ * Produces a boolean that can be used to represent isIntercepted, based on the provided index.
+ */
+ public static boolean isIntercepted(int index) {
+ return index % 2 == 0;
+ }
+
+ /**
+ * Produces a suppressed visual effects value based on the provided index
+ */
+ public static int getSuppressedVisualEffects(int index) {
+ return index * 2;
+ }
+
+ /**
+ * Produces an importance value, based on the provided index
+ */
+ public static int getImportance(int index) {
+ return index;
+ }
+
+ /**
+ * Produces an explanation value, based on the provided key
+ */
+ public static String getExplanation(String key) {
+ return key + "explain";
+ }
+
+ /**
+ * Produces a notification channel, based on the provided key and index
+ */
+ public static NotificationChannel getChannel(String key, int index) {
+ return new NotificationChannel(key, key, getImportance(index));
+ }
+
+ /**
+ * Produces a boolean that can be used to represent showBadge, based on the provided index
+ */
+ public static boolean getShowBadge(int index) {
+ return index % 3 == 0;
+ }
+
+ /**
+ * Produces a user sentiment value, based on the provided index
+ */
+ public static int getUserSentiment(int index) {
+ switch(index % 3) {
+ case 0:
+ return USER_SENTIMENT_NEGATIVE;
+ case 1:
+ return USER_SENTIMENT_NEUTRAL;
+ case 2:
+ return USER_SENTIMENT_POSITIVE;
+ }
+ return USER_SENTIMENT_NEUTRAL;
+ }
+
+ /**
+ * Produces a boolean that can be used to represent "hidden," based on the provided index.
+ */
+ public static boolean getHidden(int index) {
+ return index % 2 == 0;
+ }
+
+ /**
+ * Produces a long to represent lastAudiblyAlerted based on the provided index.
+ */
+ public static long lastAudiblyAlerted(int index) {
+ return index * 2000L;
+ }
+
+ /**
+ * Produces a boolean that can be used to represent "noisy," based on the provided index.
+ */
+ public static boolean getNoisy(int index) {
+ return index < 1;
+ }
+
+ /**
+ * Produces strings that can be used to represent people, based on the provided key and index.
+ */
+ public static ArrayList<String> getPeople(String key, int index) {
+ ArrayList<String> people = new ArrayList<>();
+ for (int i = 0; i < index; i++) {
+ people.add(i + key);
+ }
+ return people;
+ }
+
+ /**
+ * Produces a number of snoozeCriteria, based on the provided key and index.
+ */
+ public static ArrayList<SnoozeCriterion> getSnoozeCriteria(String key, int index) {
+ ArrayList<SnoozeCriterion> snooze = new ArrayList<>();
+ for (int i = 0; i < index; i++) {
+ snooze.add(new SnoozeCriterion(key + i, getExplanation(key), key));
+ }
+ return snooze;
+ }
+
+ /**
+ * Produces a list of Actions which can be used to represent smartActions.
+ * These actions are built from pending intents with intent titles based on the provided
+ * key, and ids based on the provided index.
+ */
+ public static ArrayList<Notification.Action> getSmartActions(String key,
+ int index,
+ TestableContext context) {
+ ArrayList<Notification.Action> actions = new ArrayList<>();
+ for (int i = 0; i < index; i++) {
+ PendingIntent intent = PendingIntent.getBroadcast(
+ context,
+ index /*requestCode*/,
+ new Intent("ACTION_" + key),
+ PendingIntent.FLAG_IMMUTABLE /*flags*/);
+ actions.add(new Notification.Action.Builder(null /*icon*/, key, intent).build());
+ }
+ return actions;
+ }
+
+ /**
+ * Produces index number of "smart replies," all based on the provided key and index
+ */
+ public static ArrayList<CharSequence> getSmartReplies(String key, int index) {
+ ArrayList<CharSequence> choices = new ArrayList<>();
+ for (int i = 0; i < index; i++) {
+ choices.add("choice_" + key + "_" + i);
+ }
+ return choices;
+ }
+
+ /**
+ * Produces a boolean that can be used to represent canBubble, based on the provided index
+ */
+ public static boolean canBubble(int index) {
+ return index % 4 == 0;
+ }
+
+ /**
+ * Produces a boolean that can be used to represent isTextChanged, based on the provided index.
+ */
+ public static boolean isTextChanged(int index) {
+ return index % 4 == 0;
+ }
+
+ /**
+ * Produces a boolean that can be used to represent isConversation, based on the provided index.
+ */
+ public static boolean isConversation(int index) {
+ return index % 4 == 0;
+ }
+
+ /**
+ * Produces a ShortcutInfo value based on the provided index.
+ */
+ public static ShortcutInfo getShortcutInfo(int index) {
+ ShortcutInfo si = new ShortcutInfo(
+ index, String.valueOf(index), "packageName", new ComponentName("1", "1"), null,
+ "title", 0, "titleResName", "text", 0, "textResName",
+ "disabledMessage", 0, "disabledMessageResName",
+ null, null, 0, null, 0, 0,
+ 0, "iconResName", "bitmapPath", null, 0,
+ null, null, null, null);
+ return si;
+ }
+
+ /**
+ * Produces a rankingAdjustment value, based on the provided index.
+ */
+ public static int getRankingAdjustment(int index) {
+ return index % 3 - 1;
+ }
+
+ /**
+ * Produces a proposedImportance, based on the provided index.
+ */
+ public static int getProposedImportance(int index) {
+ return index % 5 - 1;
+ }
+
+ /**
+ * Produces a boolean that can be used to represent hasSensitiveContent, based on the provided
+ * index.
+ */
+ public static boolean hasSensitiveContent(int index) {
+ return index % 3 == 0;
+ }
+
+ /**
+ * Produces a boolean that can be used to represent isBubble, based on the provided index.
+ */
+ public static boolean isBubble(int index) {
+ return index % 4 == 0;
+ }
+
+ /**
+ * Checks that each of the pairs of actions in the two provided lists has identical titles,
+ * and that the lists have the same number of elements.
+ */
+ public void assertActionsEqual(
+ List<Notification.Action> expecteds, List<Notification.Action> actuals) {
+ Assert.assertEquals(expecteds.size(), actuals.size());
+ for (int i = 0; i < expecteds.size(); i++) {
+ Notification.Action expected = expecteds.get(i);
+ Notification.Action actual = actuals.get(i);
+ Assert.assertEquals(expected.title.toString(), actual.title.toString());
+ }
+ }
+
+ /**
+ * Checks that all subelements of the provided NotificationRankingUpdates are equal.
+ */
+ public void detailedAssertEquals(NotificationRankingUpdate a, NotificationRankingUpdate b) {
+ detailedAssertEquals(a.getRankingMap(), b.getRankingMap());
+ }
+
+ /**
+ * Checks that all subelements of the provided Ranking objects are equal.
+ */
+ public void detailedAssertEquals(String comment, NotificationListenerService.Ranking a,
+ NotificationListenerService.Ranking b) {
+ Assert.assertEquals(comment, a.getKey(), b.getKey());
+ Assert.assertEquals(comment, a.getRank(), b.getRank());
+ Assert.assertEquals(comment, a.matchesInterruptionFilter(), b.matchesInterruptionFilter());
+ Assert.assertEquals(comment, a.getLockscreenVisibilityOverride(),
+ b.getLockscreenVisibilityOverride());
+ Assert.assertEquals(comment, a.getSuppressedVisualEffects(),
+ b.getSuppressedVisualEffects());
+ Assert.assertEquals(comment, a.getImportance(), b.getImportance());
+ Assert.assertEquals(comment, a.getImportanceExplanation(), b.getImportanceExplanation());
+ Assert.assertEquals(comment, a.getOverrideGroupKey(), b.getOverrideGroupKey());
+ Assert.assertEquals(comment, a.getChannel().toString(), b.getChannel().toString());
+ Assert.assertEquals(comment, a.getAdditionalPeople(), b.getAdditionalPeople());
+ Assert.assertEquals(comment, a.getSnoozeCriteria(), b.getSnoozeCriteria());
+ Assert.assertEquals(comment, a.canShowBadge(), b.canShowBadge());
+ Assert.assertEquals(comment, a.getUserSentiment(), b.getUserSentiment());
+ Assert.assertEquals(comment, a.isSuspended(), b.isSuspended());
+ Assert.assertEquals(comment, a.getLastAudiblyAlertedMillis(),
+ b.getLastAudiblyAlertedMillis());
+ Assert.assertEquals(comment, a.isNoisy(), b.isNoisy());
+ Assert.assertEquals(comment, a.getSmartReplies(), b.getSmartReplies());
+ Assert.assertEquals(comment, a.canBubble(), b.canBubble());
+ Assert.assertEquals(comment, a.isConversation(), b.isConversation());
+ if (a.getConversationShortcutInfo() != null && b.getConversationShortcutInfo() != null) {
+ Assert.assertEquals(comment, a.getConversationShortcutInfo().getId(),
+ b.getConversationShortcutInfo().getId());
+ } else {
+ // One or both must be null, so we can check for equality.
+ Assert.assertEquals(a.getConversationShortcutInfo(), b.getConversationShortcutInfo());
+ }
+ assertActionsEqual(a.getSmartActions(), b.getSmartActions());
+ Assert.assertEquals(a.getProposedImportance(), b.getProposedImportance());
+ Assert.assertEquals(a.hasSensitiveContent(), b.hasSensitiveContent());
+ }
+
+ /**
+ * Checks that the two RankingMaps have identical keys, and that each Ranking object for
+ * each of those keys is identical.
+ */
+ public void detailedAssertEquals(NotificationListenerService.RankingMap a,
+ NotificationListenerService.RankingMap b) {
+ NotificationListenerService.Ranking arank = new NotificationListenerService.Ranking();
+ NotificationListenerService.Ranking brank = new NotificationListenerService.Ranking();
+ assertArrayEquals(a.getOrderedKeys(), b.getOrderedKeys());
+ for (String key : a.getOrderedKeys()) {
+ a.getRanking(key, arank);
+ b.getRanking(key, brank);
+ detailedAssertEquals("ranking for key <" + key + ">", arank, brank);
+ }
+ }
+
@Before
public void setUp() {
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;
+ }
};
}
@@ -74,8 +450,13 @@
SystemUiSystemPropertiesFlags.TEST_RESOLVER = null;
}
- public NotificationListenerService.Ranking createTestRanking(String key, int rank) {
+ /**
+ * Creates a mostly empty Test Ranking object with the specified key, rank, and smartActions.
+ */
+ public NotificationListenerService.Ranking createEmptyTestRanking(
+ String key, int rank, ArrayList<Notification.Action> actions) {
NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking();
+
ranking.populate(
/* key= */ key,
/* rank= */ rank,
@@ -93,7 +474,7 @@
/* hidden= */ false,
/* lastAudiblyAlertedMs= */ -1,
/* noisy= */ false,
- /* smartActions= */ null,
+ /* smartActions= */ actions,
/* smartReplies= */ null,
/* canBubble= */ false,
/* isTextChanged= */ false,
@@ -107,54 +488,111 @@
return ranking;
}
+ // Tests parceling of NotificationRankingUpdate, and by extension, RankingMap and Ranking.
@Test
- public void testRankingUpdate_rankingConstructor() {
- NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123);
- NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate(
- new NotificationListenerService.Ranking[]{ranking});
-
- NotificationListenerService.RankingMap retrievedRankings = rankingUpdate.getRankingMap();
- NotificationListenerService.Ranking retrievedRanking =
- new NotificationListenerService.Ranking();
- assertTrue(retrievedRankings.getRanking(TEST_KEY, retrievedRanking));
- assertEquals(123, retrievedRanking.getRank());
- }
-
- @Test
- public void testRankingUpdate_parcelConstructor() {
- NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123);
- NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate(
- new NotificationListenerService.Ranking[]{ranking});
-
- Parcel parceledRankingUpdate = Parcel.obtain();
- rankingUpdate.writeToParcel(parceledRankingUpdate, 0);
- parceledRankingUpdate.setDataPosition(0);
-
- NotificationRankingUpdate retrievedRankingUpdate = new NotificationRankingUpdate(
- parceledRankingUpdate);
-
- NotificationListenerService.RankingMap retrievedRankings =
- retrievedRankingUpdate.getRankingMap();
- assertNotNull(retrievedRankings);
+ public void testRankingUpdate_parcel() {
+ NotificationRankingUpdate nru = generateUpdate(getContext());
+ Parcel parcel = Parcel.obtain();
+ nru.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ NotificationRankingUpdate nru1 = NotificationRankingUpdate.CREATOR.createFromParcel(parcel);
// The rankingUpdate file descriptor is only non-null in the new path.
if (SystemUiSystemPropertiesFlags.getResolver().isEnabled(
SystemUiSystemPropertiesFlags.NotificationFlags.RANKING_UPDATE_ASHMEM)) {
- assertTrue(retrievedRankingUpdate.isFdNotNullAndClosed());
+ assertTrue(nru1.isFdNotNullAndClosed());
}
- NotificationListenerService.Ranking retrievedRanking =
- new NotificationListenerService.Ranking();
- assertTrue(retrievedRankings.getRanking(TEST_KEY, retrievedRanking));
- assertEquals(123, retrievedRanking.getRank());
- assertTrue(retrievedRankingUpdate.equals(rankingUpdate));
- parceledRankingUpdate.recycle();
+ detailedAssertEquals(nru, nru1);
+ parcel.recycle();
+ }
+
+ // Tests parceling of RankingMap and RankingMap.equals
+ @Test
+ public void testRankingMap_parcel() {
+ NotificationListenerService.RankingMap rmap = generateUpdate(getContext()).getRankingMap();
+ Parcel parcel = Parcel.obtain();
+ rmap.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ NotificationListenerService.RankingMap rmap1 =
+ NotificationListenerService.RankingMap.CREATOR.createFromParcel(parcel);
+
+ detailedAssertEquals(rmap, rmap1);
+ Assert.assertEquals(rmap, rmap1);
+ parcel.recycle();
+ }
+
+ // Tests parceling of Ranking and Ranking.equals
+ @Test
+ public void testRanking_parcel() {
+ NotificationListenerService.Ranking ranking =
+ generateUpdate(getContext()).getRankingMap().getRawRankingObject(mKeys[0]);
+ Parcel parcel = Parcel.obtain();
+ ranking.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ NotificationListenerService.Ranking ranking1 =
+ new NotificationListenerService.Ranking(parcel);
+ detailedAssertEquals("rankings differ: ", ranking, ranking1);
+ Assert.assertEquals(ranking, ranking1);
+ parcel.recycle();
+ }
+
+ // Tests NotificationRankingUpdate.equals(), and by extension, RankingMap and Ranking.
+ @Test
+ public void testRankingUpdate_equals_legacy() {
+ NotificationRankingUpdate nru = generateUpdate(getContext());
+ NotificationRankingUpdate nru2 = generateUpdate(getContext());
+ detailedAssertEquals(nru, nru2);
+ Assert.assertEquals(nru, nru2);
+ NotificationListenerService.Ranking tweak =
+ nru2.getRankingMap().getRawRankingObject(mKeys[0]);
+ tweak.populate(
+ tweak.getKey(),
+ tweak.getRank(),
+ !tweak.matchesInterruptionFilter(), // note the inversion here!
+ tweak.getLockscreenVisibilityOverride(),
+ tweak.getSuppressedVisualEffects(),
+ tweak.getImportance(),
+ tweak.getImportanceExplanation(),
+ tweak.getOverrideGroupKey(),
+ tweak.getChannel(),
+ (ArrayList) tweak.getAdditionalPeople(),
+ (ArrayList) tweak.getSnoozeCriteria(),
+ tweak.canShowBadge(),
+ tweak.getUserSentiment(),
+ tweak.isSuspended(),
+ tweak.getLastAudiblyAlertedMillis(),
+ tweak.isNoisy(),
+ (ArrayList) tweak.getSmartActions(),
+ (ArrayList) tweak.getSmartReplies(),
+ tweak.canBubble(),
+ tweak.isTextChanged(),
+ tweak.isConversation(),
+ tweak.getConversationShortcutInfo(),
+ tweak.getRankingAdjustment(),
+ tweak.isBubble(),
+ tweak.getProposedImportance(),
+ tweak.hasSensitiveContent()
+ );
+ assertNotEquals(nru, nru2);
+ }
+
+ @Test
+ public void testRankingUpdate_rankingConstructor() {
+ NotificationRankingUpdate nru = generateUpdate(getContext());
+ NotificationRankingUpdate constructedNru = new NotificationRankingUpdate(
+ new NotificationListenerService.Ranking[]{
+ nru.getRankingMap().getRawRankingObject(mKeys[0]),
+ nru.getRankingMap().getRawRankingObject(mKeys[1]),
+ nru.getRankingMap().getRawRankingObject(mKeys[2]),
+ nru.getRankingMap().getRawRankingObject(mKeys[3]),
+ nru.getRankingMap().getRawRankingObject(mKeys[4])
+ });
+
+ detailedAssertEquals(nru, constructedNru);
}
@Test
public void testRankingUpdate_emptyParcelInCheck() {
- NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123);
- NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate(
- new NotificationListenerService.Ranking[]{ranking});
-
+ NotificationRankingUpdate rankingUpdate = generateUpdate(getContext());
Parcel parceledRankingUpdate = Parcel.obtain();
rankingUpdate.writeToParcel(parceledRankingUpdate, 0);
@@ -163,37 +601,119 @@
NotificationRankingUpdate retrievedRankingUpdate = new NotificationRankingUpdate(
parceledRankingUpdate);
assertNull(retrievedRankingUpdate.getRankingMap());
+ parceledRankingUpdate.recycle();
}
@Test
public void testRankingUpdate_describeContents() {
- NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123);
- NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate(
- new NotificationListenerService.Ranking[]{ranking});
+ NotificationRankingUpdate rankingUpdate = generateUpdate(getContext());
assertEquals(0, rankingUpdate.describeContents());
}
@Test
public void testRankingUpdate_equals() {
- NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123);
+ NotificationListenerService.Ranking ranking = createEmptyTestRanking(TEST_KEY, 123, null);
NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate(
new NotificationListenerService.Ranking[]{ranking});
- // Reflexive equality.
- assertTrue(rankingUpdate.equals(rankingUpdate));
- // Null or wrong class inequality.
+ // Reflexive equality, including handling nulls properly
+ detailedAssertEquals(rankingUpdate, rankingUpdate);
+ // Null or wrong class inequality
assertFalse(rankingUpdate.equals(null));
assertFalse(rankingUpdate.equals(ranking));
- // Different ranking contents inequality.
- NotificationListenerService.Ranking ranking2 = createTestRanking(TEST_KEY, 456);
+ // Different rank inequality
+ NotificationListenerService.Ranking ranking2 = createEmptyTestRanking(TEST_KEY, 456, null);
NotificationRankingUpdate rankingUpdate2 = new NotificationRankingUpdate(
new NotificationListenerService.Ranking[]{ranking2});
assertFalse(rankingUpdate.equals(rankingUpdate2));
- // Same ranking contents equality.
- ranking2 = createTestRanking(TEST_KEY, 123);
+ // Different key inequality
+ ranking2 = createEmptyTestRanking(TEST_KEY + "DIFFERENT", 123, null);
rankingUpdate2 = new NotificationRankingUpdate(
new NotificationListenerService.Ranking[]{ranking2});
- assertTrue(rankingUpdate.equals(rankingUpdate2));
+ assertFalse(rankingUpdate.equals(rankingUpdate2));
+ }
+
+ @Test
+ public void testRankingUpdate_writesSmartActionToParcel() {
+ if (!mRankingUpdateAshmem) {
+ return;
+ }
+ ArrayList<Notification.Action> actions = new ArrayList<>();
+ PendingIntent intent = PendingIntent.getBroadcast(
+ getContext(),
+ 0 /*requestCode*/,
+ new Intent("ACTION_" + TEST_KEY),
+ PendingIntent.FLAG_IMMUTABLE /*flags*/);
+ actions.add(new Notification.Action.Builder(null /*icon*/, TEST_KEY, intent).build());
+
+ NotificationListenerService.Ranking ranking =
+ createEmptyTestRanking(TEST_KEY, 123, actions);
+ NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate(
+ new NotificationListenerService.Ranking[]{ranking});
+
+ Parcel parcel = Parcel.obtain();
+ rankingUpdate.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ SharedMemory fd = parcel.readParcelable(getClass().getClassLoader(), SharedMemory.class);
+ Bundle smartActionsBundle = parcel.readBundle(getClass().getClassLoader());
+
+ // Assert the file descriptor is valid
+ assertNotNull(fd);
+ assertFalse(fd.getFd() == -1);
+
+ // Assert that the smart action is in the parcel
+ assertNotNull(smartActionsBundle);
+ ArrayList<Notification.Action> recoveredActions =
+ smartActionsBundle.getParcelableArrayList(TEST_KEY, Notification.Action.class);
+ assertNotNull(recoveredActions);
+ assertEquals(actions.size(), recoveredActions.size());
+ assertEquals(actions.get(0).title.toString(), recoveredActions.get(0).title.toString());
+ parcel.recycle();
+ }
+
+ @Test
+ public void testRankingUpdate_handlesEmptySmartActionList() {
+ if (!mRankingUpdateAshmem) {
+ return;
+ }
+ ArrayList<Notification.Action> actions = new ArrayList<>();
+ NotificationListenerService.Ranking ranking =
+ createEmptyTestRanking(TEST_KEY, 123, actions);
+ NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate(
+ new NotificationListenerService.Ranking[]{ranking});
+
+ Parcel parcel = Parcel.obtain();
+ rankingUpdate.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ // Ensure that despite an empty actions list, we can still unparcel the update.
+ NotificationRankingUpdate newRankingUpdate = new NotificationRankingUpdate(parcel);
+ assertNotNull(newRankingUpdate);
+ assertNotNull(newRankingUpdate.getRankingMap());
+ detailedAssertEquals(rankingUpdate, newRankingUpdate);
+ parcel.recycle();
+ }
+
+ @Test
+ public void testRankingUpdate_handlesNullSmartActionList() {
+ if (!mRankingUpdateAshmem) {
+ return;
+ }
+ NotificationListenerService.Ranking ranking =
+ createEmptyTestRanking(TEST_KEY, 123, null);
+ NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate(
+ new NotificationListenerService.Ranking[]{ranking});
+
+ Parcel parcel = Parcel.obtain();
+ rankingUpdate.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ // Ensure that despite an empty actions list, we can still unparcel the update.
+ NotificationRankingUpdate newRankingUpdate = new NotificationRankingUpdate(parcel);
+ assertNotNull(newRankingUpdate);
+ assertNotNull(newRankingUpdate.getRankingMap());
+ detailedAssertEquals(rankingUpdate, newRankingUpdate);
+ parcel.recycle();
}
}
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/android/widget/DifferentialMotionFlingHelperTest.java b/core/tests/coretests/src/android/widget/DifferentialMotionFlingHelperTest.java
index 51c8bc0..cce2faf 100644
--- a/core/tests/coretests/src/android/widget/DifferentialMotionFlingHelperTest.java
+++ b/core/tests/coretests/src/android/widget/DifferentialMotionFlingHelperTest.java
@@ -20,6 +20,8 @@
import static org.junit.Assert.assertFalse;
import android.view.MotionEvent;
+import android.widget.flags.FakeFeatureFlagsImpl;
+import android.widget.flags.Flags;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -54,6 +56,8 @@
private final TestDifferentialMotionFlingTarget mFlingTarget =
new TestDifferentialMotionFlingTarget();
+ private final FakeFeatureFlagsImpl mFakeWidgetFeatureFlags = new FakeFeatureFlagsImpl();
+
private DifferentialMotionFlingHelper mFlingHelper;
@Before
@@ -62,7 +66,10 @@
ApplicationProvider.getApplicationContext(),
mFlingTarget,
mVelocityThresholdCalculator,
- mVelocityProvider);
+ mVelocityProvider,
+ mFakeWidgetFeatureFlags);
+ mFakeWidgetFeatureFlags.setFlag(
+ Flags.FLAG_ENABLE_PLATFORM_WIDGET_DIFFERENTIAL_MOTION_FLING, true);
}
@Test
@@ -139,6 +146,18 @@
}
@Test
+ public void flingFeatureFlagDisabled_noFlingCalculation() {
+ mFakeWidgetFeatureFlags.setFlag(
+ Flags.FLAG_ENABLE_PLATFORM_WIDGET_DIFFERENTIAL_MOTION_FLING, false);
+ mMinVelocity = 50;
+ mMaxVelocity = 100;
+ deliverEventWithVelocity(createPointerEvent(), MotionEvent.AXIS_VSCROLL, 60);
+
+ assertFalse(mVelocityCalculated);
+ assertEquals(0, mFlingTarget.mLastFlingVelocity, /* delta= */ 0);
+ }
+
+ @Test
public void negativeFlingVelocityAboveMaximum_velocityClamped() {
mMinVelocity = 50;
mMaxVelocity = 100;
diff --git a/core/tests/coretests/src/android/window/SystemPerformanceHinterTests.java b/core/tests/coretests/src/android/window/SystemPerformanceHinterTests.java
index 25f5819..6229530 100644
--- a/core/tests/coretests/src/android/window/SystemPerformanceHinterTests.java
+++ b/core/tests/coretests/src/android/window/SystemPerformanceHinterTests.java
@@ -18,6 +18,8 @@
import static android.os.PerformanceHintManager.Session.CPU_LOAD_RESET;
import static android.os.PerformanceHintManager.Session.CPU_LOAD_UP;
+import static android.view.Surface.FRAME_RATE_CATEGORY_DEFAULT;
+import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH;
import static android.view.SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN;
import static android.view.SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_SELF;
import static android.window.SystemPerformanceHinter.HINT_ADPF;
@@ -29,6 +31,7 @@
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -150,7 +153,11 @@
verify(mTransaction).setFrameRateSelectionStrategy(
eq(mDefaultDisplayRoot),
eq(FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN));
- verify(mTransaction).apply();
+ verify(mTransaction).setFrameRateCategory(
+ eq(mDefaultDisplayRoot),
+ eq(FRAME_RATE_CATEGORY_HIGH),
+ eq(false));
+ verify(mTransaction).applyAsyncUnsafe();
}
@Test
@@ -164,7 +171,11 @@
verify(mTransaction).setFrameRateSelectionStrategy(
eq(mDefaultDisplayRoot),
eq(FRAME_RATE_SELECTION_STRATEGY_SELF));
- verify(mTransaction).apply();
+ verify(mTransaction).setFrameRateCategory(
+ eq(mDefaultDisplayRoot),
+ eq(FRAME_RATE_CATEGORY_DEFAULT),
+ eq(false));
+ verify(mTransaction).applyAsyncUnsafe();
}
@Test
@@ -177,7 +188,7 @@
// Verify we call SF
verify(mTransaction).setEarlyWakeupStart();
- verify(mTransaction).apply();
+ verify(mTransaction).applyAsyncUnsafe();
}
@Test
@@ -189,7 +200,7 @@
// Verify we call SF
verify(mTransaction).setEarlyWakeupEnd();
- verify(mTransaction).apply();
+ verify(mTransaction).applyAsyncUnsafe();
}
@Test
@@ -231,8 +242,12 @@
verify(mTransaction).setFrameRateSelectionStrategy(
eq(mDefaultDisplayRoot),
eq(FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN));
+ verify(mTransaction).setFrameRateCategory(
+ eq(mDefaultDisplayRoot),
+ eq(FRAME_RATE_CATEGORY_HIGH),
+ eq(false));
verify(mTransaction).setEarlyWakeupStart();
- verify(mTransaction).apply();
+ verify(mTransaction).applyAsyncUnsafe();
verify(mAdpfSession).sendHint(eq(CPU_LOAD_UP));
}
@@ -248,8 +263,12 @@
verify(mTransaction).setFrameRateSelectionStrategy(
eq(mDefaultDisplayRoot),
eq(FRAME_RATE_SELECTION_STRATEGY_SELF));
+ verify(mTransaction).setFrameRateCategory(
+ eq(mDefaultDisplayRoot),
+ eq(FRAME_RATE_CATEGORY_DEFAULT),
+ eq(false));
verify(mTransaction).setEarlyWakeupEnd();
- verify(mTransaction).apply();
+ verify(mTransaction).applyAsyncUnsafe();
verify(mAdpfSession).sendHint(eq(CPU_LOAD_RESET));
}
@@ -265,8 +284,12 @@
verify(mTransaction).setFrameRateSelectionStrategy(
eq(mDefaultDisplayRoot),
eq(FRAME_RATE_SELECTION_STRATEGY_SELF));
+ verify(mTransaction).setFrameRateCategory(
+ eq(mDefaultDisplayRoot),
+ eq(FRAME_RATE_CATEGORY_DEFAULT),
+ eq(false));
verify(mTransaction).setEarlyWakeupEnd();
- verify(mTransaction).apply();
+ verify(mTransaction).applyAsyncUnsafe();
verify(mAdpfSession).sendHint(eq(CPU_LOAD_RESET));
}
}
@@ -280,8 +303,12 @@
verify(mTransaction).setFrameRateSelectionStrategy(
eq(mDefaultDisplayRoot),
eq(FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN));
+ verify(mTransaction).setFrameRateCategory(
+ eq(mDefaultDisplayRoot),
+ eq(FRAME_RATE_CATEGORY_HIGH),
+ eq(false));
verify(mTransaction).setEarlyWakeupStart();
- verify(mTransaction).apply();
+ verify(mTransaction).applyAsyncUnsafe();
verify(mAdpfSession).sendHint(eq(CPU_LOAD_UP));
reset(mTransaction);
reset(mAdpfSession);
@@ -290,15 +317,17 @@
mHinter.startSession(HINT_ALL, DEFAULT_DISPLAY_ID, TEST_OTHER_REASON);
// Verify we never call SF and perf manager since session1 is already running
verify(mTransaction, never()).setFrameRateSelectionStrategy(any(), anyInt());
+ verify(mTransaction, never()).setFrameRateCategory(any(), anyInt(), anyBoolean());
verify(mTransaction, never()).setEarlyWakeupEnd();
- verify(mTransaction, never()).apply();
+ verify(mTransaction, never()).applyAsyncUnsafe();
verify(mAdpfSession, never()).sendHint(anyInt());
session2.close();
// Verify we have not cleaned up because session1 is still running
verify(mTransaction, never()).setFrameRateSelectionStrategy(any(), anyInt());
+ verify(mTransaction, never()).setFrameRateCategory(any(), anyInt(), anyBoolean());
verify(mTransaction, never()).setEarlyWakeupEnd();
- verify(mTransaction, never()).apply();
+ verify(mTransaction, never()).applyAsyncUnsafe();
verify(mAdpfSession, never()).sendHint(anyInt());
session1.close();
@@ -306,8 +335,12 @@
verify(mTransaction).setFrameRateSelectionStrategy(
eq(mDefaultDisplayRoot),
eq(FRAME_RATE_SELECTION_STRATEGY_SELF));
+ verify(mTransaction).setFrameRateCategory(
+ eq(mDefaultDisplayRoot),
+ eq(FRAME_RATE_CATEGORY_DEFAULT),
+ eq(false));
verify(mTransaction).setEarlyWakeupEnd();
- verify(mTransaction).apply();
+ verify(mTransaction).applyAsyncUnsafe();
verify(mAdpfSession).sendHint(eq(CPU_LOAD_RESET));
}
@@ -321,8 +354,12 @@
verify(mTransaction).setFrameRateSelectionStrategy(
eq(mDefaultDisplayRoot),
eq(FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN));
+ verify(mTransaction).setFrameRateCategory(
+ eq(mDefaultDisplayRoot),
+ eq(FRAME_RATE_CATEGORY_HIGH),
+ eq(false));
verify(mTransaction).setEarlyWakeupStart();
- verify(mTransaction).apply();
+ verify(mTransaction).applyAsyncUnsafe();
verify(mAdpfSession).sendHint(eq(CPU_LOAD_UP));
reset(mTransaction);
reset(mAdpfSession);
@@ -333,8 +370,12 @@
verify(mTransaction).setFrameRateSelectionStrategy(
eq(mSecondaryDisplayRoot),
eq(FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN));
+ verify(mTransaction).setFrameRateCategory(
+ eq(mSecondaryDisplayRoot),
+ eq(FRAME_RATE_CATEGORY_HIGH),
+ eq(false));
verify(mTransaction, never()).setEarlyWakeupStart();
- verify(mTransaction).apply();
+ verify(mTransaction).applyAsyncUnsafe();
verify(mAdpfSession, never()).sendHint(anyInt());
reset(mTransaction);
reset(mAdpfSession);
@@ -345,11 +386,19 @@
verify(mTransaction).setFrameRateSelectionStrategy(
eq(mDefaultDisplayRoot),
eq(FRAME_RATE_SELECTION_STRATEGY_SELF));
+ verify(mTransaction).setFrameRateCategory(
+ eq(mDefaultDisplayRoot),
+ eq(FRAME_RATE_CATEGORY_DEFAULT),
+ eq(false));
verify(mTransaction, never()).setFrameRateSelectionStrategy(
eq(mSecondaryDisplayRoot),
anyInt());
+ verify(mTransaction, never()).setFrameRateCategory(
+ eq(mSecondaryDisplayRoot),
+ anyInt(),
+ eq(false));
verify(mTransaction, never()).setEarlyWakeupEnd();
- verify(mTransaction).apply();
+ verify(mTransaction).applyAsyncUnsafe();
verify(mAdpfSession, never()).sendHint(anyInt());
reset(mTransaction);
reset(mAdpfSession);
@@ -362,8 +411,12 @@
verify(mTransaction).setFrameRateSelectionStrategy(
eq(mSecondaryDisplayRoot),
eq(FRAME_RATE_SELECTION_STRATEGY_SELF));
+ verify(mTransaction).setFrameRateCategory(
+ eq(mSecondaryDisplayRoot),
+ eq(FRAME_RATE_CATEGORY_DEFAULT),
+ eq(false));
verify(mTransaction).setEarlyWakeupEnd();
- verify(mTransaction).apply();
+ verify(mTransaction).applyAsyncUnsafe();
verify(mAdpfSession).sendHint(eq(CPU_LOAD_RESET));
}
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/core/tests/hdmitests/Android.bp b/core/tests/hdmitests/Android.bp
index 3d04937..5f6eaf9 100644
--- a/core/tests/hdmitests/Android.bp
+++ b/core/tests/hdmitests/Android.bp
@@ -30,7 +30,7 @@
"frameworks-base-testutils",
"guava-android-testlib",
"platform-test-annotations",
- "truth-prebuilt",
+ "truth",
],
libs: ["android.test.runner"],
platform_apis: true,
diff --git a/core/tests/mockingcoretests/Android.bp b/core/tests/mockingcoretests/Android.bp
index fde7c08..2d778b1 100644
--- a/core/tests/mockingcoretests/Android.bp
+++ b/core/tests/mockingcoretests/Android.bp
@@ -38,7 +38,7 @@
"androidx.test.ext.junit",
"mockito-target-extended-minus-junit4",
"platform-test-annotations",
- "truth-prebuilt",
+ "truth",
"testables",
],
diff --git a/core/tests/nfctests/Android.bp b/core/tests/nfctests/Android.bp
index c74600b..f81be49 100644
--- a/core/tests/nfctests/Android.bp
+++ b/core/tests/nfctests/Android.bp
@@ -27,7 +27,7 @@
"androidx.test.ext.junit",
"androidx.test.rules",
"mockito-target-minus-junit4",
- "truth-prebuilt",
+ "truth",
],
libs: [
"android.test.runner",
diff --git a/core/tests/overlaytests/device_self_targeting/Android.bp b/core/tests/overlaytests/device_self_targeting/Android.bp
index 063c569..931eac5 100644
--- a/core/tests/overlaytests/device_self_targeting/Android.bp
+++ b/core/tests/overlaytests/device_self_targeting/Android.bp
@@ -30,7 +30,7 @@
"androidx.test.runner",
"androidx.test.ext.junit",
"mockito-target-minus-junit4",
- "truth-prebuilt",
+ "truth",
],
optimize: {
diff --git a/core/tests/packagemonitortests/Android.bp b/core/tests/packagemonitortests/Android.bp
index 7b5d7df..b08850e 100644
--- a/core/tests/packagemonitortests/Android.bp
+++ b/core/tests/packagemonitortests/Android.bp
@@ -32,7 +32,7 @@
"compatibility-device-util-axt",
"frameworks-base-testutils",
"mockito-target-minus-junit4",
- "truth-prebuilt",
+ "truth",
],
libs: ["android.test.runner"],
platform_apis: true,
diff --git a/core/tests/privacytests/Android.bp b/core/tests/privacytests/Android.bp
index bc3dd81..4e24cd5 100644
--- a/core/tests/privacytests/Android.bp
+++ b/core/tests/privacytests/Android.bp
@@ -14,7 +14,7 @@
"junit",
"rappor-tests",
"androidx.test.rules",
- "truth-prebuilt",
+ "truth",
],
libs: ["android.test.runner"],
platform_apis: true,
diff --git a/core/tests/utiltests/Android.bp b/core/tests/utiltests/Android.bp
index 3798da5..580e73c 100644
--- a/core/tests/utiltests/Android.bp
+++ b/core/tests/utiltests/Android.bp
@@ -33,7 +33,7 @@
"frameworks-base-testutils",
"mockito-target-minus-junit4",
"androidx.test.ext.junit",
- "truth-prebuilt",
+ "truth",
"servicestests-utils",
],
diff --git a/core/tests/vibrator/Android.bp b/core/tests/vibrator/Android.bp
index 829409a..09608e9 100644
--- a/core/tests/vibrator/Android.bp
+++ b/core/tests/vibrator/Android.bp
@@ -18,7 +18,7 @@
"androidx.test.runner",
"androidx.test.rules",
"mockito-target-minus-junit4",
- "truth-prebuilt",
+ "truth",
"testng",
],
diff --git a/errorprone/Android.bp b/errorprone/Android.bp
index ad08026..c1d2235 100644
--- a/errorprone/Android.bp
+++ b/errorprone/Android.bp
@@ -41,7 +41,7 @@
java_resource_dirs: ["tests/res"],
java_resources: [":error_prone_android_framework_testdata"],
static_libs: [
- "truth-prebuilt",
+ "truth",
"kxml2-2.3.0",
"compile-testing-prebuilt",
"error_prone_android_framework_lib",
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.bp b/graphics/java/Android.bp
index 63d1f6d..db37a387 100644
--- a/graphics/java/Android.bp
+++ b/graphics/java/Android.bp
@@ -7,6 +7,12 @@
default_applicable_licenses: ["frameworks_base_license"],
}
+aconfig_declarations {
+ name: "framework_graphics_flags",
+ package: "com.android.graphics.flags",
+ srcs: ["android/framework_graphics.aconfig"],
+}
+
filegroup {
name: "framework-graphics-nonupdatable-sources",
srcs: [
diff --git a/graphics/java/android/framework_graphics.aconfig b/graphics/java/android/framework_graphics.aconfig
new file mode 100644
index 0000000..e030dad
--- /dev/null
+++ b/graphics/java/android/framework_graphics.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.graphics.flags"
+
+flag {
+ name: "exact_compute_bounds"
+ namespace: "framework_graphics"
+ description: "Add a function without unused exact param for computeBounds."
+ bug: "304478551"
+}
\ No newline at end of file
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index e7814cb..d1aceaf 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -114,6 +114,7 @@
throw new IllegalStateException("Immutable bitmap passed to Canvas constructor");
}
throwIfCannotDraw(bitmap);
+ bitmap.setGainmap(null);
mNativeCanvasWrapper = nInitRaster(bitmap.getNativeInstance());
mFinalizer = NoImagePreloadHolder.sRegistry.registerNativeAllocation(
this, mNativeCanvasWrapper);
@@ -178,7 +179,7 @@
throw new IllegalStateException();
}
throwIfCannotDraw(bitmap);
-
+ bitmap.setGainmap(null);
nSetBitmap(mNativeCanvasWrapper, bitmap.getNativeInstance());
mDensity = bitmap.mDensity;
}
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index 735bc18..52b0b95 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -236,7 +236,9 @@
}
}
- return new FontConfig(families, filtered, resultNamedFamilies, lastModifiedDate,
+ return new FontConfig(families, filtered, resultNamedFamilies,
+ customization.getLocaleFamilyCustomizations(),
+ lastModifiedDate,
configVersion);
}
diff --git a/graphics/java/android/graphics/Gainmap.java b/graphics/java/android/graphics/Gainmap.java
index b5fb13d..0a6fb84 100644
--- a/graphics/java/android/graphics/Gainmap.java
+++ b/graphics/java/android/graphics/Gainmap.java
@@ -16,11 +16,14 @@
package android.graphics;
+import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.graphics.hwui.flags.Flags;
+
import libcore.util.NativeAllocationRegistry;
/**
@@ -125,6 +128,7 @@
* Creates a new gainmap using the provided gainmap as the metadata source and the provided
* bitmap as the replacement for the gainmapContents
*/
+ @FlaggedApi(Flags.FLAG_GAINMAP_CONSTRUCTOR_WITH_METADATA)
public Gainmap(@NonNull Gainmap gainmap, @NonNull Bitmap gainmapContents) {
this(gainmapContents, nCreateCopy(gainmap.mNativePtr));
}
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/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index c9c1b23..deb89fa 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -16,11 +16,14 @@
package android.graphics;
+import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
+import com.android.graphics.flags.Flags;
+
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
@@ -309,6 +312,7 @@
*
* @param bounds Returns the computed bounds of the path's control points.
*/
+ @FlaggedApi(Flags.FLAG_EXACT_COMPUTE_BOUNDS)
public void computeBounds(@NonNull RectF bounds) {
nComputeBounds(mNativePath, bounds);
}
diff --git a/graphics/java/android/graphics/drawable/RippleShader.java b/graphics/java/android/graphics/drawable/RippleShader.java
index 4461f39..c2a7a84 100644
--- a/graphics/java/android/graphics/drawable/RippleShader.java
+++ b/graphics/java/android/graphics/drawable/RippleShader.java
@@ -109,9 +109,8 @@
+ " float alpha = min(fadeIn, 1. - fadeOutNoise);\n"
+ " vec2 uv = p * in_resolutionScale;\n"
+ " vec2 densityUv = uv - mod(uv, in_noiseScale);\n"
- + " float turbulence = turbulence(uv, in_turbulencePhase);\n"
- + " float sparkleAlpha = sparkles(densityUv, in_noisePhase) * ring * alpha "
- + "* turbulence;\n"
+ + " float turb = turbulence(uv, in_turbulencePhase);\n"
+ + " float sparkleAlpha = sparkles(densityUv, in_noisePhase) * ring * alpha * turb;\n"
+ " float fade = min(fadeIn, 1. - fadeOutRipple);\n"
+ " float waveAlpha = softCircle(p, center, in_maxRadius * scaleIn, 1.) * fade "
+ "* in_color.a;\n"
diff --git a/graphics/java/android/graphics/fonts/FontCustomizationParser.java b/graphics/java/android/graphics/fonts/FontCustomizationParser.java
index b458dd9..6e04a2f 100644
--- a/graphics/java/android/graphics/fonts/FontCustomizationParser.java
+++ b/graphics/java/android/graphics/fonts/FontCustomizationParser.java
@@ -22,6 +22,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.FontListParser;
+import android.text.FontConfig;
import android.util.Xml;
import org.xmlpull.v1.XmlPullParser;
@@ -34,6 +35,7 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
/**
@@ -52,14 +54,19 @@
private final List<Alias> mAdditionalAliases;
+ private final List<FontConfig.Customization.LocaleFallback> mLocaleFamilyCustomizations;
+
public Result() {
mAdditionalNamedFamilies = Collections.emptyMap();
+ mLocaleFamilyCustomizations = Collections.emptyList();
mAdditionalAliases = Collections.emptyList();
}
public Result(Map<String, NamedFamilyList> additionalNamedFamilies,
+ List<FontConfig.Customization.LocaleFallback> localeFamilyCustomizations,
List<Alias> additionalAliases) {
mAdditionalNamedFamilies = additionalNamedFamilies;
+ mLocaleFamilyCustomizations = localeFamilyCustomizations;
mAdditionalAliases = additionalAliases;
}
@@ -70,6 +77,10 @@
public List<Alias> getAdditionalAliases() {
return mAdditionalAliases;
}
+
+ public List<FontConfig.Customization.LocaleFallback> getLocaleFamilyCustomizations() {
+ return mLocaleFamilyCustomizations;
+ }
}
/**
@@ -89,7 +100,9 @@
}
private static Result validateAndTransformToResult(
- List<NamedFamilyList> families, List<Alias> aliases) {
+ List<NamedFamilyList> families,
+ List<FontConfig.Customization.LocaleFallback> outLocaleFamilies,
+ List<Alias> aliases) {
HashMap<String, NamedFamilyList> namedFamily = new HashMap<>();
for (int i = 0; i < families.size(); ++i) {
final NamedFamilyList family = families.get(i);
@@ -105,7 +118,7 @@
+ "requires fallackTarget attribute");
}
}
- return new Result(namedFamily, aliases);
+ return new Result(namedFamily, outLocaleFamilies, aliases);
}
private static Result readFamilies(
@@ -115,12 +128,13 @@
) throws XmlPullParserException, IOException {
List<NamedFamilyList> families = new ArrayList<>();
List<Alias> aliases = new ArrayList<>();
+ List<FontConfig.Customization.LocaleFallback> outLocaleFamilies = new ArrayList<>();
parser.require(XmlPullParser.START_TAG, null, "fonts-modification");
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) continue;
String tag = parser.getName();
if (tag.equals("family")) {
- readFamily(parser, fontDir, families, updatableFontMap);
+ readFamily(parser, fontDir, families, outLocaleFamilies, updatableFontMap);
} else if (tag.equals("family-list")) {
readFamilyList(parser, fontDir, families, updatableFontMap);
} else if (tag.equals("alias")) {
@@ -129,13 +143,14 @@
FontListParser.skip(parser);
}
}
- return validateAndTransformToResult(families, aliases);
+ return validateAndTransformToResult(families, outLocaleFamilies, aliases);
}
private static void readFamily(
@NonNull XmlPullParser parser,
@NonNull String fontDir,
@NonNull List<NamedFamilyList> out,
+ @NonNull List<FontConfig.Customization.LocaleFallback> outCustomization,
@Nullable Map<String, File> updatableFontMap)
throws XmlPullParserException, IOException {
final String customizationType = parser.getAttributeValue(null, "customizationType");
@@ -148,6 +163,29 @@
if (fontFamily != null) {
out.add(fontFamily);
}
+ } else if (customizationType.equals("new-locale-family")) {
+ final String lang = parser.getAttributeValue(null, "lang");
+ final String op = parser.getAttributeValue(null, "operation");
+ final int intOp;
+ if (op.equals("append")) {
+ intOp = FontConfig.Customization.LocaleFallback.OPERATION_APPEND;
+ } else if (op.equals("prepend")) {
+ intOp = FontConfig.Customization.LocaleFallback.OPERATION_PREPEND;
+ } else if (op.equals("replace")) {
+ intOp = FontConfig.Customization.LocaleFallback.OPERATION_REPLACE;
+ } else {
+ throw new IllegalArgumentException("Unknown operation=" + op);
+ }
+
+ final FontConfig.FontFamily family = FontListParser.readFamily(
+ parser, fontDir, updatableFontMap, false);
+
+ // For ignoring the customization, consume the new-locale-family element but don't
+ // register any customizations.
+ if (com.android.text.flags.Flags.customLocaleFallback()) {
+ outCustomization.add(new FontConfig.Customization.LocaleFallback(
+ Locale.forLanguageTag(lang), intOp, family));
+ }
} else {
throw new IllegalArgumentException("Unknown customizationType=" + customizationType);
}
diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java
index d4e35b3..618aa5b 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -16,10 +16,15 @@
package android.graphics.fonts;
+import static android.text.FontConfig.Customization.LocaleFallback.OPERATION_APPEND;
+import static android.text.FontConfig.Customization.LocaleFallback.OPERATION_PREPEND;
+import static android.text.FontConfig.Customization.LocaleFallback.OPERATION_REPLACE;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.FontListParser;
import android.graphics.Typeface;
+import android.os.LocaleList;
import android.text.FontConfig;
import android.util.ArrayMap;
import android.util.Log;
@@ -38,6 +43,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Set;
@@ -119,7 +125,6 @@
}
}
-
final FontFamily defaultFamily = defaultFonts.isEmpty() ? null : createFontFamily(
defaultFonts, languageTags, variant, xmlFamily.getVariableFontFamilyType(), false,
cache);
@@ -300,11 +305,11 @@
} catch (IOException e) {
Log.e(TAG, "Failed to open/read system font configurations.", e);
return new FontConfig(Collections.emptyList(), Collections.emptyList(),
- Collections.emptyList(), 0, 0);
+ Collections.emptyList(), Collections.emptyList(), 0, 0);
} catch (XmlPullParserException e) {
Log.e(TAG, "Failed to parse the system font configuration.", e);
return new FontConfig(Collections.emptyList(), Collections.emptyList(),
- Collections.emptyList(), 0, 0);
+ Collections.emptyList(), Collections.emptyList(), 0, 0);
}
}
@@ -328,6 +333,8 @@
ArrayMap<String, ByteBuffer> outBufferCache) {
final ArrayMap<String, NativeFamilyListSet> fallbackListMap = new ArrayMap<>();
+ final List<FontConfig.Customization.LocaleFallback> localeFallbacks =
+ fontConfig.getLocaleFallbackCustomizations();
final List<FontConfig.NamedFamilyList> namedFamilies = fontConfig.getNamedFamilyLists();
for (int i = 0; i < namedFamilies.size(); ++i) {
@@ -336,10 +343,54 @@
}
// Then, add fallback fonts to the fallback map.
+ final List<FontConfig.Customization.LocaleFallback> customizations = new ArrayList<>();
final List<FontConfig.FontFamily> xmlFamilies = fontConfig.getFontFamilies();
+ final SparseIntArray seenCustomization = new SparseIntArray();
for (int i = 0; i < xmlFamilies.size(); i++) {
final FontConfig.FontFamily xmlFamily = xmlFamilies.get(i);
- pushFamilyToFallback(xmlFamily, fallbackListMap, outBufferCache);
+
+ customizations.clear();
+ for (int j = 0; j < localeFallbacks.size(); ++j) {
+ if (seenCustomization.get(j, -1) != -1) {
+ continue; // The customization is already applied.
+ }
+ FontConfig.Customization.LocaleFallback localeFallback = localeFallbacks.get(j);
+ if (scriptMatch(xmlFamily.getLocaleList(), localeFallback.getScript())) {
+ customizations.add(localeFallback);
+ seenCustomization.put(j, 1);
+ }
+ }
+
+ if (customizations.isEmpty()) {
+ pushFamilyToFallback(xmlFamily, fallbackListMap, outBufferCache);
+ } else {
+ for (int j = 0; j < customizations.size(); ++j) {
+ FontConfig.Customization.LocaleFallback localeFallback = customizations.get(j);
+ if (localeFallback.getOperation() == OPERATION_PREPEND) {
+ pushFamilyToFallback(localeFallback.getFamily(), fallbackListMap,
+ outBufferCache);
+ }
+ }
+ boolean isReplaced = false;
+ for (int j = 0; j < customizations.size(); ++j) {
+ FontConfig.Customization.LocaleFallback localeFallback = customizations.get(j);
+ if (localeFallback.getOperation() == OPERATION_REPLACE) {
+ pushFamilyToFallback(localeFallback.getFamily(), fallbackListMap,
+ outBufferCache);
+ isReplaced = true;
+ }
+ }
+ if (!isReplaced) { // If nothing is replaced, push the original one.
+ pushFamilyToFallback(xmlFamily, fallbackListMap, outBufferCache);
+ }
+ for (int j = 0; j < customizations.size(); ++j) {
+ FontConfig.Customization.LocaleFallback localeFallback = customizations.get(j);
+ if (localeFallback.getOperation() == OPERATION_APPEND) {
+ pushFamilyToFallback(localeFallback.getFamily(), fallbackListMap,
+ outBufferCache);
+ }
+ }
+ }
}
// Build the font map and fallback map.
@@ -365,4 +416,42 @@
Typeface.initSystemDefaultTypefaces(fallbackMap, fontConfig.getAliases(), result);
return result;
}
+
+ private static boolean scriptMatch(LocaleList localeList, String targetScript) {
+ if (localeList == null || localeList.isEmpty()) {
+ return false;
+ }
+ for (int i = 0; i < localeList.size(); ++i) {
+ Locale locale = localeList.get(i);
+ if (locale == null) {
+ continue;
+ }
+ String baseScript = FontConfig.resolveScript(locale);
+ if (baseScript.equals(targetScript)) {
+ return true;
+ }
+
+ // Subtag match
+ if (targetScript.equals("Bopo") && baseScript.equals("Hanb")) {
+ // Hanb is Han with Bopomofo.
+ return true;
+ } else if (targetScript.equals("Hani")) {
+ if (baseScript.equals("Hanb") || baseScript.equals("Hans")
+ || baseScript.equals("Hant") || baseScript.equals("Kore")
+ || baseScript.equals("Jpan")) {
+ // Han id suppoted by Taiwanese, Traditional Chinese, Simplified Chinese, Korean
+ // and Japanese.
+ return true;
+ }
+ } else if (targetScript.equals("Hira") || targetScript.equals("Hrkt")
+ || targetScript.equals("Kana")) {
+ if (baseScript.equals("Jpan") || baseScript.equals("Hrkt")) {
+ // Hiragana, Hiragana-Katakana, Katakana is supported by Japanese and
+ // Hiragana-Katakana script.
+ return true;
+ }
+ }
+ }
+ return false;
+ }
}
diff --git a/graphics/java/android/graphics/text/LineBreakConfig.java b/graphics/java/android/graphics/text/LineBreakConfig.java
index e81525f..f5e5803 100644
--- a/graphics/java/android/graphics/text/LineBreakConfig.java
+++ b/graphics/java/android/graphics/text/LineBreakConfig.java
@@ -134,10 +134,25 @@
*/
public static final int LINE_BREAK_STYLE_STRICT = 3;
+ /**
+ * The line break style that used for preventing automatic line breaking.
+ *
+ * This is useful when you want to preserve some words in the same line by using
+ * {@link android.text.style.LineBreakConfigSpan} or
+ * {@link android.text.style.LineBreakConfigSpan.NoBreakSpan} as a shorthand.
+ * Note that even if this style is specified, the grapheme based line break is still performed
+ * for preventing clipping text.
+ *
+ * @see android.text.style.LineBreakConfigSpan
+ * @see android.text.style.LineBreakConfigSpan.NoBreakSpan
+ */
+ @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
+ public static final int LINE_BREAK_STYLE_NO_BREAK = 4;
+
/** @hide */
@IntDef(prefix = { "LINE_BREAK_STYLE_" }, value = {
LINE_BREAK_STYLE_NONE, LINE_BREAK_STYLE_LOOSE, LINE_BREAK_STYLE_NORMAL,
- LINE_BREAK_STYLE_STRICT, LINE_BREAK_STYLE_UNSPECIFIED
+ LINE_BREAK_STYLE_STRICT, LINE_BREAK_STYLE_UNSPECIFIED, LINE_BREAK_STYLE_NO_BREAK
})
@Retention(RetentionPolicy.SOURCE)
public @interface LineBreakStyle {}
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/keystore/java/android/security/AndroidKeyStoreMaintenance.java b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
index 0f3488b..31c2eb2 100644
--- a/keystore/java/android/security/AndroidKeyStoreMaintenance.java
+++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
@@ -28,8 +28,8 @@
import android.util.Log;
/**
- * @hide This is the client side for IKeystoreUserManager AIDL.
- * It shall only be used by the LockSettingsService.
+ * @hide This is the client side for IKeystoreMaintenance AIDL.
+ * It is used mainly by LockSettingsService.
*/
public class AndroidKeyStoreMaintenance {
private static final String TAG = "AndroidKeyStoreMaintenance";
@@ -66,7 +66,7 @@
}
/**
- * Informs Keystore 2.0 about removing a usergit mer
+ * Informs Keystore 2.0 about removing a user
*
* @param userId - Android user id of the user being removed
* @return 0 if successful or a {@code ResponseCode}
@@ -91,7 +91,7 @@
*
* @param userId - Android user id of the user
* @param password - a secret derived from the synthetic password provided by the
- * LockSettingService
+ * LockSettingsService
* @return 0 if successful or a {@code ResponseCode}
* @hide
*/
@@ -110,7 +110,7 @@
}
/**
- * Informs Keystore 2.0 that an app was uninstalled and the corresponding namspace is to
+ * Informs Keystore 2.0 that an app was uninstalled and the corresponding namespace is to
* be cleared.
*/
public static int clearNamespace(@Domain int domain, long namespace) {
@@ -172,10 +172,10 @@
* namespace.
*
* @return * 0 on success
- * * KEY_NOT_FOUND if the source did not exists.
+ * * KEY_NOT_FOUND if the source did not exist.
* * PERMISSION_DENIED if any of the required permissions was missing.
* * INVALID_ARGUMENT if the destination was occupied or any domain value other than
- * the allowed once were specified.
+ * the allowed ones was specified.
* * SYSTEM_ERROR if an unexpected error occurred.
*/
public static int migrateKeyNamespace(KeyDescriptor source, KeyDescriptor destination) {
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index c55a781..11278e8 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -43,6 +43,7 @@
import java.security.interfaces.RSAPublicKey;
import javax.crypto.Cipher;
+import javax.crypto.KeyAgreement;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
@@ -181,6 +182,8 @@
spi = ((Mac) cryptoPrimitive).getCurrentSpi();
} else if (cryptoPrimitive instanceof Cipher) {
spi = ((Cipher) cryptoPrimitive).getCurrentSpi();
+ } else if (cryptoPrimitive instanceof KeyAgreement) {
+ spi = ((KeyAgreement) cryptoPrimitive).getCurrentSpi();
} else {
throw new IllegalArgumentException("Unsupported crypto primitive: " + cryptoPrimitive
+ ". Supported: Signature, Mac, Cipher");
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/Jetpack/tests/unittest/Android.bp b/libs/WindowManager/Jetpack/tests/unittest/Android.bp
index b6e743a..ed2ff2d 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/Android.bp
+++ b/libs/WindowManager/Jetpack/tests/unittest/Android.bp
@@ -37,7 +37,7 @@
"androidx.test.rules",
"androidx.test.ext.junit",
"mockito-target-extended-minus-junit4",
- "truth-prebuilt",
+ "truth",
"testables",
"platform-test-annotations",
],
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/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 7a309f5..1f6f7ae 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -143,7 +143,9 @@
<dimen name="bubble_expanded_view_padding">16dp</dimen>
<!-- Padding for the edge of the expanded view that is closest to the edge of the screen used
when displaying in landscape on a large screen. -->
- <dimen name="bubble_expanded_view_largescreen_landscape_padding">128dp</dimen>
+ <dimen name="bubble_expanded_view_largescreen_landscape_padding">102dp</dimen>
+ <!-- The width of the expanded view on large screens. -->
+ <dimen name="bubble_expanded_view_largescreen_width">540dp</dimen>
<!-- This should be at least the size of bubble_expanded_view_padding; it is used to include
a slight touch slop around the expanded view. -->
<dimen name="bubble_expanded_view_slop">8dp</dimen>
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/bubbles/BadgedImageView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
index f1ee8fa..a67821b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
@@ -318,7 +318,7 @@
/**
* Animates the dot to the given scale, running the optional callback when the animation ends.
*/
- private void animateDotScale(float toScale, @Nullable Runnable after) {
+ public void animateDotScale(float toScale, @Nullable Runnable after) {
mDotIsAnimating = true;
// Don't restart the animation if we're already animating to the given value.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
index df197572..dc099d9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
@@ -27,6 +27,7 @@
import android.graphics.drawable.InsetDrawable
import android.util.PathParser
import android.view.LayoutInflater
+import android.view.View.VISIBLE
import android.widget.FrameLayout
import com.android.launcher3.icons.BubbleIconFactory
import com.android.wm.shell.R
@@ -156,7 +157,9 @@
fun setShowDot(show: Boolean) {
showDot = show
- overflowBtn?.updateDotVisibility(true /* animate */)
+ if (overflowBtn?.visibility == VISIBLE) {
+ overflowBtn?.updateDotVisibility(true /* animate */)
+ }
}
/** Creates the expanded view for bubbles showing in the stack view. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index ea7053d..17e06e9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -54,10 +54,6 @@
public static final float FLYOUT_MAX_WIDTH_PERCENT_LARGE_SCREEN = 0.3f;
/** The max percent of screen width to use for the flyout on phone. */
public static final float FLYOUT_MAX_WIDTH_PERCENT = 0.6f;
- /** The percent of screen width for the expanded view on a large screen. **/
- private static final float EXPANDED_VIEW_LARGE_SCREEN_LANDSCAPE_WIDTH_PERCENT = 0.48f;
- /** The percent of screen width for the expanded view on a large screen. **/
- private static final float EXPANDED_VIEW_LARGE_SCREEN_PORTRAIT_WIDTH_PERCENT = 0.70f;
/** The percent of screen width for the expanded view on a small tablet. **/
private static final float EXPANDED_VIEW_SMALL_TABLET_WIDTH_PERCENT = 0.72f;
/** The percent of screen width for the expanded view when shown in the bubble bar. **/
@@ -95,6 +91,7 @@
private int mPointerWidth;
private int mPointerHeight;
private int mPointerOverlap;
+ private int mManageButtonHeightIncludingMargins;
private int mManageButtonHeight;
private int mOverflowHeight;
private int mMinimumFlyoutWidthLargeScreen;
@@ -176,21 +173,20 @@
mExpandedViewLargeScreenWidth = (int) (bounds.width()
* EXPANDED_VIEW_SMALL_TABLET_WIDTH_PERCENT);
} else {
- mExpandedViewLargeScreenWidth = isLandscape()
- ? (int) (bounds.width() * EXPANDED_VIEW_LARGE_SCREEN_LANDSCAPE_WIDTH_PERCENT)
- : (int) (bounds.width() * EXPANDED_VIEW_LARGE_SCREEN_PORTRAIT_WIDTH_PERCENT);
+ mExpandedViewLargeScreenWidth =
+ res.getDimensionPixelSize(R.dimen.bubble_expanded_view_largescreen_width);
}
if (mIsLargeScreen) {
- if (isLandscape() && !mIsSmallTablet) {
+ if (mIsSmallTablet) {
+ final int centeredInset = (bounds.width() - mExpandedViewLargeScreenWidth) / 2;
+ mExpandedViewLargeScreenInsetClosestEdge = centeredInset;
+ mExpandedViewLargeScreenInsetFurthestEdge = centeredInset;
+ } else {
mExpandedViewLargeScreenInsetClosestEdge = res.getDimensionPixelSize(
R.dimen.bubble_expanded_view_largescreen_landscape_padding);
mExpandedViewLargeScreenInsetFurthestEdge = bounds.width()
- mExpandedViewLargeScreenInsetClosestEdge
- mExpandedViewLargeScreenWidth;
- } else {
- final int centeredInset = (bounds.width() - mExpandedViewLargeScreenWidth) / 2;
- mExpandedViewLargeScreenInsetClosestEdge = centeredInset;
- mExpandedViewLargeScreenInsetFurthestEdge = centeredInset;
}
} else {
mExpandedViewLargeScreenInsetClosestEdge = mExpandedViewPadding;
@@ -202,7 +198,9 @@
mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin);
mPointerOverlap = res.getDimensionPixelSize(R.dimen.bubble_pointer_overlap);
- mManageButtonHeight = res.getDimensionPixelSize(R.dimen.bubble_manage_button_total_height);
+ mManageButtonHeightIncludingMargins =
+ res.getDimensionPixelSize(R.dimen.bubble_manage_button_total_height);
+ mManageButtonHeight = res.getDimensionPixelSize(R.dimen.bubble_manage_button_height);
mExpandedViewMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
mOverflowHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height);
mMinimumFlyoutWidthLargeScreen = res.getDimensionPixelSize(
@@ -420,7 +418,7 @@
int pointerSize = showBubblesVertically()
? mPointerWidth
: (mPointerHeight + mPointerMargin);
- int bottomPadding = isOverflow ? mExpandedViewPadding : mManageButtonHeight;
+ int bottomPadding = isOverflow ? mExpandedViewPadding : mManageButtonHeightIncludingMargins;
return getAvailableRect().height()
- expandedContainerY
- paddingTop
@@ -438,6 +436,15 @@
// overflow in landscape on phone is max
return MAX_HEIGHT;
}
+
+ if (mIsLargeScreen && !mIsSmallTablet && !isOverflow) {
+ // the expanded view height on large tablets is calculated based on the shortest screen
+ // size and is the same in both portrait and landscape
+ int maxVerticalInset = Math.max(mInsets.top, mInsets.bottom);
+ int shortestScreenSide = Math.min(mScreenRect.height(), mScreenRect.width());
+ return shortestScreenSide - 2 * maxVerticalInset - mManageButtonHeight;
+ }
+
float desiredHeight = isOverflow
? mOverflowHeight
: ((Bubble) bubble).getDesiredHeight(mContext);
@@ -466,7 +473,8 @@
return topAlignment;
}
// If we're here, we're showing vertically & developer has made height less than maximum.
- int manageButtonHeight = isOverflow ? mExpandedViewPadding : mManageButtonHeight;
+ int manageButtonHeight =
+ isOverflow ? mExpandedViewPadding : mManageButtonHeightIncludingMargins;
float pointerPosition = getPointerPosition(bubblePosition);
float bottomIfCentered = pointerPosition + (expandedViewHeight / 2) + manageButtonHeight;
float topIfCentered = pointerPosition - (expandedViewHeight / 2);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index c124b53..2241c34 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1864,6 +1864,14 @@
: GONE);
}
+ private void updateOverflowDotVisibility(boolean expanding) {
+ if (mBubbleOverflow.showDot()) {
+ mBubbleOverflow.getIconView().animateDotScale(expanding ? 1 : 0f, () -> {
+ mBubbleOverflow.setVisible(expanding ? VISIBLE : GONE);
+ });
+ }
+ }
+
// via BubbleData.Listener
void updateBubble(Bubble bubble) {
animateInFlyoutForBubble(bubble);
@@ -2274,6 +2282,7 @@
if (mIsExpanded && mExpandedBubble.getExpandedView() != null) {
maybeShowManageEdu();
}
+ updateOverflowDotVisibility(true /* expanding */);
} /* after */);
int index;
if (mExpandedBubble != null && BubbleOverflow.KEY.equals(mExpandedBubble.getKey())) {
@@ -2405,11 +2414,15 @@
// since we're about to animate collapsed.
mExpandedAnimationController.notifyPreparingToCollapse();
+ updateOverflowDotVisibility(false /* expanding */);
final Runnable collapseBackToStack = () -> mExpandedAnimationController.collapseBackToStack(
mStackAnimationController
.getStackPositionAlongNearestHorizontalEdge()
/* collapseTo */,
- () -> mBubbleContainer.setActiveController(mStackAnimationController));
+ () -> {
+ mBubbleContainer.setActiveController(mStackAnimationController);
+ updateOverflowVisibility();
+ });
final Runnable after = () -> {
final BubbleViewProvider previouslySelected = mExpandedBubble;
@@ -2424,7 +2437,6 @@
Log.d(TAG, BubbleDebugConfig.formatBubblesString(getBubblesOnScreen(),
mExpandedBubble));
}
- updateOverflowVisibility();
updateZOrder();
updateBadges(true /* setBadgeForCollapsedStack */);
afterExpandedViewAnimation();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
index 4d7042b..738c94e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
@@ -34,6 +34,8 @@
import com.android.wm.shell.R;
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.animation.PhysicsAnimator;
+import com.android.wm.shell.bubbles.BadgedImageView;
+import com.android.wm.shell.bubbles.BubbleOverflow;
import com.android.wm.shell.bubbles.BubblePositioner;
import com.android.wm.shell.bubbles.BubbleStackView;
import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
@@ -63,6 +65,12 @@
/** Damping ratio for expand/collapse spring. */
private static final float DAMPING_RATIO_MEDIUM_LOW_BOUNCY = 0.65f;
+ /**
+ * Damping ratio for the overflow bubble spring; this is less bouncy so it doesn't bounce behind
+ * the top bubble when it goes to disappear.
+ */
+ private static final float DAMPING_RATIO_OVERFLOW_BOUNCY = 0.90f;
+
/** Stiffness for the expand/collapse path-following animation. */
private static final int EXPAND_COLLAPSE_ANIM_STIFFNESS = 400;
@@ -274,9 +282,14 @@
// of the screen where the bubble will be stacked.
path.lineTo(stackedX, p.y);
+ // The overflow should animate to the collapse point, so 0 offset.
+ final boolean isOverflow = bubble instanceof BadgedImageView
+ && BubbleOverflow.KEY.equals(((BadgedImageView) bubble).getKey());
+ final float offsetY = isOverflow
+ ? 0
+ : Math.min(index, NUM_VISIBLE_WHEN_RESTING - 1) * mStackOffsetPx;
// Then, draw a line down to the stack position.
- path.lineTo(stackedX, mCollapsePoint.y
- + Math.min(index, NUM_VISIBLE_WHEN_RESTING - 1) * mStackOffsetPx);
+ path.lineTo(stackedX, mCollapsePoint.y + offsetY);
}
// The lead bubble should be the bubble with the longest distance to travel when we're
@@ -505,8 +518,12 @@
@Override
SpringForce getSpringForce(DynamicAnimation.ViewProperty property, View view) {
+ boolean isOverflow = (view instanceof BadgedImageView)
+ && BubbleOverflow.KEY.equals(((BadgedImageView) view).getKey());
return new SpringForce()
- .setDampingRatio(DAMPING_RATIO_MEDIUM_LOW_BOUNCY)
+ .setDampingRatio(isOverflow
+ ? DAMPING_RATIO_OVERFLOW_BOUNCY
+ : DAMPING_RATIO_MEDIUM_LOW_BOUNCY)
.setStiffness(SpringForce.STIFFNESS_LOW);
}
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/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
index 5e42782..e9344ff 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
@@ -203,7 +203,7 @@
+ "SystemWindow:" + view);
return null;
}
- return root.getFocusGrantToken();
+ return root.getInputTransferToken();
}
private class PerDisplay {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TvWindowMenuActionButton.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TvWindowMenuActionButton.java
index 931cf0c..c6c9b35 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TvWindowMenuActionButton.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TvWindowMenuActionButton.java
@@ -94,6 +94,10 @@
mCurrentIcon = icon;
// Remove old image while waiting for the new one to load.
mIconImageView.setImageDrawable(null);
+ if (icon.getType() == Icon.TYPE_URI || icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP) {
+ // Disallow loading icon from content URI
+ return;
+ }
icon.loadDrawableAsync(mContext, d -> {
// The image hasn't been set any other way and the drawable belongs to the most
// recently set Icon.
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 11aa0546..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,
@@ -494,13 +496,14 @@
ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler,
@DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository,
LaunchAdjacentController launchAdjacentController,
+ RecentsTransitionHandler recentsTransitionHandler,
@ShellMainThread ShellExecutor mainExecutor
) {
return new DesktopTasksController(context, shellInit, shellCommandHandler, shellController,
displayController, shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer,
transitions, enterDesktopTransitionHandler, exitDesktopTransitionHandler,
toggleResizeDesktopTaskTransitionHandler, desktopModeTaskRepository,
- launchAdjacentController, mainExecutor);
+ launchAdjacentController, recentsTransitionHandler, mainExecutor);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
index 8a64037..c508d55 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
@@ -17,13 +17,19 @@
package com.android.wm.shell.dagger.pip;
import android.annotation.NonNull;
+import android.content.Context;
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.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.dagger.WMShellBaseModule;
import com.android.wm.shell.dagger.WMSingleton;
+import com.android.wm.shell.pip2.phone.PipController;
import com.android.wm.shell.pip2.phone.PipTransition;
+import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
@@ -42,8 +48,21 @@
@NonNull ShellTaskOrganizer shellTaskOrganizer,
@NonNull Transitions transitions,
PipBoundsState pipBoundsState,
- PipBoundsAlgorithm pipBoundsAlgorithm) {
+ PipBoundsAlgorithm pipBoundsAlgorithm,
+ PipController pipController) {
return new PipTransition(shellInit, shellTaskOrganizer, transitions, pipBoundsState, null,
pipBoundsAlgorithm);
}
+
+ @WMSingleton
+ @Provides
+ static PipController providePipController(Context context,
+ ShellInit shellInit,
+ ShellController shellController,
+ DisplayController displayController,
+ DisplayInsetsController displayInsetsController,
+ PipDisplayLayoutState pipDisplayLayoutState) {
+ return PipController.create(context, shellInit, shellController, displayController,
+ displayInsetsController, pipDisplayLayoutState);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 09ba4f7..412a5b5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -60,6 +60,8 @@
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository.VisibleTasksListener
import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.TO_DESKTOP_INDICATOR
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+import com.android.wm.shell.recents.RecentsTransitionHandler
+import com.android.wm.shell.recents.RecentsTransitionStateListener
import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_ENTER_DESKTOP
import com.android.wm.shell.sysui.ShellCommandHandler
@@ -68,7 +70,6 @@
import com.android.wm.shell.sysui.ShellSharedConstants
import com.android.wm.shell.transition.OneShotRemoteHandler
import com.android.wm.shell.transition.Transitions
-import com.android.wm.shell.transition.Transitions.TransitionHandler
import com.android.wm.shell.util.KtProtoLog
import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
import com.android.wm.shell.windowdecor.MoveToDesktopAnimator
@@ -93,6 +94,7 @@
ToggleResizeDesktopTaskTransitionHandler,
private val desktopModeTaskRepository: DesktopModeTaskRepository,
private val launchAdjacentController: LaunchAdjacentController,
+ private val recentsTransitionHandler: RecentsTransitionHandler,
@ShellMainThread private val mainExecutor: ShellExecutor
) : RemoteCallable<DesktopTasksController>, Transitions.TransitionHandler {
@@ -119,6 +121,8 @@
com.android.wm.shell.R.dimen.desktop_mode_transition_area_width
)
+ private var recentsAnimationRunning = false
+
// This is public to avoid cyclic dependency; it is set by SplitScreenController
lateinit var splitScreenController: SplitScreenController
@@ -139,6 +143,19 @@
)
transitions.addHandler(this)
desktopModeTaskRepository.addVisibleTasksListener(taskVisibilityListener, mainExecutor)
+
+ recentsTransitionHandler.addTransitionStateListener(
+ object : RecentsTransitionStateListener {
+ override fun onAnimationStateChanged(running: Boolean) {
+ KtProtoLog.v(
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksController: recents animation state changed running=%b",
+ running
+ )
+ recentsAnimationRunning = running
+ }
+ }
+ )
}
/** Show all tasks, that are part of the desktop, on top of launcher */
@@ -644,6 +661,10 @@
val triggerTask = request.triggerTask
val shouldHandleRequest =
when {
+ recentsAnimationRunning -> {
+ reason = "recents animation is running"
+ false
+ }
// Only handle open or to front transitions
request.type != TRANSIT_OPEN && request.type != TRANSIT_TO_FRONT -> {
reason = "transition type not handled (${request.type})"
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/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
new file mode 100644
index 0000000..186cb61
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
@@ -0,0 +1,133 @@
+/*
+ * 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.wm.shell.pip2.phone;
+
+import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.view.InsetsState;
+
+import com.android.internal.protolog.common.ProtoLog;
+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.pip.PipDisplayLayoutState;
+import com.android.wm.shell.common.pip.PipUtils;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.sysui.ConfigurationChangeListener;
+import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
+
+/**
+ * Manages the picture-in-picture (PIP) UI and states for Phones.
+ */
+public class PipController implements ConfigurationChangeListener,
+ DisplayController.OnDisplaysChangedListener {
+ private static final String TAG = PipController.class.getSimpleName();
+
+ private Context mContext;
+ private ShellController mShellController;
+ private DisplayController mDisplayController;
+ private DisplayInsetsController mDisplayInsetsController;
+ private PipDisplayLayoutState mPipDisplayLayoutState;
+
+ private PipController(Context context,
+ ShellInit shellInit,
+ ShellController shellController,
+ DisplayController displayController,
+ DisplayInsetsController displayInsetsController,
+ PipDisplayLayoutState pipDisplayLayoutState) {
+ mContext = context;
+ mShellController = shellController;
+ mDisplayController = displayController;
+ mDisplayInsetsController = displayInsetsController;
+ mPipDisplayLayoutState = pipDisplayLayoutState;
+
+ if (PipUtils.isPip2ExperimentEnabled()) {
+ shellInit.addInitCallback(this::onInit, this);
+ }
+ }
+
+ private void onInit() {
+ // Ensure that we have the display info in case we get calls to update the bounds before the
+ // listener calls back
+ mPipDisplayLayoutState.setDisplayId(mContext.getDisplayId());
+ DisplayLayout layout = new DisplayLayout(mContext, mContext.getDisplay());
+ mPipDisplayLayoutState.setDisplayLayout(layout);
+
+ mShellController.addConfigurationChangeListener(this);
+ mDisplayController.addDisplayWindowListener(this);
+ mDisplayInsetsController.addInsetsChangedListener(mPipDisplayLayoutState.getDisplayId(),
+ new DisplayInsetsController.OnInsetsChangedListener() {
+ @Override
+ public void insetsChanged(InsetsState insetsState) {
+ onDisplayChanged(mDisplayController
+ .getDisplayLayout(mPipDisplayLayoutState.getDisplayId()));
+ }
+ });
+ }
+
+ /**
+ * Instantiates {@link PipController}, returns {@code null} if the feature not supported.
+ */
+ public static PipController create(Context context,
+ ShellInit shellInit,
+ ShellController shellController,
+ DisplayController displayController,
+ DisplayInsetsController displayInsetsController,
+ PipDisplayLayoutState pipDisplayLayoutState) {
+ if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
+ ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: Device doesn't support Pip feature", TAG);
+ return null;
+ }
+ return new PipController(context, shellInit, shellController, displayController,
+ displayInsetsController, pipDisplayLayoutState);
+ }
+
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfiguration) {
+ mPipDisplayLayoutState.onConfigurationChanged();
+ }
+
+ @Override
+ public void onThemeChanged() {
+ onDisplayChanged(new DisplayLayout(mContext, mContext.getDisplay()));
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ if (displayId != mPipDisplayLayoutState.getDisplayId()) {
+ return;
+ }
+ onDisplayChanged(mDisplayController.getDisplayLayout(displayId));
+ }
+
+ @Override
+ public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
+ if (displayId != mPipDisplayLayoutState.getDisplayId()) {
+ return;
+ }
+ onDisplayChanged(mDisplayController.getDisplayLayout(displayId));
+ }
+
+ private void onDisplayChanged(DisplayLayout layout) {
+ mPipDisplayLayoutState.setDisplayLayout(layout);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index ead2f9c..d31476c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -54,6 +54,7 @@
import android.window.WindowContainerTransaction;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.IResultReceiver;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
@@ -279,7 +280,7 @@
mDeathHandler = () -> {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
"[%d] RecentsController.DeathRecipient: binder died", mInstanceId);
- finish(mWillFinishToHome, false /* leaveHint */);
+ finish(mWillFinishToHome, false /* leaveHint */, null /* finishCb */);
};
try {
mListener.asBinder().linkToDeath(mDeathHandler, 0 /* flags */);
@@ -313,7 +314,7 @@
}
}
if (mFinishCB != null) {
- finishInner(toHome, false /* userLeave */);
+ finishInner(toHome, false /* userLeave */, null /* finishCb */);
} else {
cleanUp();
}
@@ -670,7 +671,8 @@
// now and let it do its animation (since recents is going to be occluded).
sendCancelWithSnapshots();
mExecutor.executeDelayed(
- () -> finishInner(true /* toHome */, false /* userLeaveHint */), 0);
+ () -> finishInner(true /* toHome */, false /* userLeaveHint */,
+ null /* finishCb */), 0);
return;
}
if (recentsOpening != null) {
@@ -899,11 +901,12 @@
@Override
@SuppressLint("NewApi")
- public void finish(boolean toHome, boolean sendUserLeaveHint) {
- mExecutor.execute(() -> finishInner(toHome, sendUserLeaveHint));
+ public void finish(boolean toHome, boolean sendUserLeaveHint, IResultReceiver finishCb) {
+ mExecutor.execute(() -> finishInner(toHome, sendUserLeaveHint, finishCb));
}
- private void finishInner(boolean toHome, boolean sendUserLeaveHint) {
+ private void finishInner(boolean toHome, boolean sendUserLeaveHint,
+ IResultReceiver runnerFinishCb) {
if (mFinishCB == null) {
Slog.e(TAG, "Duplicate call to finish");
return;
@@ -993,6 +996,16 @@
}
cleanUp();
finishCB.onTransitionFinished(wct.isEmpty() ? null : wct);
+ if (runnerFinishCb != null) {
+ try {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "[%d] RecentsController.finishInner: calling finish callback",
+ mInstanceId);
+ runnerFinishCb.send(0, null);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to report transition finished", e);
+ }
+ }
}
@Override
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 5e2c61b..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
@@ -143,6 +143,8 @@
import com.android.wm.shell.util.TransitionUtil;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
+import dalvik.annotation.optimization.NeverCompile;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
@@ -2240,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 =
@@ -2477,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);
}
}
@@ -2691,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;
@@ -2730,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) {
@@ -2759,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 "
@@ -2784,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;
}
@@ -2894,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) {
@@ -3109,6 +3150,7 @@
null /* taskInfo */, false /* allowEnterPip */, TYPE_DOCK_DIVIDER);
}
+ @NeverCompile
@Override
public void dump(@NonNull PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
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 83dc7fa..451e618 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
@@ -23,6 +23,7 @@
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
+
import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
@@ -49,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;
@@ -343,6 +345,8 @@
// Keyguard handler cannot handle it, process through original mixed
mActiveTransitions.remove(keyguardMixed);
}
+ } else if (mPipHandler != null) {
+ mPipHandler.syncPipSurfaceState(info, startTransaction, finishTransaction);
}
}
@@ -510,8 +514,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);
@@ -693,9 +715,19 @@
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
+ Transitions.TransitionFinishCallback finishCB = wct -> {
+ mixed.mInFlightSubAnimations--;
+ if (mixed.mInFlightSubAnimations == 0) {
+ mActiveTransitions.remove(mixed);
+ finishCallback.onTransitionFinished(wct);
+ }
+ };
+
+ mixed.mInFlightSubAnimations++;
boolean consumed = mRecentsHandler.startAnimation(
- mixed.mTransition, info, startTransaction, finishTransaction, finishCallback);
+ mixed.mTransition, info, startTransaction, finishTransaction, finishCB);
if (!consumed) {
+ mixed.mInFlightSubAnimations--;
return false;
}
if (mDesktopTasksController != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index e0635ac..de03f582 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -322,7 +322,6 @@
final Runnable onAnimFinish = () -> {
if (!animations.isEmpty()) return;
mAnimations.remove(transition);
- info.releaseAllSurfaces();
finishCallback.onTransitionFinished(null /* wct */);
};
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/SleepHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SleepHandler.java
index 87c438a..ba0ef20 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SleepHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SleepHandler.java
@@ -19,13 +19,12 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.IBinder;
+import android.util.Slog;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
import android.window.WindowContainerTransaction;
-import java.util.ArrayList;
-
/**
* A Simple handler that tracks SLEEP transitions. We track them specially since we (ab)use these
* as sentinels for fast-forwarding through animations when the screen is off.
@@ -34,30 +33,25 @@
* don't register it like a normal handler.
*/
class SleepHandler implements Transitions.TransitionHandler {
- final ArrayList<IBinder> mSleepTransitions = new ArrayList<>();
-
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
- mSleepTransitions.remove(transition);
- startTransaction.apply();
- finishCallback.onTransitionFinished(null);
- return true;
+ if (info.hasChangesOrSideEffects()) {
+ Slog.e(Transitions.TAG, "Real changes included in a SLEEP transition");
+ return false;
+ } else {
+ startTransaction.apply();
+ finishCallback.onTransitionFinished(null);
+ return true;
+ }
}
@Override
@Nullable
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
@NonNull TransitionRequestInfo request) {
- mSleepTransitions.add(transition);
return new WindowContainerTransaction();
}
-
- @Override
- public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
- @Nullable SurfaceControl.Transaction finishTransaction) {
- mSleepTransitions.remove(transition);
- }
}
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 6062e34..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;
@@ -44,6 +47,7 @@
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.desktopmode.DesktopModeStatus;
import java.util.function.Supplier;
@@ -118,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();
@@ -224,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();
@@ -271,22 +278,26 @@
// 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);
}
// Task surface itself
float shadowRadius = loadDimension(resources, params.mShadowRadiusId);
- int backgroundColorInt = mTaskInfo.taskDescription.getBackgroundColor();
- mTmpColor[0] = (float) Color.red(backgroundColorInt) / 255.f;
- mTmpColor[1] = (float) Color.green(backgroundColorInt) / 255.f;
- mTmpColor[2] = (float) Color.blue(backgroundColorInt) / 255.f;
final Point taskPosition = mTaskInfo.positionInParent;
if (isFullscreen) {
// Setting the task crop to the width/height stops input events from being sent to
@@ -302,13 +313,22 @@
finishT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight);
}
startT.setShadowRadius(mTaskSurface, shadowRadius)
- .setColor(mTaskSurface, mTmpColor)
.show(mTaskSurface);
finishT.setPosition(mTaskSurface, taskPosition.x, taskPosition.y)
.setShadowRadius(mTaskSurface, shadowRadius);
if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
+ if (!DesktopModeStatus.isVeiledResizeEnabled()) {
+ // When fluid resize is enabled, add a background to freeform tasks
+ int backgroundColorInt = mTaskInfo.taskDescription.getBackgroundColor();
+ mTmpColor[0] = (float) Color.red(backgroundColorInt) / 255.f;
+ mTmpColor[1] = (float) Color.green(backgroundColorInt) / 255.f;
+ mTmpColor[2] = (float) Color.blue(backgroundColorInt) / 255.f;
+ startT.setColor(mTaskSurface, mTmpColor);
+ }
startT.setCornerRadius(mTaskSurface, params.mCornerRadius);
finishT.setCornerRadius(mTaskSurface, params.mCornerRadius);
+ } else if (!DesktopModeStatus.isVeiledResizeEnabled()) {
+ startT.unsetColor(mTaskSurface);
}
if (mCaptionWindowManager == null) {
@@ -342,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.
@@ -460,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/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml
index b13e9a2..1df1136 100644
--- a/libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml
@@ -81,9 +81,7 @@
<option name="shell-timeout" value="6600s"/>
<option name="test-timeout" value="6000s"/>
<option name="hidden-api-checks" value="false"/>
- <!-- TODO(b/288396763): re-enable when PerfettoListener is fixed
<option name="device-listeners" value="android.device.collectors.PerfettoListener"/>
- -->
<!-- PerfettoListener related arguments -->
<option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/>
<option name="instrumentation-arg"
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 54f9498..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",
@@ -45,10 +46,11 @@
"kotlinx-coroutines-core",
"mockito-kotlin2",
"mockito-target-extended-minus-junit4",
- "truth-prebuilt",
+ "truth",
"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/bubbles/BubblePositionerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java
index 58d9a64..287a97c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java
@@ -22,20 +22,24 @@
import static android.view.View.LAYOUT_DIRECTION_RTL;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
+import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Insets;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.os.UserHandle;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableResources;
+import android.util.DisplayMetrics;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowMetrics;
@@ -257,6 +261,27 @@
assertThat(mPositioner.hasUserModifiedDefaultPosition()).isTrue();
}
+ @Test
+ public void testExpandedViewHeight_onLargeTablet() {
+ Insets insets = Insets.of(10, 20, 5, 15);
+ Rect screenBounds = new Rect(0, 0, 1800, 2600);
+
+ new WindowManagerConfig()
+ .setLargeScreen()
+ .setInsets(insets)
+ .setScreenBounds(screenBounds)
+ .setUpConfig();
+ mPositioner.update();
+
+ Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName());
+ Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor());
+
+ int manageButtonHeight =
+ mContext.getResources().getDimensionPixelSize(R.dimen.bubble_manage_button_height);
+ float expectedHeight = 1800 - 2 * 20 - manageButtonHeight;
+ assertThat(mPositioner.getExpandedViewHeight(bubble)).isWithin(0.1f).of(expectedHeight);
+ }
+
/**
* Calculates the Y position bubbles should be placed based on the config. Based on
* the calculations in {@link BubblePositioner#getDefaultStartPosition()} and
@@ -323,6 +348,8 @@
? MIN_WIDTH_FOR_TABLET
: MIN_WIDTH_FOR_TABLET - 1;
mConfiguration.orientation = mOrientation;
+ mConfiguration.screenWidthDp = pxToDp(mScreenBounds.width());
+ mConfiguration.screenHeightDp = pxToDp(mScreenBounds.height());
when(mConfiguration.getLayoutDirection()).thenReturn(mLayoutDirection);
WindowInsets windowInsets = mock(WindowInsets.class);
@@ -331,5 +358,11 @@
when(mWindowMetrics.getBounds()).thenReturn(mScreenBounds);
when(mWindowManager.getCurrentWindowMetrics()).thenReturn(mWindowMetrics);
}
+
+ private int pxToDp(float px) {
+ int dpi = mContext.getResources().getDisplayMetrics().densityDpi;
+ float dp = px / ((float) dpi / DisplayMetrics.DENSITY_DEFAULT);
+ return (int) dp;
+ }
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index dea1617..ebcb640 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -54,6 +54,8 @@
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createHomeTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createSplitScreenTask
+import com.android.wm.shell.recents.RecentsTransitionHandler
+import com.android.wm.shell.recents.RecentsTransitionStateListener
import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
@@ -101,11 +103,13 @@
@Mock lateinit var launchAdjacentController: LaunchAdjacentController
@Mock lateinit var desktopModeWindowDecoration: DesktopModeWindowDecoration
@Mock lateinit var splitScreenController: SplitScreenController
+ @Mock lateinit var recentsTransitionHandler: RecentsTransitionHandler
private lateinit var mockitoSession: StaticMockitoSession
private lateinit var controller: DesktopTasksController
private lateinit var shellInit: ShellInit
private lateinit var desktopModeTaskRepository: DesktopModeTaskRepository
+ private lateinit var recentsTransitionStateListener: RecentsTransitionStateListener
private val shellExecutor = TestShellExecutor()
// Mock running tasks are registered here so we can get the list from mock shell task organizer
@@ -126,6 +130,10 @@
controller.splitScreenController = splitScreenController
shellInit.init()
+
+ val captor = ArgumentCaptor.forClass(RecentsTransitionStateListener::class.java)
+ verify(recentsTransitionHandler).addTransitionStateListener(captor.capture())
+ recentsTransitionStateListener = captor.value
}
private fun createController(): DesktopTasksController {
@@ -144,6 +152,7 @@
mToggleResizeDesktopTaskTransitionHandler,
desktopModeTaskRepository,
launchAdjacentController,
+ recentsTransitionHandler,
shellExecutor
)
}
@@ -355,7 +364,7 @@
@Test
fun moveToDesktop_splitTaskExitsSplit() {
- var task = setUpSplitScreenTask()
+ val task = setUpSplitScreenTask()
controller.moveToDesktop(desktopModeWindowDecoration, task)
val wct = getLatestMoveToDesktopWct()
assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
@@ -367,7 +376,7 @@
@Test
fun moveToDesktop_fullscreenTaskDoesNotExitSplit() {
- var task = setUpFullscreenTask()
+ val task = setUpFullscreenTask()
controller.moveToDesktop(desktopModeWindowDecoration, task)
val wct = getLatestMoveToDesktopWct()
assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
@@ -666,6 +675,20 @@
}
@Test
+ fun handleRequest_recentsAnimationRunning_returnNull() {
+ // Set up a visible freeform task so a fullscreen task should be converted to freeform
+ val freeformTask = setUpFreeformTask()
+ markTaskVisible(freeformTask)
+
+ // Mark recents animation running
+ recentsTransitionStateListener.onAnimationStateChanged(true)
+
+ // Open a fullscreen task, check that it does not result in a WCT with changes to it
+ val fullscreenTask = createFullscreenTask()
+ assertThat(controller.handleRequest(Binder(), createTransition(fullscreenTask))).isNull()
+ }
+
+ @Test
fun stashDesktopApps_stateUpdates() {
whenever(DesktopModeStatus.isStashingEnabled()).thenReturn(true)
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..da83d4c0 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;
@@ -1153,7 +1169,7 @@
}
@Test
- public void testEmptyTransitionStillReportsKeyguardGoingAway() {
+ public void testEmptyTransition_withKeyguardGoingAway_plays() {
Transitions transitions = createTestTransitions();
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
@@ -1172,6 +1188,65 @@
}
@Test
+ public void testSleepTransition_withKeyguardGoingAway_plays(){
+ Transitions transitions = createTestTransitions();
+ transitions.replaceDefaultHandlerForTest(mDefaultHandler);
+
+ IBinder transitToken = new Binder();
+ transitions.requestStartTransition(transitToken,
+ new TransitionRequestInfo(TRANSIT_SLEEP, null /* trigger */, null /* remote */));
+
+ // Make a no-op transition
+ TransitionInfo info = new TransitionInfoBuilder(
+ TRANSIT_SLEEP, TRANSIT_FLAG_KEYGUARD_GOING_AWAY, true /* noOp */).build();
+ transitions.onTransitionReady(transitToken, info, new StubTransaction(),
+ new StubTransaction());
+
+ // If keyguard-going-away flag set, then it shouldn't be aborted.
+ assertEquals(1, mDefaultHandler.activeCount());
+ }
+
+ @Test
+ public void testSleepTransition_withChanges_plays(){
+ Transitions transitions = createTestTransitions();
+ transitions.replaceDefaultHandlerForTest(mDefaultHandler);
+
+ IBinder transitToken = new Binder();
+ transitions.requestStartTransition(transitToken,
+ new TransitionRequestInfo(TRANSIT_SLEEP, null /* trigger */, null /* remote */));
+
+ // Make a transition with some changes
+ TransitionInfo info = new TransitionInfoBuilder(TRANSIT_SLEEP)
+ .addChange(TRANSIT_OPEN).build();
+ info.setTrack(0);
+ transitions.onTransitionReady(transitToken, info, new StubTransaction(),
+ new StubTransaction());
+
+ // If there is an actual change, then it shouldn't be aborted.
+ assertEquals(1, mDefaultHandler.activeCount());
+ }
+
+
+ @Test
+ public void testSleepTransition_empty_SyncBySleepHandler() {
+ Transitions transitions = createTestTransitions();
+ transitions.replaceDefaultHandlerForTest(mDefaultHandler);
+
+ IBinder transitToken = new Binder();
+ transitions.requestStartTransition(transitToken,
+ new TransitionRequestInfo(TRANSIT_SLEEP, null /* trigger */, null /* remote */));
+
+ // Make a no-op transition
+ TransitionInfo info = new TransitionInfoBuilder(
+ TRANSIT_SLEEP, 0x0, true /* noOp */).build();
+ transitions.onTransitionReady(transitToken, info, new StubTransaction(),
+ new StubTransaction());
+
+ // If there is nothing to actually play, it should not be offered to handlers.
+ assertEquals(0, mDefaultHandler.activeCount());
+ }
+
+ @Test
public void testMultipleTracks() {
Transitions transitions = createTestTransitions();
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
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 966a99ee..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
@@ -17,12 +17,19 @@
package com.android.wm.shell.windowdecor;
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;
import static com.android.wm.shell.MockSurfaceControlHelper.createMockSurfaceControlTransaction;
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;
@@ -36,6 +43,7 @@
import static org.mockito.Mockito.same;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.mockito.quality.Strictness.LENIENT;
import android.app.ActivityManager;
import android.content.Context;
@@ -48,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;
@@ -59,10 +68,12 @@
import androidx.test.filters.SmallTest;
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.tests.R;
import org.junit.Before;
@@ -89,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<>();
@@ -113,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;
@@ -136,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
@@ -201,12 +219,8 @@
createMockSurfaceControlBuilder(captionContainerSurface);
mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder);
- final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
- new ActivityManager.TaskDescription.Builder()
- .setBackgroundColor(Color.YELLOW);
final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
.setDisplayId(Display.DEFAULT_DISPLAY)
- .setTaskDescriptionBuilder(taskDescriptionBuilder)
.setBounds(TASK_BOUNDS)
.setPositionInParent(TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y)
.setVisible(true)
@@ -255,8 +269,6 @@
verify(mMockSurfaceControlFinishT).setCornerRadius(taskSurface, CORNER_RADIUS);
verify(mMockSurfaceControlStartT)
.show(taskSurface);
- verify(mMockSurfaceControlStartT)
- .setColor(taskSurface, new float[] {1.f, 1.f, 0.f});
verify(mMockSurfaceControlStartT).setShadowRadius(taskSurface, 10);
assertEquals(300, mRelayoutResult.mWidth);
@@ -502,6 +514,142 @@
verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockSurfaceControlStartT);
}
+ @Test
+ public void testRelayout_fluidResizeEnabled_freeformTask_setTaskSurfaceColor() {
+ StaticMockitoSession mockitoSession = mockitoSession().mockStatic(
+ DesktopModeStatus.class).strictness(
+ LENIENT).startMocking();
+ when(DesktopModeStatus.isVeiledResizeEnabled()).thenReturn(false);
+
+ final Display defaultDisplay = mock(Display.class);
+ doReturn(defaultDisplay).when(mMockDisplayController)
+ .getDisplay(Display.DEFAULT_DISPLAY);
+
+ final SurfaceControl decorContainerSurface = mock(SurfaceControl.class);
+ final SurfaceControl.Builder decorContainerSurfaceBuilder =
+ createMockSurfaceControlBuilder(decorContainerSurface);
+ mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
+ final SurfaceControl captionContainerSurface = mock(SurfaceControl.class);
+ final SurfaceControl.Builder captionContainerSurfaceBuilder =
+ createMockSurfaceControlBuilder(captionContainerSurface);
+ mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder);
+
+ final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
+ new ActivityManager.TaskDescription.Builder()
+ .setBackgroundColor(Color.YELLOW);
+
+ final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setDisplayId(Display.DEFAULT_DISPLAY)
+ .setTaskDescriptionBuilder(taskDescriptionBuilder)
+ .setVisible(true)
+ .setWindowingMode(WINDOWING_MODE_FREEFORM)
+ .build();
+ taskInfo.isFocused = true;
+ final SurfaceControl taskSurface = mock(SurfaceControl.class);
+ final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface);
+
+ windowDecor.relayout(taskInfo);
+
+ 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();
+ when(DesktopModeStatus.isVeiledResizeEnabled()).thenReturn(false);
+
+ final Display defaultDisplay = mock(Display.class);
+ doReturn(defaultDisplay).when(mMockDisplayController)
+ .getDisplay(Display.DEFAULT_DISPLAY);
+
+ final SurfaceControl decorContainerSurface = mock(SurfaceControl.class);
+ final SurfaceControl.Builder decorContainerSurfaceBuilder =
+ createMockSurfaceControlBuilder(decorContainerSurface);
+ mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
+ final SurfaceControl captionContainerSurface = mock(SurfaceControl.class);
+ final SurfaceControl.Builder captionContainerSurfaceBuilder =
+ createMockSurfaceControlBuilder(captionContainerSurface);
+ mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder);
+
+ final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
+ new ActivityManager.TaskDescription.Builder()
+ .setBackgroundColor(Color.YELLOW);
+ final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setDisplayId(Display.DEFAULT_DISPLAY)
+ .setTaskDescriptionBuilder(taskDescriptionBuilder)
+ .setVisible(true)
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+ .build();
+ taskInfo.isFocused = true;
+ final SurfaceControl taskSurface = mock(SurfaceControl.class);
+ final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface);
+
+ windowDecor.relayout(taskInfo);
+
+ verify(mMockSurfaceControlStartT).unsetColor(taskSurface);
+
+ 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/dream/lowlight/tests/Android.bp b/libs/dream/lowlight/tests/Android.bp
index 64b53cb..4dafd0a 100644
--- a/libs/dream/lowlight/tests/Android.bp
+++ b/libs/dream/lowlight/tests/Android.bp
@@ -34,7 +34,7 @@
"mockito-target-extended-minus-junit4",
"platform-test-annotations",
"testables",
- "truth-prebuilt",
+ "truth",
],
libs: [
"android.test.mock",
diff --git a/libs/hwui/aconfig/hwui_flags.aconfig b/libs/hwui/aconfig/hwui_flags.aconfig
index d0d3c5e..e672b98 100644
--- a/libs/hwui/aconfig/hwui_flags.aconfig
+++ b/libs/hwui/aconfig/hwui_flags.aconfig
@@ -20,3 +20,10 @@
description: "APIs to help enable animations involving gainmaps"
bug: "296482289"
}
+
+flag {
+ name: "gainmap_constructor_with_metadata"
+ namespace: "core_graphics"
+ description: "APIs to create a new gainmap with a bitmap for metadata."
+ bug: "304478551"
+}
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/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 90850d3..22c5862 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -26,6 +26,7 @@
#include <gui/TraceUtils.h>
#include <include/gpu/ganesh/SkSurfaceGanesh.h>
#include <include/gpu/ganesh/vk/GrVkBackendSurface.h>
+#include <include/gpu/ganesh/vk/GrVkDirectContext.h>
#include <ui/FatVector.h>
#include <vk/GrVkExtensions.h>
#include <vk/GrVkTypes.h>
@@ -435,7 +436,7 @@
options.fContextDeleteContext = this;
options.fContextDeleteProc = onGrContextReleased;
- return GrDirectContext::MakeVulkan(backendContext, options);
+ return GrDirectContexts::MakeVulkan(backendContext, options);
}
VkFunctorInitParams VulkanManager::getVkFunctorInitParams() const {
diff --git a/libs/securebox/tests/Android.bp b/libs/securebox/tests/Android.bp
index 7df546a..80b501d 100644
--- a/libs/securebox/tests/Android.bp
+++ b/libs/securebox/tests/Android.bp
@@ -32,7 +32,7 @@
"platform-test-annotations",
"testables",
"testng",
- "truth-prebuilt",
+ "truth",
],
libs: [
"android.test.mock",
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..b002bbf 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -1282,11 +1282,8 @@
* {@link AudioFormat#CHANNEL_OUT_BACK_CENTER},
* {@link AudioFormat#CHANNEL_OUT_SIDE_LEFT},
* {@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> (2) right/left pairs should be matched.
+ * <p> For output or {@link AudioTrack}, channel position masks which do not contain
+ * matched left/right pairs are invalid.
* <p> For input or {@link AudioRecord}, the mask should be
* {@link AudioFormat#CHANNEL_IN_MONO} or
* {@link AudioFormat#CHANNEL_IN_STEREO}. {@link AudioFormat#CHANNEL_IN_MONO} is
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index adc0e16..5b88079 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1473,8 +1473,7 @@
* Returns the volume group id associated to the given {@link AudioAttributes}.
*
* @param attributes The {@link AudioAttributes} to consider.
- * @return {@link android.media.audiopolicy.AudioVolumeGroup} id supporting the given
- * {@link AudioAttributes} if found,
+ * @return audio volume group id supporting the given {@link AudioAttributes} if found,
* {@code android.media.audiopolicy.AudioVolumeGroup.DEFAULT_VOLUME_GROUP} otherwise.
*/
public int getVolumeGroupIdForAttributes(@NonNull AudioAttributes attributes) {
@@ -1589,7 +1588,7 @@
* <p> Call first in prior {@link #getVolumeGroupIdForAttributes(AudioAttributes)} to retrieve
* the volume group id supporting the given {@link AudioAttributes}.
*
- * @param groupId of the {@link android.media.audiopolicy.AudioVolumeGroup} to consider.
+ * @param groupId of the audio volume group to consider.
* @param direction The direction to adjust the volume. One of
* {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE}, or
* {@link #ADJUST_SAME}.
@@ -1633,8 +1632,8 @@
* <p> Call first in prior {@link #getVolumeGroupIdForAttributes(AudioAttributes)} to retrieve
* the volume group id supporting the given {@link AudioAttributes}.
*
- * @param groupId of the {@link android.media.audiopolicy.AudioVolumeGroup} to consider.
- * @return The mute state for the given {@link android.media.audiopolicy.AudioVolumeGroup} id.
+ * @param groupId of the audio volume group to consider.
+ * @return The mute state for the given audio volume group id.
* @see #adjustVolumeGroupVolume(int, int, int)
*/
public boolean isVolumeGroupMuted(int groupId) {
@@ -4659,24 +4658,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/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 8c63580..2169090 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -463,7 +463,7 @@
/**
* Returns the current {@link RouteListingPreference} of the target router.
*
- * <p>If this instance was created using {@link #getInstance(Context, String)}, then it returns
+ * <p>If this instance was created using {@code #getInstance(Context, String)}, then it returns
* the last {@link RouteListingPreference} set by the process this router was created for.
*
* @see #setRouteListingPreference(RouteListingPreference)
diff --git a/media/java/android/media/RingtoneSelection.java b/media/java/android/media/RingtoneSelection.java
index 74f7276..b74b6a3 100644
--- a/media/java/android/media/RingtoneSelection.java
+++ b/media/java/android/media/RingtoneSelection.java
@@ -18,16 +18,23 @@
import static java.util.Objects.requireNonNull;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
+import android.content.ContentProvider;
import android.content.ContentResolver;
import android.net.Uri;
+import android.os.UserHandle;
+import android.os.vibrator.Flags;
import android.provider.MediaStore;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
/**
* Immutable representation a desired ringtone, usually originating from a user preference.
@@ -46,7 +53,7 @@
* to be internally consistent and reflect effective values - with the exception of not verifying
* the actual URI content. For example, loading a selection Uri that sets a sound source to
* {@link #SOUND_SOURCE_URI}, but doesn't also have a sound Uri set, will result in this class
- * instead returning {@link #SOUND_SOURCE_DEFAULT} from {@link #getSoundSource}.
+ * instead returning {@link #SOUND_SOURCE_UNSPECIFIED} from {@link #getSoundSource}.
*
* <h2>Storing preferences</h2>
*
@@ -57,6 +64,7 @@
* @hide
*/
@TestApi
+@FlaggedApi(Flags.FLAG_HAPTICS_CUSTOMIZATION_ENABLED)
public final class RingtoneSelection {
/**
@@ -70,7 +78,7 @@
* The sound source is not explicitly specified, so it can follow default behavior for its
* context.
*/
- public static final int SOUND_SOURCE_DEFAULT = 0;
+ public static final int SOUND_SOURCE_UNSPECIFIED = 0;
/**
* Sound is explicitly disabled, such as the user having selected "Silent" in the sound picker.
@@ -83,15 +91,25 @@
public static final int SOUND_SOURCE_URI = 2;
/**
+ * The sound should explicitly use the system default.
+ *
+ * <p>This value isn't valid within the system default itself.
+ */
+ public static final int SOUND_SOURCE_SYSTEM_DEFAULT = 3;
+
+ // Note: Value 4 reserved for possibility of SOURCE_SOURCE_APPLICATION_DEFAULT.
+
+ /**
* Directive for how to make sound.
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = "SOUND_SOURCE_", value = {
SOUND_SOURCE_UNKNOWN,
- SOUND_SOURCE_DEFAULT,
+ SOUND_SOURCE_UNSPECIFIED,
SOUND_SOURCE_OFF,
SOUND_SOURCE_URI,
+ SOUND_SOURCE_SYSTEM_DEFAULT,
})
public @interface SoundSource {}
@@ -106,9 +124,9 @@
/**
* Vibration source is not explicitly specified. If vibration is enabled, this will use the
* first available of {@link #VIBRATION_SOURCE_AUDIO_CHANNEL},
- * {@link #VIBRATION_SOURCE_APPLICATION_PROVIDED}, or system default vibration.
+ * {@link #VIBRATION_SOURCE_APPLICATION_DEFAULT}, or {@link #VIBRATION_SOURCE_SYSTEM_DEFAULT}.
*/
- public static final int VIBRATION_SOURCE_DEFAULT = 0;
+ public static final int VIBRATION_SOURCE_UNSPECIFIED = 0;
/** Specifies that vibration is explicitly disabled for this ringtone. */
public static final int VIBRATION_SOURCE_OFF = 1;
@@ -117,22 +135,31 @@
public static final int VIBRATION_SOURCE_URI = 2;
/**
+ * The vibration should explicitly use the system default.
+ *
+ * <p>This value isn't valid within the system default itself.
+ */
+ public static final int VIBRATION_SOURCE_SYSTEM_DEFAULT = 3;
+
+ /**
* Specifies that vibration should use the vibration provided by the application. This is
* typically the application's own default for the use-case, provided via
* {@link Ringtone.Builder#setVibrationEffect}. For notification channels, this is the vibration
* effect saved on the notification channel.
*
* <p>If no vibration is specified by the application, this value behaves if the source was
- * {@link #VIBRATION_SOURCE_DEFAULT}.
+ * {@link #VIBRATION_SOURCE_UNSPECIFIED}.
+ *
+ * <p>This value isn't valid within the system default.
*/
- public static final int VIBRATION_SOURCE_APPLICATION_PROVIDED = 3;
+ public static final int VIBRATION_SOURCE_APPLICATION_DEFAULT = 4;
/**
* Specifies that vibration should use haptic audio channels from the
* sound Uri. If the sound URI doesn't have haptic channels, then reverts to the order specified
- * by {@link #VIBRATION_SOURCE_DEFAULT}.
+ * by {@link #VIBRATION_SOURCE_UNSPECIFIED}.
*/
- // Numeric gap from VIBRATION_SOURCE_APPLICATION_PROVIDED in case we want other common elements.
+ // Numeric gap from VIBRATION_SOURCE_APPLICATION_DEFAULT in case we want other common elements.
public static final int VIBRATION_SOURCE_AUDIO_CHANNEL = 10;
/**
@@ -151,10 +178,10 @@
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = "VIBRATION_SOURCE_", value = {
VIBRATION_SOURCE_UNKNOWN,
- VIBRATION_SOURCE_DEFAULT,
+ VIBRATION_SOURCE_UNSPECIFIED,
VIBRATION_SOURCE_OFF,
VIBRATION_SOURCE_URI,
- VIBRATION_SOURCE_APPLICATION_PROVIDED,
+ VIBRATION_SOURCE_APPLICATION_DEFAULT,
VIBRATION_SOURCE_AUDIO_CHANNEL,
VIBRATION_SOURCE_HAPTIC_GENERATOR,
})
@@ -162,9 +189,12 @@
/**
* Configures {@link #RingtoneSelection#fromUri} to treat an unrecognized Uri as the sound Uri
- * for the returned {@link RingtoneSelection}, with null meaning {@link #SOUND_SOURCE_OFF}.
- * This behavior is particularly suited to loading values from older settings that may contain
- * a raw sound Uri or null for silent.
+ * for the returned {@link RingtoneSelection}, with null meaning {@link #SOUND_SOURCE_OFF},
+ * and symbolic default URIs ({@link RingtoneManager#getDefaultUri}) meaning
+ * {@link #SOUND_SOURCE_SYSTEM_DEFAULT}.
+ *
+ * <p>This behavior is particularly suited to loading values from older settings that may
+ * contain a raw sound Uri or null for silent.
*
* <p>An unrecognized Uri is one for which {@link #isRingtoneSelectionUri(Uri)} returns false.
*/
@@ -173,7 +203,8 @@
/**
* Configures {@link #RingtoneSelection#fromUri} to treat an unrecognized Uri as the vibration
* Uri for the returned {@link RingtoneSelection}, with null meaning
- * {@link #VIBRATION_SOURCE_OFF}.
+ * {@link #VIBRATION_SOURCE_OFF} and symbolic default URIs
+ * ({@link RingtoneManager#getDefaultUri}) meaning {@link #VIBRATION_SOURCE_SYSTEM_DEFAULT}.
*
* <p>An unrecognized Uri is one for which {@link #isRingtoneSelectionUri(Uri)} returns false.
*/
@@ -182,7 +213,9 @@
/**
* Configures {@link #RingtoneSelection#fromUri} to treat an unrecognized Uri as an invalid
* value. Null or an invalid values will revert to default behavior correspnoding to
- * {@link #DEFAULT_SELECTION_URI_STRING}.
+ * {@link #DEFAULT_SELECTION_URI_STRING}. Symbolic default URIs
+ * ({@link RingtoneManager#getDefaultUri}) will set both
+ * {@link #SOUND_SOURCE_SYSTEM_DEFAULT} and {@link #VIBRATION_SOURCE_SYSTEM_DEFAULT}.
*
* <p>An unrecognized Uri is one for which {@link #isRingtoneSelectionUri(Uri)} returns false,
* which include {@code null}.
@@ -218,10 +251,11 @@
/* Common param values */
private static final String SOURCE_OFF_STRING = "off";
+ private static final String SOURCE_SYSTEM_DEFAULT_STRING = "sys";
/* Vibration source param values. */
private static final String VIBRATION_SOURCE_AUDIO_CHANNEL_STRING = "ac";
- private static final String VIBRATION_SOURCE_APPLICATION_PROVIDED_STRING = "app";
+ private static final String VIBRATION_SOURCE_APPLICATION_DEFAULT_STRING = "app";
private static final String VIBRATION_SOURCE_HAPTIC_GENERATOR_STRING = "hg";
@Nullable
@@ -236,53 +270,45 @@
private RingtoneSelection(@Nullable Uri soundUri, @SoundSource int soundSource,
@Nullable Uri vibrationUri, @VibrationSource int vibrationSource) {
- // Enforce guarantees on the source values: revert to unset if they depend on something
- // that's not set.
- switch (soundSource) {
- case SOUND_SOURCE_URI:
- case SOUND_SOURCE_UNKNOWN: // Allow unknown to revert to URI before default.
- mSoundSource = soundUri != null ? SOUND_SOURCE_URI : SOUND_SOURCE_DEFAULT;
- break;
- default:
- mSoundSource = soundSource;
- break;
- }
- switch (vibrationSource) {
- case VIBRATION_SOURCE_AUDIO_CHANNEL:
- case VIBRATION_SOURCE_HAPTIC_GENERATOR:
- mVibrationSource = soundUri != null ? vibrationSource : VIBRATION_SOURCE_DEFAULT;
- break;
- case VIBRATION_SOURCE_URI:
- case VIBRATION_SOURCE_UNKNOWN: // Allow unknown to revert to URI.
- mVibrationSource =
- vibrationUri != null ? VIBRATION_SOURCE_URI : VIBRATION_SOURCE_DEFAULT;
- break;
- default:
- mVibrationSource = vibrationSource;
- break;
- }
+ // Enforce guarantees on the source values: revert to unspecified if they depend on
+ // something that's not set.
+ //
+ // The non-public "unknown" value can't appear in a getter result, it's just a reserved
+ // "null" value and should be treated the same as an unrecognized value. This can be seen
+ // in Uri parsing. For this and other unrecognized values, we either revert them to the URI
+ // source, if a Uri was included, or the "unspecified" source otherwise. This can be
+ // seen in action in the Uri parsing.
+ //
+ // The "unspecified" source is a public value meaning that there is no specific
+ // behavior indicated, and the defaults and fallbacks should be applied. For example, an
+ // vibration source value of "system default" means to explicitly use the system default
+ // vibration. However, an "unspecified" vibration source will first see if audio coupled
+ // or application-default vibrations are available.
+ mSoundSource = switch (soundSource) {
+ // Supported explicit values that don't have a Uri.
+ case SOUND_SOURCE_OFF, SOUND_SOURCE_UNSPECIFIED, SOUND_SOURCE_SYSTEM_DEFAULT ->
+ soundSource;
+ // Uri and unknown/unrecognized values: use a Uri if one is present, else revert to
+ // unspecified.
+ default ->
+ soundUri != null ? SOUND_SOURCE_URI : SOUND_SOURCE_UNSPECIFIED;
+ };
+ mVibrationSource = switch (vibrationSource) {
+ // Enforce vibration sources that require a sound Uri.
+ case VIBRATION_SOURCE_AUDIO_CHANNEL, VIBRATION_SOURCE_HAPTIC_GENERATOR ->
+ soundUri != null ? vibrationSource : VIBRATION_SOURCE_UNSPECIFIED;
+ // Supported explicit values that don't rely on any Uri.
+ case VIBRATION_SOURCE_OFF, VIBRATION_SOURCE_UNSPECIFIED,
+ VIBRATION_SOURCE_SYSTEM_DEFAULT, VIBRATION_SOURCE_APPLICATION_DEFAULT ->
+ vibrationSource;
+ // Uri and unknown/unrecognized values: use a Uri if one is present, else revert to
+ // unspecified.
+ default ->
+ vibrationUri != null ? VIBRATION_SOURCE_URI : VIBRATION_SOURCE_UNSPECIFIED;
+ };
// Clear Uri values if they're un-used by the source.
- switch (mSoundSource) {
- case SOUND_SOURCE_OFF:
- mSoundUri = null;
- break;
- default:
- // Unset case isn't handled here: the defaulting behavior is left to the player.
- mSoundUri = soundUri;
- break;
- }
- switch (mVibrationSource) {
- case VIBRATION_SOURCE_OFF:
- case VIBRATION_SOURCE_APPLICATION_PROVIDED:
- case VIBRATION_SOURCE_AUDIO_CHANNEL:
- case VIBRATION_SOURCE_HAPTIC_GENERATOR:
- mVibrationUri = null;
- break;
- default:
- // Unset case isn't handled here: the defaulting behavior is left to the player.
- mVibrationUri = vibrationUri;
- break;
- }
+ mSoundUri = mSoundSource == SOUND_SOURCE_URI ? soundUri : null;
+ mVibrationUri = mVibrationSource == VIBRATION_SOURCE_URI ? vibrationUri : null;
}
/**
@@ -360,18 +386,83 @@
}
// Any URI content://media/ringtone
return ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())
- && MediaStore.AUTHORITY.equals(uri.getAuthority())
+ && MediaStore.AUTHORITY.equals(
+ ContentProvider.getAuthorityWithoutUserId(uri.getAuthority()))
&& MEDIA_URI_RINGTONE_PATH.equals(uri.getPath());
}
+ /**
+ * Strip the specified userId from internal Uris. Non-stripped userIds will typically be
+ * for work profiles referencing system ringtones from the host user.
+ *
+ * This is only for use in RingtoneManager.
+ * @hide
+ */
+ @VisibleForTesting
+ public RingtoneSelection getWithoutUserId(int userIdToStrip) {
+ if (mSoundSource != SOUND_SOURCE_URI && mVibrationSource != VIBRATION_SOURCE_URI) {
+ return this;
+ }
+ // Ok if uri is null. We only replace explicit references to the specified (current) userId.
+ int soundUserId = ContentProvider.getUserIdFromUri(mSoundUri, UserHandle.USER_NULL);
+ int vibrationUserId = ContentProvider.getUserIdFromUri(mVibrationUri, UserHandle.USER_NULL);
+ boolean needToChangeSound =
+ soundUserId != UserHandle.USER_NULL && soundUserId == userIdToStrip;
+ boolean needToChangeVibration =
+ vibrationUserId != UserHandle.USER_NULL && vibrationUserId == userIdToStrip;
+
+ // Anything to do?
+ if (!needToChangeSound && !needToChangeVibration) {
+ return this;
+ }
+
+ RingtoneSelection.Builder updated = new Builder(this);
+ // The relevant uris can't be null, because they contain userIdToStrip.
+ if (needToChangeSound) {
+ // mSoundUri is not null, so the result of getUriWithoutUserId won't be null.
+ updated.setSoundSource(ContentProvider.getUriWithoutUserId(mSoundUri));
+ }
+ if (needToChangeVibration) {
+ updated.setVibrationSource(ContentProvider.getUriWithoutUserId(mVibrationUri));
+ }
+ return updated.build();
+ }
+
+ @Override
+ public String toString() {
+ return toUri().toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof RingtoneSelection other)) {
+ return false;
+ }
+ return this.mSoundSource == other.mSoundSource
+ && this.mVibrationSource == other.mVibrationSource
+ && Objects.equals(this.mSoundUri, other.mSoundUri)
+ && Objects.equals(this.mVibrationUri, other.mVibrationUri);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mSoundSource, mVibrationSource, mSoundUri, mVibrationUri);
+ }
/**
* Converts a Uri into a RingtoneSelection.
*
- * <p>Null values and Uris that {@link #isRingtoneSelectionUri(Uri)} returns false will be
- * treated according to the behaviour specified by the {@code unrecognizedValueBehavior}
- * parameter.
+ * <p>Null values, Uris that {@link #isRingtoneSelectionUri(Uri)} returns false (except for
+ * old-style symbolic default Uris {@link RingtoneManager#getDefaultUri}) will be treated
+ * according to the behaviour specified by the {@code unrecognizedValueBehavior} parameter.
+ *
+ * <p>Symbolic default Uris (where {@link RingtoneManager#getDefaultType(Uri)} returns -1,
+ * will map sources to {@link #SOUND_SOURCE_SYSTEM_DEFAULT} and
+ * {@link #VIBRATION_SOURCE_SYSTEM_DEFAULT}.
*
* @param uri The Uri to convert, potentially null.
* @param unrecognizedValueBehavior indicates how to treat values for which
@@ -384,21 +475,35 @@
if (isRingtoneSelectionUri(uri)) {
return parseRingtoneSelectionUri(uri);
}
+ // Old symbolic default URIs map to the sources suggested by the unrecognized behavior.
+ // It doesn't always map to both sources because the app may have its own default behavior
+ // to apply, so non-primary sources are left as unspecified, which will revert to the
+ // system default in the absence of an app default.
+ boolean isDefaultUri = RingtoneManager.getDefaultType(uri) > 0;
RingtoneSelection.Builder builder = new RingtoneSelection.Builder();
switch (unrecognizedValueBehavior) {
case FROM_URI_RINGTONE_SELECTION_ONLY:
- // Always return use-defaults for unrecognized ringtone selection Uris.
+ if (isDefaultUri) {
+ builder.setSoundSource(SOUND_SOURCE_SYSTEM_DEFAULT);
+ builder.setVibrationSource(VIBRATION_SOURCE_SYSTEM_DEFAULT);
+ }
+ // Always return unspecified (defaults) for unrecognized ringtone selection Uris.
return builder.build();
case FROM_URI_RINGTONE_SELECTION_OR_SOUND:
if (uri == null) {
return builder.setSoundSource(SOUND_SOURCE_OFF).build();
+ } else if (isDefaultUri) {
+ return builder.setSoundSource(SOUND_SOURCE_SYSTEM_DEFAULT).build();
} else {
return builder.setSoundSource(uri).build();
}
case FROM_URI_RINGTONE_SELECTION_OR_VIBRATION:
if (uri == null) {
return builder.setVibrationSource(VIBRATION_SOURCE_OFF).build();
+ } else if (isDefaultUri) {
+ return builder.setVibrationSource(VIBRATION_SOURCE_SYSTEM_DEFAULT).build();
} else {
+ // Unlike sound, there's no legacy settings alias uri for the default.
return builder.setVibrationSource(uri).build();
}
default:
@@ -407,7 +512,12 @@
}
}
- /** Parses the Uri, which has already been checked for {@link #isRingtoneSelectionUri(Uri)}. */
+ /**
+ * Parses the Uri, which has already been checked for {@link #isRingtoneSelectionUri(Uri)}.
+ * As a special case to be more compatible, if the RingtoneSelection has a userId specified in
+ * the authority, then this is pushed down into the sound and vibration Uri, as the selection
+ * itself is agnostic.
+ */
@NonNull
private static RingtoneSelection parseRingtoneSelectionUri(@NonNull Uri uri) {
RingtoneSelection.Builder builder = new RingtoneSelection.Builder();
@@ -416,19 +526,39 @@
uri.getQueryParameter(URI_PARAM_VIBRATION_SOURCE));
Uri soundUri = getParamAsUri(uri, URI_PARAM_SOUND_URI);
Uri vibrationUri = getParamAsUri(uri, URI_PARAM_VIBRATION_URI);
+
+ // Add userId if necessary. This won't override an existing one in the sound/vib Uris.
+ int userIdFromAuthority = ContentProvider.getUserIdFromAuthority(
+ uri.getAuthority(), UserHandle.USER_NULL);
+ if (userIdFromAuthority != UserHandle.USER_NULL) {
+ // Won't override existing user id.
+ if (soundUri != null) {
+ soundUri = ContentProvider.maybeAddUserId(soundUri, userIdFromAuthority);
+ }
+ if (vibrationUri != null) {
+ vibrationUri = ContentProvider.maybeAddUserId(vibrationUri, userIdFromAuthority);
+ }
+ }
+
+ // Set sound uri if present, but map system default Uris to the system default source.
if (soundUri != null) {
- builder.setSoundSource(soundUri);
+ if (RingtoneManager.getDefaultType(uri) >= 0) {
+ builder.setSoundSource(SOUND_SOURCE_SYSTEM_DEFAULT);
+ } else {
+ builder.setSoundSource(soundUri);
+ }
}
if (vibrationUri != null) {
builder.setVibrationSource(vibrationUri);
}
+
// Don't set the source if there's a URI and the source is default, because that will
// override the Uri source set above. In effect, we prioritise "explicit" sources over
// an implicit Uri source - except for "default", which isn't really explicit.
- if (soundSource != SOUND_SOURCE_DEFAULT || soundUri == null) {
+ if (soundSource != SOUND_SOURCE_UNSPECIFIED || soundUri == null) {
builder.setSoundSource(soundSource);
}
- if (vibrationSource != VIBRATION_SOURCE_DEFAULT || vibrationUri == null) {
+ if (vibrationSource != VIBRATION_SOURCE_UNSPECIFIED || vibrationUri == null) {
builder.setVibrationSource(vibrationSource);
}
return builder.build();
@@ -450,26 +580,28 @@
*/
@Nullable
private static String soundSourceToString(@SoundSource int soundSource) {
- switch (soundSource) {
- case SOUND_SOURCE_OFF: return SOURCE_OFF_STRING;
- default: return null;
- }
+ return switch (soundSource) {
+ case SOUND_SOURCE_OFF -> SOURCE_OFF_STRING;
+ case SOUND_SOURCE_SYSTEM_DEFAULT -> SOURCE_SYSTEM_DEFAULT_STRING;
+ default -> null;
+ };
}
/**
* Returns the sound source int corresponding to the query string value. Returns
* {@link #SOUND_SOURCE_UNKNOWN} if the value isn't recognised, and
- * {@link #SOUND_SOURCE_DEFAULT} if the value is {@code null} (not in the Uri).
+ * {@link #SOUND_SOURCE_UNSPECIFIED} if the value is {@code null} (not in the Uri).
*/
@SoundSource
private static int stringToSoundSource(@Nullable String soundSource) {
if (soundSource == null) {
- return SOUND_SOURCE_DEFAULT;
+ return SOUND_SOURCE_UNSPECIFIED;
}
- switch (soundSource) {
- case SOURCE_OFF_STRING: return SOUND_SOURCE_OFF;
- default: return SOUND_SOURCE_UNKNOWN;
- }
+ return switch (soundSource) {
+ case SOURCE_OFF_STRING -> SOUND_SOURCE_OFF;
+ case SOURCE_SYSTEM_DEFAULT_STRING -> SOUND_SOURCE_SYSTEM_DEFAULT;
+ default -> SOUND_SOURCE_UNKNOWN;
+ };
}
/**
@@ -478,30 +610,31 @@
*/
@Nullable
private static String vibrationSourceToString(@VibrationSource int vibrationSource) {
- switch (vibrationSource) {
- case VIBRATION_SOURCE_OFF: return SOURCE_OFF_STRING;
- case VIBRATION_SOURCE_AUDIO_CHANNEL: return VIBRATION_SOURCE_AUDIO_CHANNEL_STRING;
- case VIBRATION_SOURCE_HAPTIC_GENERATOR:
- return VIBRATION_SOURCE_HAPTIC_GENERATOR_STRING;
- case VIBRATION_SOURCE_APPLICATION_PROVIDED:
- return VIBRATION_SOURCE_APPLICATION_PROVIDED_STRING;
- default: return null;
- }
+ return switch (vibrationSource) {
+ case VIBRATION_SOURCE_OFF -> SOURCE_OFF_STRING;
+ case VIBRATION_SOURCE_AUDIO_CHANNEL -> VIBRATION_SOURCE_AUDIO_CHANNEL_STRING;
+ case VIBRATION_SOURCE_HAPTIC_GENERATOR -> VIBRATION_SOURCE_HAPTIC_GENERATOR_STRING;
+ case VIBRATION_SOURCE_APPLICATION_DEFAULT ->
+ VIBRATION_SOURCE_APPLICATION_DEFAULT_STRING;
+ case VIBRATION_SOURCE_SYSTEM_DEFAULT -> SOURCE_SYSTEM_DEFAULT_STRING;
+ default -> null;
+ };
}
@VibrationSource
private static int stringToVibrationSource(@Nullable String vibrationSource) {
if (vibrationSource == null) {
- return VIBRATION_SOURCE_DEFAULT;
+ return VIBRATION_SOURCE_UNSPECIFIED;
}
- switch (vibrationSource) {
- case SOURCE_OFF_STRING: return VIBRATION_SOURCE_OFF;
- case VIBRATION_SOURCE_AUDIO_CHANNEL_STRING: return VIBRATION_SOURCE_AUDIO_CHANNEL;
- case VIBRATION_SOURCE_HAPTIC_GENERATOR_STRING: return VIBRATION_SOURCE_HAPTIC_GENERATOR;
- case VIBRATION_SOURCE_APPLICATION_PROVIDED_STRING:
- return VIBRATION_SOURCE_APPLICATION_PROVIDED;
- default: return VIBRATION_SOURCE_UNKNOWN;
- }
+ return switch (vibrationSource) {
+ case SOURCE_OFF_STRING -> VIBRATION_SOURCE_OFF;
+ case SOURCE_SYSTEM_DEFAULT_STRING -> VIBRATION_SOURCE_SYSTEM_DEFAULT;
+ case VIBRATION_SOURCE_AUDIO_CHANNEL_STRING -> VIBRATION_SOURCE_AUDIO_CHANNEL;
+ case VIBRATION_SOURCE_HAPTIC_GENERATOR_STRING -> VIBRATION_SOURCE_HAPTIC_GENERATOR;
+ case VIBRATION_SOURCE_APPLICATION_DEFAULT_STRING ->
+ VIBRATION_SOURCE_APPLICATION_DEFAULT;
+ default -> VIBRATION_SOURCE_UNKNOWN;
+ };
}
/**
@@ -512,12 +645,13 @@
public static final class Builder {
private Uri mSoundUri;
private Uri mVibrationUri;
- @SoundSource private int mSoundSource = SOUND_SOURCE_DEFAULT;
- @VibrationSource private int mVibrationSource = VIBRATION_SOURCE_DEFAULT;
+ @SoundSource private int mSoundSource = SOUND_SOURCE_UNSPECIFIED;
+ @VibrationSource private int mVibrationSource = VIBRATION_SOURCE_UNSPECIFIED;
/**
* Creates a new {@link RingtoneSelection} builder. A default ringtone selection has its
- * sound and vibration source unset, which means they would fall back to system defaults.
+ * sound and vibration source unspecified, which means they would fall back to app/system
+ * defaults.
*/
public Builder() {}
@@ -559,7 +693,9 @@
*/
@NonNull
public Builder setSoundSource(@NonNull Uri soundUri) {
- mSoundUri = requireNonNull(soundUri);
+ // getCanonicalUri shouldn't return null. If it somehow did, then the
+ // RingtoneSelection constructor will revert this to unspecified.
+ mSoundUri = requireNonNull(soundUri).getCanonicalUri();
mSoundSource = SOUND_SOURCE_URI;
return this;
}
@@ -587,7 +723,9 @@
*/
@NonNull
public Builder setVibrationSource(@NonNull Uri vibrationUri) {
- mVibrationUri = requireNonNull(vibrationUri);
+ // getCanonicalUri shouldn't return null. If it somehow did, then the
+ // RingtoneSelection constructor will revert this to unspecified.
+ mVibrationUri = requireNonNull(vibrationUri).getCanonicalUri();
mVibrationSource = VIBRATION_SOURCE_URI;
return this;
}
diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig
index 83d2b61..a354f91 100644
--- a/media/java/android/media/flags/media_better_together.aconfig
+++ b/media/java/android/media/flags/media_better_together.aconfig
@@ -15,8 +15,15 @@
}
flag {
- namespace: "media_solutions"
- name: "enable_audio_policies_device_and_bluetooth_controller"
- description: "Use Audio Policies implementation for device and Bluetooth route controllers."
- bug: "280576228"
+ namespace: "media_solutions"
+ name: "enable_audio_policies_device_and_bluetooth_controller"
+ description: "Use Audio Policies implementation for device and Bluetooth route controllers."
+ bug: "280576228"
+}
+
+flag {
+ namespace: "media_solutions"
+ name: "disable_screen_off_broadcast_receiver"
+ description: "Disables the broadcast receiver that prevents scanning when the screen is off."
+ bug: "304234628"
}
diff --git a/media/java/android/media/midi/MidiUmpDeviceService.java b/media/java/android/media/midi/MidiUmpDeviceService.java
index 6e2aaab..bbbe7f6 100644
--- a/media/java/android/media/midi/MidiUmpDeviceService.java
+++ b/media/java/android/media/midi/MidiUmpDeviceService.java
@@ -38,7 +38,7 @@
* of {@link MidiReceiver}s for sending data out the output ports.
*
* Unlike traditional MIDI byte streams, only complete UMPs should be sent.
- * Unlike with {@link #MidiDeviceService}, the number of input and output ports must be equal.
+ * Unlike with {@link MidiDeviceService}, the number of input and output ports must be equal.
*
* <p>To extend this class, you must declare the service in your manifest file with
* an intent filter with the {@link #SERVICE_INTERFACE} action
diff --git a/media/java/android/media/projection/IMediaProjectionManager.aidl b/media/java/android/media/projection/IMediaProjectionManager.aidl
index d294601..80e2247 100644
--- a/media/java/android/media/projection/IMediaProjectionManager.aidl
+++ b/media/java/android/media/projection/IMediaProjectionManager.aidl
@@ -156,4 +156,24 @@
+ ".permission.MANAGE_MEDIA_PROJECTION)")
void setUserReviewGrantedConsentResult(ReviewGrantedConsentResult consentResult,
in @nullable IMediaProjection projection);
+
+ /**
+ * Notifies system server that we are handling a particular state during the consent flow.
+ *
+ * <p>Only used for emitting atoms.
+ *
+ * @param hostUid The uid of the process requesting consent to capture, may be an app or
+ * SystemUI.
+ * @param state The state that SystemUI is handling during the consent flow.
+ * Must be a valid
+ * state defined in the MediaProjectionState enum.
+ * @param sessionCreationSource Only set if the state is MEDIA_PROJECTION_STATE_INITIATED.
+ * Indicates the entry point for requesting the permission. Must be
+ * a valid state defined
+ * in the SessionCreationSource enum.
+ */
+ @EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION")
+ @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+ + ".permission.MANAGE_MEDIA_PROJECTION)")
+ void notifyPermissionRequestStateChange(int hostUid, int state, int sessionCreationSource);
}
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..2db4be8 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -939,9 +939,8 @@
type = TYPE_HDMI;
isHardwareInput = true;
hdmiConnectionRelativePosition = getRelativePosition(mContext, mHdmiDeviceInfo);
- isConnectedToHdmiSwitch =
- hdmiConnectionRelativePosition
- != HdmiUtils.HDMI_RELATIVE_POSITION_DIRECTLY_BELOW;
+ isConnectedToHdmiSwitch = hdmiConnectionRelativePosition
+ == HdmiUtils.HDMI_RELATIVE_POSITION_BELOW;
} 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/media/tests/MediaFrameworkTest/Android.bp b/media/tests/MediaFrameworkTest/Android.bp
index ca20225e..bdd7afe 100644
--- a/media/tests/MediaFrameworkTest/Android.bp
+++ b/media/tests/MediaFrameworkTest/Android.bp
@@ -22,7 +22,7 @@
"android-ex-camera2",
"testables",
"testng",
- "truth-prebuilt",
+ "truth",
],
jni_libs: [
"libdexmakerjvmtiagent",
diff --git a/media/tests/MediaRouter/Android.bp b/media/tests/MediaRouter/Android.bp
index 4cccf89..61b18c8 100644
--- a/media/tests/MediaRouter/Android.bp
+++ b/media/tests/MediaRouter/Android.bp
@@ -24,7 +24,7 @@
"compatibility-device-util-axt",
"mockito-target-minus-junit4",
"testng",
- "truth-prebuilt",
+ "truth",
],
test_suites: ["general-tests"],
platform_apis: true,
diff --git a/media/tests/projection/Android.bp b/media/tests/projection/Android.bp
index e313c46..48cd8b6 100644
--- a/media/tests/projection/Android.bp
+++ b/media/tests/projection/Android.bp
@@ -30,7 +30,7 @@
"platform-test-annotations",
"testng",
"testables",
- "truth-prebuilt",
+ "truth",
"platform-compat-test-rules",
],
diff --git a/omapi/aidl/Android.bp b/omapi/aidl/Android.bp
index 58bcd1d..e71597a 100644
--- a/omapi/aidl/Android.bp
+++ b/omapi/aidl/Android.bp
@@ -24,6 +24,11 @@
backend: {
java: {
sdk_version: "module_current",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.nfcservices",
+ ],
+
},
rust: {
enabled: true,
diff --git a/packages/CompanionDeviceManager/res/values-pl/strings.xml b/packages/CompanionDeviceManager/res/values-pl/strings.xml
index 7ca172e..4eb8de6 100644
--- a/packages/CompanionDeviceManager/res/values-pl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pl/strings.xml
@@ -37,7 +37,7 @@
<string name="title_nearby_device_streaming" msgid="7269956847378799794">"Zezwolić urządzeniu <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> na wykonanie tego działania?"</string>
<string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> prosi w imieniu urządzenia <xliff:g id="DEVICE_NAME">%2$s</xliff:g> o uprawnienia do strumieniowego odtwarzania treści i innych funkcji systemowych na urządzeniach w pobliżu"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"urządzenie"</string>
- <string name="summary_generic" msgid="1761976003668044801">"Ta aplikacja może synchronizować informacje takie jak nazwa osoby dzwoniącej między Twoim telefonem i wybranym urządzeniem"</string>
+ <string name="summary_generic" msgid="1761976003668044801">"Ta aplikacja może synchronizować informacje takie jak imię i nazwisko osoby dzwoniącej między Twoim telefonem i wybranym urządzeniem"</string>
<string name="consent_yes" msgid="8344487259618762872">"Zezwól"</string>
<string name="consent_no" msgid="2640796915611404382">"Nie zezwalaj"</string>
<string name="consent_cancel" msgid="5655005528379285841">"Anuluj"</string>
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/res/values-hu/strings.xml b/packages/CredentialManager/res/values-hu/strings.xml
index 118a77c..c25fa99 100644
--- a/packages/CredentialManager/res/values-hu/strings.xml
+++ b/packages/CredentialManager/res/values-hu/strings.xml
@@ -70,7 +70,7 @@
<string name="accessibility_snackbar_dismiss" msgid="3456598374801836120">"Elvetés"</string>
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"Szeretné a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazáshoz mentett azonosítókulcsot használni?"</string>
<string name="get_dialog_title_use_password_for" msgid="625828023234318484">"Szeretné az elmentett jelszavát használni a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazáshoz?"</string>
- <string name="get_dialog_title_use_sign_in_for" msgid="790049858275131785">"Szeretné használni a következőhöz tartozó bejelentkezési adatait: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="790049858275131785">"Szeretné használni bejelentkezési adatait a következőhöz: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="get_dialog_title_unlock_options_for" msgid="7605568190597632433">"Feloldja a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> bejelentkezési lehetőségeit?"</string>
<string name="get_dialog_title_choose_passkey_for" msgid="9175997688078538490">"Mentett azonosítókulcs kiválasztása a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazáshoz"</string>
<string name="get_dialog_title_choose_password_for" msgid="1724435823820819221">"Mentett jelszó kiválasztása a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazáshoz"</string>
diff --git a/packages/CredentialManager/res/values-ka/strings.xml b/packages/CredentialManager/res/values-ka/strings.xml
index ed42f3a..8125ec6 100644
--- a/packages/CredentialManager/res/values-ka/strings.xml
+++ b/packages/CredentialManager/res/values-ka/strings.xml
@@ -70,12 +70,12 @@
<string name="accessibility_snackbar_dismiss" msgid="3456598374801836120">"დახურვა"</string>
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"გსურთ თქვენი დამახსოვრებული წვდომის გასაღების გამოყენება აპისთვის: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="get_dialog_title_use_password_for" msgid="625828023234318484">"გამოიყენებთ შენახულ პაროლს <xliff:g id="APP_NAME">%1$s</xliff:g>-სთვის?"</string>
- <string name="get_dialog_title_use_sign_in_for" msgid="790049858275131785">"გსურთ შესვლის <xliff:g id="APP_NAME">%1$s</xliff:g>-სთვის გამოყენება?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="790049858275131785">"გსურთ შესვლის <xliff:g id="APP_NAME">%1$s</xliff:g>-ისთვის გამოყენება?"</string>
<string name="get_dialog_title_unlock_options_for" msgid="7605568190597632433">"გსურთ შესვლის ვარიანტების განბლოკვა <xliff:g id="APP_NAME">%1$s</xliff:g>-სთვის?"</string>
<string name="get_dialog_title_choose_passkey_for" msgid="9175997688078538490">"აირჩიეთ შენახული წვდომის გასაღები <xliff:g id="APP_NAME">%1$s</xliff:g>-სთვის"</string>
<string name="get_dialog_title_choose_password_for" msgid="1724435823820819221">"აირჩიეთ შენახული პაროლი <xliff:g id="APP_NAME">%1$s</xliff:g>-სთვის"</string>
<string name="get_dialog_title_choose_saved_sign_in_for" msgid="2420298653461652728">"აირჩიეთ სისტემაში შესვლის ინფორმაცია <xliff:g id="APP_NAME">%1$s</xliff:g>-სთვის"</string>
- <string name="get_dialog_title_choose_sign_in_for" msgid="3048870756117876514">"აირჩიეთ შესვლა <xliff:g id="APP_NAME">%1$s</xliff:g>-სთვის"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="3048870756117876514">"აირჩიეთ შესვლა <xliff:g id="APP_NAME">%1$s</xliff:g>-ისთვის"</string>
<string name="get_dialog_title_choose_option_for" msgid="4976380044745029107">"გსურთ აირჩიოთ ვარიანტი <xliff:g id="APP_NAME">%1$s</xliff:g>-ისთვის?"</string>
<string name="get_dialog_title_use_info_on" msgid="8863708099535435146">"გსურთ ამ ინფორმაციის გამოყენება <xliff:g id="APP_NAME">%1$s</xliff:g>-ში?"</string>
<string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"სხვა ხერხით შესვლა"</string>
diff --git a/packages/CredentialManager/res/values-ky/strings.xml b/packages/CredentialManager/res/values-ky/strings.xml
index b4c5670..e2de2ef 100644
--- a/packages/CredentialManager/res/values-ky/strings.xml
+++ b/packages/CredentialManager/res/values-ky/strings.xml
@@ -70,12 +70,12 @@
<string name="accessibility_snackbar_dismiss" msgid="3456598374801836120">"Жабуу"</string>
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосуна кирүү үчүн сакталган ачкычты колдоносузбу?"</string>
<string name="get_dialog_title_use_password_for" msgid="625828023234318484">"<xliff:g id="APP_NAME">%1$s</xliff:g> үчүн сакталган сырсөздү колдоносузбу?"</string>
- <string name="get_dialog_title_use_sign_in_for" msgid="790049858275131785">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосуна кирүү жолун тандайсызбы?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="790049858275131785">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосуна төмөнкү аккаунт менен киресизби?"</string>
<string name="get_dialog_title_unlock_options_for" msgid="7605568190597632433">"<xliff:g id="APP_NAME">%1$s</xliff:g> үчүн кирүү параметрлеринин кулпусу ачылсынбы?"</string>
<string name="get_dialog_title_choose_passkey_for" msgid="9175997688078538490">"<xliff:g id="APP_NAME">%1$s</xliff:g> үчүн сакталган киргизүүчү ачкычты тандаңыз"</string>
<string name="get_dialog_title_choose_password_for" msgid="1724435823820819221">"<xliff:g id="APP_NAME">%1$s</xliff:g> үчүн сакталган сырсөздү тандаңыз"</string>
<string name="get_dialog_title_choose_saved_sign_in_for" msgid="2420298653461652728">"<xliff:g id="APP_NAME">%1$s</xliff:g> үчүн кирүү маалыматын тандаңыз"</string>
- <string name="get_dialog_title_choose_sign_in_for" msgid="3048870756117876514">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосуна кирүү жолун тандаңыз"</string>
+ <string name="get_dialog_title_choose_sign_in_for" msgid="3048870756117876514">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосуна кайсы аккаунт менен киресиз:"</string>
<string name="get_dialog_title_choose_option_for" msgid="4976380044745029107">"<xliff:g id="APP_NAME">%1$s</xliff:g> үчүн параметр тандайсызбы?"</string>
<string name="get_dialog_title_use_info_on" msgid="8863708099535435146">"Бул маалыматты <xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосунда пайдаланасызбы?"</string>
<string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Башка жол менен кирүү"</string>
diff --git a/packages/CredentialManager/res/values-zh-rCN/strings.xml b/packages/CredentialManager/res/values-zh-rCN/strings.xml
index 15a668a..222b865 100644
--- a/packages/CredentialManager/res/values-zh-rCN/strings.xml
+++ b/packages/CredentialManager/res/values-zh-rCN/strings.xml
@@ -70,7 +70,7 @@
<string name="accessibility_snackbar_dismiss" msgid="3456598374801836120">"忽略"</string>
<string name="get_dialog_title_use_passkey_for" msgid="6236608872708021767">"要使用您为“<xliff:g id="APP_NAME">%1$s</xliff:g>”保存的通行密钥吗?"</string>
<string name="get_dialog_title_use_password_for" msgid="625828023234318484">"要使用已保存的密码登录“<xliff:g id="APP_NAME">%1$s</xliff:g>”吗?"</string>
- <string name="get_dialog_title_use_sign_in_for" msgid="790049858275131785">"使用您的<xliff:g id="APP_NAME">%1$s</xliff:g>登录凭据?"</string>
+ <string name="get_dialog_title_use_sign_in_for" msgid="790049858275131785">"是否使用您的<xliff:g id="APP_NAME">%1$s</xliff:g>登录凭据继续?"</string>
<string name="get_dialog_title_unlock_options_for" msgid="7605568190597632433">"要解锁“<xliff:g id="APP_NAME">%1$s</xliff:g>”的登录选项吗?"</string>
<string name="get_dialog_title_choose_passkey_for" msgid="9175997688078538490">"选择一个已保存的通行密钥来登录“<xliff:g id="APP_NAME">%1$s</xliff:g>”"</string>
<string name="get_dialog_title_choose_password_for" msgid="1724435823820819221">"选择一个已保存的密码来登录“<xliff:g id="APP_NAME">%1$s</xliff:g>”"</string>
diff --git a/packages/CredentialManager/res/values-zh-rTW/strings.xml b/packages/CredentialManager/res/values-zh-rTW/strings.xml
index f14a5ce..c09bf86 100644
--- a/packages/CredentialManager/res/values-zh-rTW/strings.xml
+++ b/packages/CredentialManager/res/values-zh-rTW/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="4539824758261855508">"憑證管理工具"</string>
+ <string name="app_name" msgid="4539824758261855508">"Credential Manager"</string>
<string name="string_cancel" msgid="6369133483981306063">"取消"</string>
<string name="string_continue" msgid="1346732695941131882">"繼續"</string>
<string name="string_more_options" msgid="2763852250269945472">"儲存其他方式"</string>
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 ba88484..9355517 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -18,21 +18,31 @@
import android.app.assist.AssistStructure
import android.content.Context
-import android.credentials.GetCredentialRequest
import android.credentials.CredentialManager
-import android.credentials.GetCandidateCredentialsResponse
import android.credentials.CredentialOption
import android.credentials.GetCandidateCredentialsException
+import android.credentials.GetCandidateCredentialsResponse
+import android.credentials.GetCredentialRequest
import android.os.Bundle
import android.os.CancellationSignal
import android.os.OutcomeReceiver
-import android.service.autofill.FillRequest
import android.service.autofill.AutofillService
-import android.service.autofill.FillResponse
+import android.service.autofill.Dataset
+import android.service.autofill.Field
import android.service.autofill.FillCallback
-import android.service.autofill.SaveRequest
+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
@@ -47,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,
@@ -64,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) {
@@ -95,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) {
@@ -129,27 +212,31 @@
}
private fun traverseNode(
- viewNode: AssistStructure.ViewNode?,
+ viewNode: AssistStructure.ViewNode,
cmRequests: MutableList<CredentialOption>
) {
- val options = getCredentialOptionsFromViewNode(viewNode)
- cmRequests.addAll(options)
+ viewNode.autofillId?.let {
+ val options = getCredentialOptionsFromViewNode(viewNode, it)
+ cmRequests.addAll(options)
+ }
- val children: List<AssistStructure.ViewNode>? =
- viewNode?.run {
+ val children: List<AssistStructure.ViewNode> =
+ viewNode.run {
(0 until childCount).map { getChildAt(it) }
}
- children?.forEach { childNode: AssistStructure.ViewNode ->
+ children.forEach { childNode: AssistStructure.ViewNode ->
traverseNode(childNode, cmRequests)
}
}
- private fun getCredentialOptionsFromViewNode(viewNode: AssistStructure.ViewNode?):
- List<CredentialOption> {
+ private fun getCredentialOptionsFromViewNode(
+ viewNode: AssistStructure.ViewNode,
+ autofillId: AutofillId
+ ): List<CredentialOption> {
// TODO(b/293945193) Replace with isCredential check from viewNode
val credentialHints: MutableList<String> = mutableListOf()
- if (viewNode != null && viewNode.autofillHints != null) {
+ if (viewNode.autofillHints != null) {
for (hint in viewNode.autofillHints!!) {
if (hint.startsWith(CRED_HINT_PREFIX)) {
credentialHints.add(hint.substringAfter(CRED_HINT_PREFIX))
@@ -159,25 +246,35 @@
val credentialOptions: MutableList<CredentialOption> = mutableListOf()
for (credentialHint in credentialHints) {
- convertJsonToCredentialOption(credentialHint).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
}
- private fun convertJsonToCredentialOption(jsonString: String): List<CredentialOption> {
+ private fun convertJsonToCredentialOption(jsonString: String, autofillId: AutofillId):
+ List<CredentialOption> {
// TODO(b/302000646) Move this logic to jetpack so that is consistent
// with building the json
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))
+ candidateBundle.putParcelable(
+ CredentialProviderService.EXTRA_AUTOFILL_ID,
+ autofillId)
credentialOptions.add(CredentialOption(
option.getString(TYPE_KEY),
convertJsonToBundle(option.getJSONObject(REQUEST_DATA_KEY)),
- convertJsonToBundle(option.getJSONObject(CANDIDATE_DATA_KEY)),
+ candidateBundle,
option.getBoolean(SYS_PROVIDER_REQ_KEY),
))
}
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 4c313b2..3409c29 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -16,6 +16,8 @@
package com.android.externalstorage;
+import static java.util.regex.Pattern.CASE_INSENSITIVE;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.usage.StorageStatsManager;
@@ -64,7 +66,19 @@
import java.util.Locale;
import java.util.Objects;
import java.util.UUID;
+import java.util.regex.Pattern;
+/**
+ * Presents content of the shared (a.k.a. "external") storage.
+ * <p>
+ * Starting with Android 11 (R), restricts access to the certain sections of the shared storage:
+ * {@code Android/data/}, {@code Android/obb/} and {@code Android/sandbox/}, that will be hidden in
+ * the DocumentsUI by default.
+ * See <a href="https://developer.android.com/about/versions/11/privacy/storage">
+ * Storage updates in Android 11</a>.
+ * <p>
+ * Documents ID format: {@code root:path/to/file}.
+ */
public class ExternalStorageProvider extends FileSystemProvider {
private static final String TAG = "ExternalStorage";
@@ -75,7 +89,12 @@
private static final Uri BASE_URI =
new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY).build();
- // docId format: root:path/to/file
+ /**
+ * Regex for detecting {@code /Android/data/}, {@code /Android/obb/} and
+ * {@code /Android/sandbox/} along with all their subdirectories and content.
+ */
+ private static final Pattern PATTERN_RESTRICTED_ANDROID_SUBTREES =
+ Pattern.compile("^Android/(?:data|obb|sandbox)(?:/.+)?", CASE_INSENSITIVE);
private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE,
@@ -278,76 +297,91 @@
return projection != null ? projection : DEFAULT_ROOT_PROJECTION;
}
+ /**
+ * Mark {@code Android/data/}, {@code Android/obb/} and {@code Android/sandbox/} on the
+ * integrated shared ("external") storage along with all their content and subdirectories as
+ * hidden.
+ */
@Override
- public Cursor queryChildDocumentsForManage(
- String parentDocId, String[] projection, String sortOrder)
- throws FileNotFoundException {
- return queryChildDocumentsShowAll(parentDocId, projection, sortOrder);
+ protected boolean shouldHideDocument(@NonNull String documentId) {
+ // Don't need to hide anything on USB drives.
+ if (isOnRemovableUsbStorage(documentId)) {
+ return false;
+ }
+
+ final String path = getPathFromDocId(documentId);
+ return PATTERN_RESTRICTED_ANDROID_SUBTREES.matcher(path).matches();
}
/**
* Check that the directory is the root of storage or blocked file from tree.
+ * <p>
+ * Note, that this is different from hidden documents: blocked documents <b>WILL</b> appear
+ * the UI, but the user <b>WILL NOT</b> be able to select them.
*
- * @param docId the docId of the directory to be checked
+ * @param documentId the docId of the directory to be checked
* @return true, should be blocked from tree. Otherwise, false.
+ *
+ * @see Document#FLAG_DIR_BLOCKS_OPEN_DOCUMENT_TREE
*/
@Override
- protected boolean shouldBlockFromTree(@NonNull String docId) {
- try {
- final File dir = getFileForDocId(docId, false /* visible */);
-
- // the file is null or it is not a directory
- if (dir == null || !dir.isDirectory()) {
- return false;
- }
-
- // Allow all directories on USB, including the root.
- try {
- RootInfo rootInfo = getRootFromDocId(docId);
- if ((rootInfo.flags & Root.FLAG_REMOVABLE_USB) == Root.FLAG_REMOVABLE_USB) {
- return false;
- }
- } catch (FileNotFoundException e) {
- Log.e(TAG, "Failed to determine rootInfo for docId");
- }
-
- final String path = getPathFromDocId(docId);
-
- // Block the root of the storage
- if (path.isEmpty()) {
- return true;
- }
-
- // Block Download folder from tree
- if (TextUtils.equals(Environment.DIRECTORY_DOWNLOADS.toLowerCase(Locale.ROOT),
- path.toLowerCase(Locale.ROOT))) {
- return true;
- }
-
- // Block /Android
- if (TextUtils.equals(Environment.DIRECTORY_ANDROID.toLowerCase(Locale.ROOT),
- path.toLowerCase(Locale.ROOT))) {
- return true;
- }
-
- // Block /Android/data, /Android/obb, /Android/sandbox and sub dirs
- if (shouldHide(dir)) {
- return true;
- }
-
+ protected boolean shouldBlockDirectoryFromTree(@NonNull String documentId)
+ throws FileNotFoundException {
+ final File dir = getFileForDocId(documentId, false);
+ // The file is null or it is not a directory
+ if (dir == null || !dir.isDirectory()) {
return false;
- } catch (IOException e) {
- throw new IllegalArgumentException(
- "Failed to determine if " + docId + " should block from tree " + ": " + e);
}
+
+ // Allow all directories on USB, including the root.
+ if (isOnRemovableUsbStorage(documentId)) {
+ return false;
+ }
+
+ // Get canonical(!) path. Note that this path will have neither leading nor training "/".
+ // This the root's path will be just an empty string.
+ final String path = getPathFromDocId(documentId);
+
+ // Block the root of the storage
+ if (path.isEmpty()) {
+ return true;
+ }
+
+ // Block /Download/ and /Android/ folders from the tree.
+ if (equalIgnoringCase(path, Environment.DIRECTORY_DOWNLOADS) ||
+ equalIgnoringCase(path, Environment.DIRECTORY_ANDROID)) {
+ return true;
+ }
+
+ // This shouldn't really make a difference, but just in case - let's block hidden
+ // directories as well.
+ if (shouldHideDocument(documentId)) {
+ return true;
+ }
+
+ return false;
}
+ private boolean isOnRemovableUsbStorage(@NonNull String documentId) {
+ final RootInfo rootInfo;
+ try {
+ rootInfo = getRootFromDocId(documentId);
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "Failed to determine rootInfo for docId\"" + documentId + '"');
+ return false;
+ }
+
+ return (rootInfo.flags & Root.FLAG_REMOVABLE_USB) != 0;
+ }
+
+ @NonNull
@Override
- protected String getDocIdForFile(File file) throws FileNotFoundException {
+ protected String getDocIdForFile(@NonNull File file) throws FileNotFoundException {
return getDocIdForFileMaybeCreate(file, false);
}
- private String getDocIdForFileMaybeCreate(File file, boolean createNewDir)
+ @NonNull
+ private String getDocIdForFileMaybeCreate(@NonNull File file, boolean createNewDir)
throws FileNotFoundException {
String path = file.getAbsolutePath();
@@ -417,31 +451,33 @@
private File getFileForDocId(String docId, boolean visible, boolean mustExist)
throws FileNotFoundException {
RootInfo root = getRootFromDocId(docId);
- return buildFile(root, docId, visible, mustExist);
+ return buildFile(root, docId, mustExist);
}
- private Pair<RootInfo, File> resolveDocId(String docId, boolean visible)
- throws FileNotFoundException {
+ private Pair<RootInfo, File> resolveDocId(String docId) throws FileNotFoundException {
RootInfo root = getRootFromDocId(docId);
- return Pair.create(root, buildFile(root, docId, visible, true));
+ return Pair.create(root, buildFile(root, docId, /* mustExist */ true));
}
@VisibleForTesting
- static String getPathFromDocId(String docId) throws IOException {
+ static String getPathFromDocId(String docId) {
final int splitIndex = docId.indexOf(':', 1);
final String docIdPath = docId.substring(splitIndex + 1);
- // Get CanonicalPath and remove the first "/"
- final String canonicalPath = new File(docIdPath).getCanonicalPath().substring(1);
- if (canonicalPath.isEmpty()) {
- return canonicalPath;
+ // Canonicalize path and strip the leading "/"
+ final String path;
+ try {
+ path = new File(docIdPath).getCanonicalPath().substring(1);
+ } catch (IOException e) {
+ Log.w(TAG, "Could not canonicalize \"" + docIdPath + '"');
+ return "";
}
- // remove trailing "/"
- if (canonicalPath.charAt(canonicalPath.length() - 1) == '/') {
- return canonicalPath.substring(0, canonicalPath.length() - 1);
+ // Remove the trailing "/" as well.
+ if (!path.isEmpty() && path.charAt(path.length() - 1) == '/') {
+ return path.substring(0, path.length() - 1);
} else {
- return canonicalPath;
+ return path;
}
}
@@ -460,7 +496,7 @@
return root;
}
- private File buildFile(RootInfo root, String docId, boolean visible, boolean mustExist)
+ private File buildFile(RootInfo root, String docId, boolean mustExist)
throws FileNotFoundException {
final int splitIndex = docId.indexOf(':', 1);
final String path = docId.substring(splitIndex + 1);
@@ -544,7 +580,7 @@
@Override
public Path findDocumentPath(@Nullable String parentDocId, String childDocId)
throws FileNotFoundException {
- final Pair<RootInfo, File> resolvedDocId = resolveDocId(childDocId, false);
+ final Pair<RootInfo, File> resolvedDocId = resolveDocId(childDocId);
final RootInfo root = resolvedDocId.first;
File child = resolvedDocId.second;
@@ -648,6 +684,13 @@
}
}
+ /**
+ * Print the state into the given stream.
+ * Gets invoked when you run:
+ * <pre>
+ * adb shell dumpsys activity provider com.android.externalstorage/.ExternalStorageProvider
+ * </pre>
+ */
@Override
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ", 160);
@@ -731,4 +774,8 @@
}
return bundle;
}
+
+ private static boolean equalIgnoringCase(@NonNull String a, @NonNull String b) {
+ return TextUtils.equals(a.toLowerCase(Locale.ROOT), b.toLowerCase(Locale.ROOT));
+ }
}
diff --git a/packages/ExternalStorageProvider/tests/Android.bp b/packages/ExternalStorageProvider/tests/Android.bp
index 633f186..86c62ef 100644
--- a/packages/ExternalStorageProvider/tests/Android.bp
+++ b/packages/ExternalStorageProvider/tests/Android.bp
@@ -25,7 +25,7 @@
static_libs: [
"androidx.test.rules",
"mockito-target",
- "truth-prebuilt",
+ "truth",
],
certificate: "platform",
diff --git a/packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java b/packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java
index 18a8edc..0144b6e 100644
--- a/packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java
+++ b/packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java
@@ -16,47 +16,64 @@
package com.android.externalstorage;
+import static android.provider.DocumentsContract.EXTERNAL_STORAGE_PRIMARY_EMULATED_ROOT_ID;
+
import static com.android.externalstorage.ExternalStorageProvider.AUTHORITY;
import static com.android.externalstorage.ExternalStorageProvider.getPathFromDocId;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import android.app.Instrumentation;
+import android.content.Context;
import android.content.pm.ProviderInfo;
+import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class ExternalStorageProviderTest {
+
+ @NonNull
+ private static final Instrumentation sInstrumentation =
+ InstrumentationRegistry.getInstrumentation();
+ @NonNull
+ private static final Context sTargetContext = sInstrumentation.getTargetContext();
+
+ private ExternalStorageProvider mExternalStorageProvider;
+
+ @Before
+ public void setUp() {
+ mExternalStorageProvider = new ExternalStorageProvider();
+ }
+
+
@Test
- public void onCreate_shouldUpdateVolumes() throws Exception {
- ExternalStorageProvider externalStorageProvider = new ExternalStorageProvider();
- ExternalStorageProvider spyProvider = spy(externalStorageProvider);
- ProviderInfo providerInfo = new ProviderInfo();
+ public void onCreate_shouldUpdateVolumes() {
+ final ExternalStorageProvider spyProvider = spy(mExternalStorageProvider);
+
+ final ProviderInfo providerInfo = new ProviderInfo();
providerInfo.authority = AUTHORITY;
providerInfo.grantUriPermissions = true;
providerInfo.exported = true;
- InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- spyProvider.attachInfoForTesting(
- InstrumentationRegistry.getTargetContext(), providerInfo);
- }
- });
+ sInstrumentation.runOnMainSync(() ->
+ spyProvider.attachInfoForTesting(sTargetContext, providerInfo));
verify(spyProvider, atLeast(1)).updateVolumes();
}
@Test
- public void testGetPathFromDocId() throws Exception {
+ public void test_getPathFromDocId() {
final String root = "root";
final String path = "abc/def/ghi";
String docId = root + ":" + path;
@@ -79,4 +96,62 @@
docId = root + ":" + twoDotPath;
assertEquals(getPathFromDocId(docId), path);
}
+
+ @Test
+ public void test_shouldHideDocument() {
+ // Should hide "Android/data", "Android/obb", "Android/sandbox" and all their
+ // "subtrees".
+ final String[] shouldHide = {
+ // "Android/data" and all its subdirectories
+ "Android/data",
+ "Android/data/com.my.app",
+ "Android/data/com.my.app/cache",
+ "Android/data/com.my.app/cache/image.png",
+ "Android/data/mydata",
+
+ // "Android/obb" and all its subdirectories
+ "Android/obb",
+ "Android/obb/com.my.app",
+ "Android/obb/com.my.app/file.blob",
+
+ // "Android/sandbox" and all its subdirectories
+ "Android/sandbox",
+ "Android/sandbox/com.my.app",
+
+ // Also make sure we are not allowing path traversals
+ "Android/./data",
+ "Android/Download/../data",
+ };
+ for (String path : shouldHide) {
+ final String docId = buildDocId(path);
+ assertTrue("ExternalStorageProvider should hide \"" + docId + "\", but it didn't",
+ mExternalStorageProvider.shouldHideDocument(docId));
+ }
+
+ // Should NOT hide anything else.
+ final String[] shouldNotHide = {
+ "Android",
+ "Android/datadir",
+ "Documents",
+ "Download",
+ "Music",
+ "Pictures",
+ };
+ for (String path : shouldNotHide) {
+ final String docId = buildDocId(path);
+ assertFalse("ExternalStorageProvider should NOT hide \"" + docId + "\", but it did",
+ mExternalStorageProvider.shouldHideDocument(docId));
+ }
+ }
+
+ @NonNull
+ private static String buildDocId(@NonNull String path) {
+ return buildDocId(EXTERNAL_STORAGE_PRIMARY_EMULATED_ROOT_ID, path);
+ }
+
+ @NonNull
+ private static String buildDocId(@NonNull String root, @NonNull String path) {
+ // docId format: root:path/to/file
+ return root + ':' + path;
+ }
}
diff --git a/packages/FusedLocation/Android.bp b/packages/FusedLocation/Android.bp
index 64b4c54..61a8270 100644
--- a/packages/FusedLocation/Android.bp
+++ b/packages/FusedLocation/Android.bp
@@ -47,7 +47,7 @@
test_config: "test/AndroidTest.xml",
srcs: [
"test/src/**/*.java",
- "src/**/*.java", // include real sources because we're forced to test this directly
+ "src/**/*.java", // include real sources because we're forced to test this directly
],
libs: [
"android.test.base",
@@ -60,9 +60,9 @@
"androidx.test.ext.junit",
"androidx.test.ext.truth",
"mockito-target-minus-junit4",
- "truth-prebuilt",
+ "truth",
],
platform_apis: true,
certificate: "platform",
- test_suites: ["device-tests"]
+ test_suites: ["device-tests"],
}
diff --git a/packages/InputDevices/res/raw/keyboard_layout_brazilian.kcm b/packages/InputDevices/res/raw/keyboard_layout_brazilian.kcm
index ad3199f..d9338b1 100644
--- a/packages/InputDevices/res/raw/keyboard_layout_brazilian.kcm
+++ b/packages/InputDevices/res/raw/keyboard_layout_brazilian.kcm
@@ -337,7 +337,7 @@
label: 'M'
base: 'm'
shift, capslock: 'M'
- shift+capslock: 'n'
+ shift+capslock: 'm'
}
key COMMA {
diff --git a/packages/InputDevices/res/raw/keyboard_layout_persian.kcm b/packages/InputDevices/res/raw/keyboard_layout_persian.kcm
index 6744922..fc53cba 100644
--- a/packages/InputDevices/res/raw/keyboard_layout_persian.kcm
+++ b/packages/InputDevices/res/raw/keyboard_layout_persian.kcm
@@ -12,9 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-
-
-type FULL
+type OVERLAY
### Basic QWERTY keys ###
diff --git a/packages/InputDevices/res/xml/keyboard_layouts.xml b/packages/InputDevices/res/xml/keyboard_layouts.xml
index 88bb30b..7f23f74 100644
--- a/packages/InputDevices/res/xml/keyboard_layouts.xml
+++ b/packages/InputDevices/res/xml/keyboard_layouts.xml
@@ -227,13 +227,6 @@
android:label="@string/keyboard_layout_turkish"
android:keyboardLayout="@raw/keyboard_layout_turkish"
android:keyboardLocale="tr-Latn"
- android:keyboardLayoutType="qwerty" />
-
- <keyboard-layout
- android:name="keyboard_layout_turkish"
- android:label="@string/keyboard_layout_turkish"
- android:keyboardLayout="@raw/keyboard_layout_turkish"
- android:keyboardLocale="tr-Latn"
android:keyboardLayoutType="turkish_q" />
<keyboard-layout
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..976a3ad 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,6 +167,7 @@
dest.writeString(label.toString());
Bitmap bmp = getBitmapFromDrawable(icon);
dest.writeParcelable(bmp, 0);
+ dest.writeInt(iconSize);
}
private Bitmap getBitmapFromDrawable(Drawable drawable) {
@@ -174,6 +181,14 @@
// bitmap held within
drawable.draw(canvas);
+ // Scale it down if the icon is too large
+ if ((bmp.getWidth() > iconSize * 2) || (bmp.getHeight() > iconSize * 2)) {
+ Bitmap scaledBitmap = Bitmap.createScaledBitmap(bmp, iconSize, iconSize, true);
+ if (scaledBitmap != bmp) {
+ bmp.recycle();
+ }
+ return scaledBitmap;
+ }
return bmp;
}
@@ -241,7 +256,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/editor/SettingsTextFieldPassword.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPassword.kt
index d0a6188..0757df3 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPassword.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsTextFieldPassword.kt
@@ -29,8 +29,8 @@
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.remember
import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
@@ -46,6 +46,7 @@
fun SettingsTextFieldPassword(
value: String,
label: String,
+ enabled: Boolean = true,
onTextChange: (String) -> Unit,
) {
var visibility by remember { mutableStateOf(false) }
@@ -60,6 +61,7 @@
keyboardType = KeyboardType.Password,
imeAction = ImeAction.Send
),
+ enabled = enabled,
trailingIcon = {
Icon(
imageVector = if (visibility) Icons.Outlined.VisibilityOff
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/Spa/testutils/Android.bp b/packages/SettingsLib/Spa/testutils/Android.bp
index 4031cd7..639d1a7 100644
--- a/packages/SettingsLib/Spa/testutils/Android.bp
+++ b/packages/SettingsLib/Spa/testutils/Android.bp
@@ -31,7 +31,7 @@
"androidx.compose.ui_ui-test-manifest",
"androidx.lifecycle_lifecycle-runtime-testing",
"mockito-kotlin2",
- "truth-prebuilt",
+ "truth",
],
kotlincflags: [
"-Xjvm-default=all",
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/aconfig/settingslib_media_flag_declarations.aconfig b/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig
index d1bcb57..4936f88 100644
--- a/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig
@@ -5,4 +5,11 @@
namespace: "media_solutions"
description: "Gates whether to use a MediaRouter2-based implementation of InfoMediaManager, instead of the legacy MediaRouter2Manager-based implementation."
bug: "192657812"
+}
+
+flag {
+ name: "enable_tv_media_output_dialog"
+ namespace: "tv_system_ui"
+ description: "Gates all the changes for the tv specific media output dialog"
+ bug: "303205631"
}
\ No newline at end of file
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-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 53db192..afdb92b 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi twee stawe."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi drie stawe."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi-sein vol."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Gekoppel aan jou toestel."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Oop netwerk"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Veilige netwerk"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android-bedryfstelsel"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 77c9a97..ea8491b 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"ሁለት የWiFi አሞሌዎች።"</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"ሦስት የWiFi አሞሌዎች።"</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"የWiFi ምልክት ሙሉ ነው።"</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"ከመሣሪያዎ ጋር ተገናኝቷል።"</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"አውታረ መረብ ክፈት"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"ደህንነቱ የተጠበቀ አውታረ መረብ"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android ስርዓተ ክወና"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 30d011a..c29e691 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"إشارة Wi-Fi تتكون من شريطين."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"إشارة Wi-Fi تتكون من ثلاثة أشرطة."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"إشارة Wi-Fi كاملة."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"تم الاتصال بجهازك."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"شبكة مفتوحة"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"شبكة محمية بكلمة مرور"</string>
<string name="process_kernel_label" msgid="950292573930336765">"نظام التشغيل Android"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index 31aff6a..8eff4f6 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"ৱাই-ফাইৰ দুডাল দণ্ড।"</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"ৱাই-ফাইৰ তিনিডাল দণ্ড।"</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"ৱাই-ফাই সংকেত সৰ্বোচ্চ।"</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"আপোনাৰ ডিভাইচটোৰ সৈতে সংযোগ কৰা আছে।"</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"মুক্ত নেটৱৰ্ক"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"সুৰক্ষিত নেটৱৰ্ক"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index c675ce7..dfd1b7b 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wifi iki xətdir."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wifi üç xətdir."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wifi siqnalı tamdır."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Cihaza qoşuldu."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Açıq şəbəkə"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Təhlükəsiz şəbəkə"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index f7be60f..528e96e 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"WiFi signal ima dve crte."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"WiFi signal ima tri crte."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"WiFi signal je najjači."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Povezano je sa uređajem."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Otvorena mreža"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Bezbedna mreža"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index d2f2851..52614b7 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Два слупкi Wi-Fi."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Тры слупкi Wi-Fi."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Поўны сігнал Wi-Fi."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Устаноўлена падключэнне да прылады."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Адкрытая сетка"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Бяспечная сетка"</string>
<string name="process_kernel_label" msgid="950292573930336765">"АС Android"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 2540c12..b28ae67 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi е с две чертички."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi е с три чертички."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Сигналът за Wi-Fi е пълен."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Установена е връзка с устройството ви."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Отворена мрежа"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Защитена мрежа"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android (ОС)"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 745b944..c91f14c 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"ওয়াই ফাই এ দুইটি দণ্ড৷"</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"ওয়াই ফাই এ তিনটি দণ্ড৷"</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"ওয়াই ফাই এ সম্পূর্ণ সিগন্যাল৷"</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"আপনার ডিভাইসের সাথে কানেক্ট করা হয়েছে।"</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"খোলা নেটওয়ার্ক"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"সুরক্ষিত নেটওয়ার্ক"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index cab0841d..aa1073d 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"WiFi signal ima dvije crte."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"WiFi signal ima tri crte."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"WiFi signal je pun."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Povezani ste s uređajem."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Otvorena mreža"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Sigurna mreža"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index a65b9c2..ab6393a7 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Senyal Wi-Fi: dues barres."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Senyal Wi-Fi: tres barres."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Senyal Wi-Fi: complet."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"S\'ha connectat al teu dispositiu."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Xarxa oberta"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Xarxa segura"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 96c02ae..e7d5243 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi – dvě čárky."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi – tři čárky."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi – plný signál."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Připojeno k vašemu zařízení."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Nezabezpečená síť"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Zabezpečená síť"</string>
<string name="process_kernel_label" msgid="950292573930336765">"OS Android"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 1e4c7b7..1612ac0 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi har to bjælker."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi har tre bjælker."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi har fuldt signal."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Der er oprettet forbindelse til din enhed."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Åbent netværk"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Sikkert netværk"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 37f7e5a..73a44f1 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"WLAN: zwei Balken"</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"WLAN: drei Balken"</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"WLAN: volle Signalstärke"</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Mit deinem Gerät verbunden."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Offenes Netzwerk"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Sicheres Netzwerk"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 84b8bf2..7dfd679 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Δύο γραμμές Wi-Fi."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Τρεις γραμμές Wi-Fi."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Άριστο σήμα Wi-Fi."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Συνδέθηκε στη συσκευή σας."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Ανοικτό δίκτυο"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Ασφαλές δίκτυο"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Λειτουργικό σύστημα Android"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 7298c02..aead40d 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi two bars."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi three bars."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi signal full."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Connected to your device."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Open network"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Secure network"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 7298c02..aead40d 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi two bars."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi three bars."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi signal full."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Connected to your device."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Open network"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Secure network"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 7298c02..aead40d 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi two bars."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi three bars."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi signal full."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Connected to your device."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Open network"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Secure network"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 86541fd..291a68b 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Dos barras de Wi-Fi"</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Tres barras de Wi-Fi"</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Señal de Wi-Fi excelente"</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Se estableció conexión con el dispositivo."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Red abierta"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Red segura"</string>
<string name="process_kernel_label" msgid="950292573930336765">"SO Android"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index ef9cc03..b4bc3191 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Dos barras de Wi-Fi."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Tres barras de Wi-Fi."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Señal de Wi-Fi al máximo."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Conectado a tu dispositivo."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Red abierta"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Red segura"</string>
<string name="process_kernel_label" msgid="950292573930336765">"SO Android"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index c8d806b..e5cbaf6 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"WiFi: kaks pulka."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"WiFi: kolm pulka."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"WiFi-signaal on tugev."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Seadmega ühendatud."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Avatud võrk"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Turvaline võrk"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-eu/arrays.xml b/packages/SettingsLib/res/values-eu/arrays.xml
index 95adf96..00f43a3 100644
--- a/packages/SettingsLib/res/values-eu/arrays.xml
+++ b/packages/SettingsLib/res/values-eu/arrays.xml
@@ -264,7 +264,7 @@
<string-array name="debug_hw_overdraw_entries">
<item msgid="1968128556747588800">"Desaktibatuta"</item>
<item msgid="3033215374382962216">"Erakutsi gainidatzi diren eremuak"</item>
- <item msgid="3474333938380896988">"Erakutsi daltonismorako eremuak"</item>
+ <item msgid="3474333938380896988">"Erakutsi deuteranomaliarako eremuak"</item>
</string-array>
<string-array name="app_process_limit_entries">
<item msgid="794656271086646068">"Muga estandarra"</item>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 7d76514..90d45eb 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi sarearen bi barra."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi sarearen hiru barra."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi sarearen seinalea osoa."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Gailura konektatuta."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Sare irekia"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Sare segurua"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android sistema eragilea"</string>
@@ -368,7 +367,7 @@
<string name="debug_hw_overdraw" msgid="8944851091008756796">"Araztu GPU gainidazketa"</string>
<string name="disable_overlays" msgid="4206590799671557143">"Desgaitu HW gainjartzeak"</string>
<string name="disable_overlays_summary" msgid="1954852414363338166">"Erabili beti GPUa pantaila-muntaietarako"</string>
- <string name="simulate_color_space" msgid="1206503300335835151">"Simulatu kolore-eremua"</string>
+ <string name="simulate_color_space" msgid="1206503300335835151">"Simulatu kolore-espazioa"</string>
<string name="enable_opengl_traces_title" msgid="4638773318659125196">"Gaitu OpenGL aztarnak"</string>
<string name="usb_audio_disable_routing" msgid="3367656923544254975">"Desgaitu USB bidez audioa bideratzeko aukera"</string>
<string name="usb_audio_disable_routing_summary" msgid="8768242894849534699">"Desgaitu USB bidezko audio-gailuetara automatikoki bideratzeko aukera"</string>
@@ -441,7 +440,7 @@
<string name="picture_color_mode_desc" msgid="151780973768136200">"Erabili sRGB"</string>
<string name="daltonizer_mode_disabled" msgid="403424372812399228">"Desgaituta"</string>
<string name="daltonizer_mode_monochromacy" msgid="362060873835885014">"Ikusmen-monokromia"</string>
- <string name="daltonizer_mode_deuteranomaly" msgid="3507284319584683963">"Daltonismoa (gorri-berdeak)"</string>
+ <string name="daltonizer_mode_deuteranomaly" msgid="3507284319584683963">"Deuteranomalia (gorri-berdeak)"</string>
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanopia (gorri-berdeak)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalia (urdin-horia)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Koloreen zuzenketa"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index beb0b96..f3f97c4 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"دو نوار برای Wi‑Fi."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"سه نوار برای Wi‑Fi."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"قدرت سیگنال Wi‑Fi کامل است."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"به دستگاهتان متصل شد."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"شبکه باز"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"شبکه ایمن"</string>
<string name="process_kernel_label" msgid="950292573930336765">"سیستمعامل Android"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 8872316..7f3d0f2 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi-signaali – kaksi palkkia"</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi-signaali – kolme palkkia"</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Vahva Wi-Fi-signaali"</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Yhdistetty laitteeseesi."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Avoin verkko"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Suojattu verkko"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android-käyttöjärjestelmä"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 98e3753..b7c3a81 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi : deux barres."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi : trois barres."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi : signal complet."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Connecté à votre appareil."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Réseau ouvert"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Réseau sécurisé"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Système d\'exploitation Android"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 4a64041..a9e0c6a 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Signal Wi-Fi moyen"</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Signal Wi-Fi bon"</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Signal Wi-Fi excellent"</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Connecté à votre appareil."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Réseau ouvert"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Réseau sécurisé"</string>
<string name="process_kernel_label" msgid="950292573930336765">"OS Android"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 26738e6..09c2969 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Dúas barras de wifi."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Tres barras de wifi."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Sinal completo de wifi."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Conexión establecida co teu dispositivo."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Rede aberta"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Rede segura"</string>
<string name="process_kernel_label" msgid="950292573930336765">"SO Android"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index b620d41..575c675 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wifi બે બાર."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wifi ત્રણ બાર."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"પૂર્ણ Wifi સિગ્નલ."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"તમારા ડિવાઇસ સાથે કનેક્ટેડ છે."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"નેટવર્ક ખોલો"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"સુરક્ષિત નેટવર્ક"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 3811167..655fac0 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"वाई-फ़ाई की दो पट्टी मिल रही हैं."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"वाई-फ़ाई की एक पट्टी मिल रही है."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"पूरे वाई-फ़ाई सिग्नल मिल रहे हैं."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"आपके डिवाइस से कनेक्ट हो गया है."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"खुला नेटवर्क"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"सुरक्षित नेटवर्क"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
@@ -598,7 +597,7 @@
<string name="profile_info_settings_title" msgid="105699672534365099">"प्रोफ़ाइल की जानकारी"</string>
<string name="user_need_lock_message" msgid="4311424336209509301">"इससे पहले कि आप कोई प्रतिबंधित प्रोफ़ाइल बनाएं, आपको अपने ऐप्लिकेशन और व्यक्तिगत डेटा की सुरक्षा करने के लिए एक स्क्रीन लॉक सेट करना होगा."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"लॉक सेट करें"</string>
- <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> पर जाएं"</string>
+ <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> पर स्विच करें"</string>
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"नए उपयोगकर्ता को जोड़ा जा रहा है…"</string>
<string name="creating_new_guest_dialog_message" msgid="1114905602181350690">"नया मेहमान खाता बनाया जा रहा है…"</string>
<string name="add_user_failed" msgid="4809887794313944872">"नया उपयोगकर्ता जोड़ा नहीं जा सका"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 71a2dae..f0e16a4 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi signal ima dva stupca."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi signal ima tri stupca."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi signal je pun."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Povezano s vašim uređajem."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Otvorena mreža"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Sigurna mreža"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
@@ -619,7 +618,7 @@
<string name="guest_exit_dialog_message" msgid="1743218864242719783">"Time će se izbrisati aplikacije i podaci iz trenutačne gostujuće sesije."</string>
<string name="grant_admin" msgid="4323199171790522574">"Da, dodijeli status administratora"</string>
<string name="not_grant_admin" msgid="3557849576157702485">"Ne, nemoj dodijeliti status administratora"</string>
- <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Izlaz"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Izađi"</string>
<string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Želite li spremiti aktivnosti gosta?"</string>
<string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Možete spremiti aktivnosti iz ove sesije ili izbrisati sve aplikacije i podatke"</string>
<string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Izbriši"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 5fac75d..420e0e9 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi-jel: két sáv."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi-jel: három sáv."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi-jel: teljes."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Csatlakoztatva az eszközhöz."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Nyílt hálózat"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Biztonságos hálózat"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index e40c5e1..705fcf6 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi-ի ուժգնությունը՝ երկու գիծ:"</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi-ի ուժգնությունը՝ երեք գիծ:"</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi-ի ազդանշանը ուժեղ է:"</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Միացած է ձեր սարքին։"</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Բաց ցանց"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Անվտանգ ցանց"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index ca1b6be..e33dd05 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi dua baris"</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi tiga baris."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Sinyal Wi-Fi penuh."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Terhubung ke perangkat Anda."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Jaringan terbuka"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Jaringan aman"</string>
<string name="process_kernel_label" msgid="950292573930336765">"OS Android"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 1f2dcfb..e1fb248 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi: Tvö strik."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi: Þrjú strik."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Fullur Wi-Fi sendistyrkur."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Tengt við tækið þitt."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Opið net"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Öruggt net"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android stýrikerfið"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index e255c85..b2cfd37 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi: due barre."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi: tre barre."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Segnale Wi-Fi completo."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Connessione al dispositivo effettuata."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Rete aperta"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Rete protetta"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Sistema operativo Android"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index edcac10..9ca7477 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"שני פסים של Wi-Fi."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"שלושה פסים של Wi-Fi."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"אות Wi-Fi מלא."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"מחובר למכשיר שלך."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"רשת פתוחה"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"רשת מאובטחת"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index bf21de2..dcce2b4 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fiはレベル2です。"</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fiはレベル3です。"</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fiの電波はフルです。"</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"デバイスに接続しました。"</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"オープンネットワーク"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"保護されたネットワーク"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 9000de4..eb32e1c 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"WiFi სიგნალი ორ ზოლზეა."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"WiFi სიგნალი სამ ზოლზეა."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"WiFi სიგნალი სრულია."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"დაკავშირებულია თქვენს მოწყობილობასთან."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"ღია ქსელი"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"დაცული ქსელი"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 610fed1..73a8efd 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi сигналы — екі жолақ."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi сигналы — үш жолақ."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi сигналы толық."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Құрылғыңызға жалғанды."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Ашық желі"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Қауіпсіз желі"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index b7999c7..1c32e62 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wifi ពីរកាំ"</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wifi បីកាំ"</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"សេវា Wifi ពេញ"</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"បានភ្ជាប់ទៅឧបករណ៍របស់អ្នក។"</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"បើកបណ្ដាញ"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"បណ្តាញដែលមានសុវត្ថិភាព"</string>
<string name="process_kernel_label" msgid="950292573930336765">"ប្រព័ន្ធប្រតិបត្តិការ Android"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index c788dd5..ede347d 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"ವೈಫೈ ಎರಡು ಪಟ್ಟಿಗಳು."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"ವೈಫೈ ಮೂರು ಪಟ್ಟಿಗಳು."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"ವೈಫೈ ಸಿಗ್ನಲ್ ಪೂರ್ತಿ ಇದೆ."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"ನಿಮ್ಮ ಸಾಧನಕ್ಕೆ ಕನೆಕ್ಟ್ ಮಾಡಲಾಗಿದೆ."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"ನೆಟ್ವರ್ಕ್ ತೆರೆಯಿರಿ"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"ಸುರಕ್ಷಿತ ನೆಟ್ವರ್ಕ್"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 2c51d1f..78bd616 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi 신호 막대가 두 개입니다."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi 신호 막대가 세 개입니다."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi 신호가 강합니다."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"기기에 연결되었습니다."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"개방형 네트워크"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"보안 네트워크"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 14814f4..a0b9123 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wifi: эки таякча."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wifi: үч таякча."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wifi: күчтүү сигнал."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Түзмөгүңүзгө туташты."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Ачык тармак"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Коопсуз тармак"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 6430a98..96b2dc1 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"ສັນຍານ Wi-Fi ສອງຂີດ."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wifi ສາມຂີດ."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"ສັນຍານ Wi-Fi ເຕັມ"</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"ເຊື່ອມຕໍ່ກັບອຸປະກອນຂອງທ່ານແລ້ວ."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"ເຄືອຂ່າຍເປີດ"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"ເຄືອຂ່າຍເຂົ້າລະຫັດ"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 3642a90..69630f4 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Dvi „Wi-Fi“ signalo juostos."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Trys „Wi-Fi“ signalo juostos."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Stiprus „Wi-Fi“ signalas."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Prisijungta prie įrenginio."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Atviras tinklas"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Saugus tinklas"</string>
<string name="process_kernel_label" msgid="950292573930336765">"„Android“ OS"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index c3163b8..4f76cee 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi: divas joslas"</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi: trīs joslas"</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Pilna piekļuve Wi-Fi signālam"</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Izveidots savienojums ar ierīci."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Atvērts tīkls"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Drošs tīkls"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 017fed7..1c80c65 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Две црти на Wi-Fi."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Три црти на Wi-Fi."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Полн сигнал на Wi-Fi."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Поврзано со уредот."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Отворена мрежа"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Заклучена мрежа"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Оперативен систем Android"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 3ef3f63..31e3c83 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"വൈഫൈ സിഗ്നൽ രണ്ട് ബാറുകൾ."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"വൈഫൈ സിഗ്നൽ മൂന്ന് ബാറുകൾ."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"വൈഫൈ മികച്ച സിഗ്നൽ."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"നിങ്ങളുടെ ഉപകരണത്തിലേക്ക് കണക്റ്റ് ചെയ്തു."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"ഓപ്പൺ നെറ്റ്വര്ക്ക്"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"സുരക്ഷിത നെറ്റ്വര്ക്ക്"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index ccd1d41..5a636d9 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wifi сүлжээний дохио хоёр баганатай байна."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wifi сүлжээний дохио гурван баганатай байна."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wifi-н дохио дүүрэн байна."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Таны төхөөрөмжид холбогдсон."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Нээлттэй сүлжээ"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Аюулгүй сүлжээ"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Андройд OS"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index a34a9d3..4d78e57 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"वाय-फाय दोन बार."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"वाय-फाय तीन बार."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"वाय-फाय सिग्नल संपूर्ण आहे."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"तुमच्या डिव्हाइसशी कनेक्ट केले आहे."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"नेटवर्क उघडा"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"सुरक्षित नेटवर्क"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index b7d2734..2ae69bd 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi dua bar."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi tiga bar."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Isyarat Wi-Fi penuh."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Disambungkan kepada peranti anda."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Rangkaian terbuka"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Rangkaian selamat"</string>
<string name="process_kernel_label" msgid="950292573930336765">"OS Android"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 0e43f65..7d6f2a7 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi ၂ ဘား"</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi ၃ ဘား"</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi အပြည့်ရှိ"</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"သင့်စက်သို့ ချိတ်ဆက်ထားသည်။"</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"အများသုံး ကွန်ရက်"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"လုံခြုံသည့် ကွန်ရက်"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 43824c3..9e550fb2 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wifi-signal med to stolper."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wifi-signal med tre stolper."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wifi-signalet er ved full styrke."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Koblet til enheten."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Åpent nettverk"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Sikkert nettverk"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android-operativsystem"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index e879849..1f6ad5b 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi दुई पट्टि।"</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi तीन बारहरू।"</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"पूर्ण Wi-Fi सिंग्नल।"</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"तपाईंको डिभाइसमा कनेक्ट गरिएको छ।"</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"खुला नेटवर्क"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"सुरक्षित नेटवर्क"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index dedacff..6a6f184 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wifi: twee streepjes."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wifi: drie streepjes."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wifii-signaal is op volledige sterkte."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Verbonden met je apparaat."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Open netwerk"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Beveiligd netwerk"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android-besturingssysteem"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index cbd9ffd..980a374 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"ୱାଇ-ଫାଇର ଦୁଇଟି ବାର୍ ଅଛି।"</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"ୱାଇ-ଫାଇର ତିନୋଟି ବାର୍।"</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"ୱାଇ-ଫାଇର ସଙ୍କେତ ସର୍ବୋଚ୍ଚ।"</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"ଆପଣଙ୍କ ଡିଭାଇସ ସହ କନେକ୍ଟ କରାଯାଇଛି।"</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"ଖୋଲା ନେଟୱର୍କ"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"ସୁରକ୍ଷିତ ନେଟ୍ୱର୍କ"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index e4814ac..dd3fa15 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wifi ਦੋ ਬਾਰ।"</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wifi ਤਿੰਨ ਬਾਰ।"</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wifi ਸਿਗਨਲ ਪੂਰਾ।"</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ।"</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"ਖੁੱਲ੍ਹਾ ਨੈੱਟਵਰਕ"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"ਸੁਰੱਖਿਅਤ ਨੈੱਟਵਰਕ"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index f5dc9cc..f4ebd29 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi: dwa paski."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi: trzy paski."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi: pełna moc sygnału."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Połączono z urządzeniem."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Sieć otwarta"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Sieć zabezpieczona"</string>
<string name="process_kernel_label" msgid="950292573930336765">"System operacyjny Android"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 4e96bc6..bb6c7d8 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Duas barras de Wi-Fi."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Três barras de Wi-Fi."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Sinal Wi-Fi cheio."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Conectado ao dispositivo."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Rede aberta"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Rede segura"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Sistema operacional Android"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 0c4abdb..6bd5c2a 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Duas barras de Wi-Fi."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Três barras de Wi-Fi."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Sinal de Wi-Fi completo."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Com ligação ao dispositivo."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Rede aberta"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Rede segura"</string>
<string name="process_kernel_label" msgid="950292573930336765">"SO Android"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 4e96bc6..bb6c7d8 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Duas barras de Wi-Fi."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Três barras de Wi-Fi."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Sinal Wi-Fi cheio."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Conectado ao dispositivo."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Rede aberta"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Rede segura"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Sistema operacional Android"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 529a6c6..f4399dc 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Semnal Wi-Fi: două bare."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Semnal Wi-Fi: trei bare."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Semnal Wi-Fi: complet."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Conectată la dispozitiv."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Rețea nesecurizată"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Securizează rețeaua"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Sistem de operare Android"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 87a81ee..5d45e29 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi: два деления"</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi: три деления"</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi: надежный сигнал"</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Подключено к точке доступа на вашем устройстве."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Открытая сеть"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Защищенная сеть"</string>
<string name="process_kernel_label" msgid="950292573930336765">"ОС Android"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 9015a42..996507d 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wifi තීරු දෙකයි."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"WiFi තීරු තුනයි."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wifi සංඥාව පිරී ඇත."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"ඔබේ උපාංගයට සම්බන්ධයි."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"විවෘත ජාලය"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"ආරක්ෂිත ජාලය"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 8c6bf4f..a51acd4 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Dve čiarky signálu Wi‑Fi."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Tri čiarky signálu Wi‑Fi."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Plný signál Wi‑Fi."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Pripojené k vášmu zariadeniu."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Otvorená sieť"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Zabezpečená sieť"</string>
<string name="process_kernel_label" msgid="950292573930336765">"OS Android"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index cc19abe..0d7188d 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Dve črtici signala Wi-Fi."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Tri črtice signala Wi-Fi."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Poln signal Wi-Fi."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Povezava z napravo je vzpostavljena."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Odprto omrežje"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Varno omrežje"</string>
<string name="process_kernel_label" msgid="950292573930336765">"OS Android"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 43a2c4c..c2bcce7 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi ka dy vija."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi: tre vija."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi ka sinjal të plotë."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Lidhur me pajisjen tënde"</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Rrjet i hapur"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Rrjet i sigurt"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Sistemi operativ Android"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 00a98bca..09ff994 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"WiFi сигнал има две црте."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"WiFi сигнал има три црте."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"WiFi сигнал је најјачи."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Повезано је са уређајем."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Отворена мрежа"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Безбедна мрежа"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android ОС"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 01b462d..fdd9d8c 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wifi: två staplar."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wifi: tre staplar."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Full signalstyrka för wifi."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Ansluten till enheten."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Öppet nätverk"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Säkert nätverk"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Operativsystemet Android"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 9453e5f..bd41752 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Vipima mtandao viwili vya Wifi."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Vipima mtandao vitatu vya Wifi."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Nguvu kamili ya mtandao wa Wifi."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Imeunganishwa kwenye kifaa chako."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Mtandao unaotumiwa na mtu yeyote"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Mtandao salama"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Mfumo wa Uendeshaji wa Android"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 836b4da..fd379f0 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"வைஃபை சிக்னல்: இரண்டு கோடுகள்."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"வைஃபை சிக்னல்: மூன்று கோடுகள்."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"வைஃபை சிக்னல் முழுமையாக உள்ளது."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"உங்கள் சாதனத்துடன் இணைக்கப்பட்டது."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"கடவுச்சொல் தேவைப்படாத திறந்த நெட்வொர்க்"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"கடவுச்சொல் தேவைப்படும் பாதுகாப்பான நெட்வொர்க்"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 34b8264..3e9d8be 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wifi సిగ్నల్ రెండు బార్లు ఉంది."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wifi సిగ్నల్ మూడు బార్లు ఉంది."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wifi సిగ్నల్ పూర్తిగా ఉంది."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"మీ పరికరానికి కనెక్ట్ చేయబడింది."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"ఓపెన్ నెట్వర్క్"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"సురక్షిత నెట్వర్క్"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index da3a5c5..e67f371 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"สัญญาณ Wi-Fi 2 ขีด"</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"สัญญาณ Wi-Fi 3 ขีด"</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"สัญญาณ Wi-Fi เต็ม"</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"เชื่อมต่อกับอุปกรณ์แล้ว"</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"เครือข่ายแบบเปิด"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"เครือข่ายที่ปลอดภัย"</string>
<string name="process_kernel_label" msgid="950292573930336765">"ระบบปฏิบัติการ Android"</string>
@@ -598,7 +597,7 @@
<string name="profile_info_settings_title" msgid="105699672534365099">"ข้อมูลโปรไฟล์"</string>
<string name="user_need_lock_message" msgid="4311424336209509301">"ก่อนที่คุณจะสามารถสร้างโปรไฟล์ที่ถูกจำกัดได้ คุณจะต้องตั้งค่าล็อกหน้าจอเพื่อปกป้องแอปและข้อมูลส่วนตัวของคุณ"</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"ตั้งค่าล็อก"</string>
- <string name="user_switch_to_user" msgid="6975428297154968543">"เปลี่ยนเป็น <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+ <string name="user_switch_to_user" msgid="6975428297154968543">"เปลี่ยนเป็น<xliff:g id="USER_NAME">%s</xliff:g>"</string>
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"กำลังสร้างผู้ใช้ใหม่…"</string>
<string name="creating_new_guest_dialog_message" msgid="1114905602181350690">"กำลังสร้างผู้ใช้ชั่วคราวใหม่…"</string>
<string name="add_user_failed" msgid="4809887794313944872">"สร้างผู้ใช้ใหม่ไม่ได้"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index f6d2256..440bbe7 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"May dalawang bar ang Wifi."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"May tatlong bar ang Wifi."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Puno ang signal ng Wifi."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Nakakonekta sa iyong device."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Bukas na network"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Ligtas na network"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index acd3d00e..b38012f 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Kablosuz sinyal gücü iki çubuk."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Kablosuz sinyal gücü üç çubuk."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Kablosuz sinyal gücü tam."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Cihazınıza bağlandı."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Açık ağ"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Güvenli ağ"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index dd5898a..4f08547 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Дві смужки сигналу Wi-Fi."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Три смужки сигналу Wi-Fi."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Максимальний сигнал Wi-Fi."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Підключено до пристрою."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Відкрита мережа"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Захищена мережа"</string>
<string name="process_kernel_label" msgid="950292573930336765">"ОС Android"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 182bd04..ce67d15 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wifi دو بارز۔"</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wifi تین بارز۔"</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wifi سگنل پورا ہے۔"</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"آپ کے آلے سے منسلک ہے۔"</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"اوپن نیٹ ورک"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"محفوظ نیٹ ورک"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index b831a4b..77da981 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi: ikkita ustun"</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi: uchta ustun"</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi: signal to‘liq"</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Qurilmaga ulandi."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Ochiq tarmoq"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Xavfsiz tarmoq"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 3e94593..bf510f6 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Tín hiệu Wi-Fi hai vạch."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Tín hiệu Wi-Fi ba vạch."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Tín hiệu Wi-Fi đủ."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Đã kết nối với thiết bị của bạn."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Mạng mở"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Mạng bảo mật"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Hệ điều hành Android"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 53389c7..8e3145a 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"WLAN 信号强度为两格。"</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"WLAN 信号强度为三格。"</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"WLAN 信号满格。"</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"已连接到您的设备。"</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"开放网络"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"安全网络"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android 操作系统"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index daedba6..aa9f21f 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi 訊號兩格。"</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi 訊號三格。"</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi 訊號滿格。"</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"已連接裝置。"</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"開放式網絡"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"安全網絡"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android 作業系統"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 94ebd98..3c65a4d 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Wi-Fi 訊號強度兩格。"</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Wi-Fi 訊號強度三格。"</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Wi-Fi 訊號強度滿格。"</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"已連上裝置。"</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"開放式網路"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"安全網路"</string>
<string name="process_kernel_label" msgid="950292573930336765">"Android 作業系統"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 92a4b81..08b04cc 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -157,8 +157,7 @@
<string name="accessibility_wifi_two_bars" msgid="687800024970972270">"Amabha amabili we-Wifi."</string>
<string name="accessibility_wifi_three_bars" msgid="779895671061950234">"Amabha amathathu we-Wifi."</string>
<string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"Isiginali ye-Wifi igcwele."</string>
- <!-- no translation found for accessibility_wifi_other_device (2815627624555795918) -->
- <skip />
+ <string name="accessibility_wifi_other_device" msgid="2815627624555795918">"Ixhume kudivayisi yakho."</string>
<string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Vula inethiwekhi"</string>
<string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Inethiwekhi evikelekile"</string>
<string name="process_kernel_label" msgid="950292573930336765">"I-Android OS"</string>
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..ce0772f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -54,6 +54,7 @@
import com.android.internal.util.UserIcons;
import com.android.launcher3.icons.BaseIconFactory.IconOptions;
import com.android.launcher3.icons.IconFactory;
+import com.android.launcher3.util.UserIconInfo;
import com.android.settingslib.drawable.UserIconDrawable;
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.settingslib.utils.BuildCompatUtils;
@@ -67,8 +68,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;
@@ -598,15 +598,25 @@
/** Get the corresponding adaptive icon drawable. */
public static Drawable getBadgedIcon(Context context, Drawable icon, UserHandle user) {
- UserManager um = context.getSystemService(UserManager.class);
- boolean isClone = um.getProfiles(user.getIdentifier()).stream()
- .anyMatch(profile ->
- profile.isCloneProfile() && profile.id == user.getIdentifier());
+ int userType = UserIconInfo.TYPE_MAIN;
+ try {
+ UserInfo ui = context.getSystemService(UserManager.class).getUserInfo(
+ user.getIdentifier());
+ if (ui != null) {
+ if (ui.isCloneProfile()) {
+ userType = UserIconInfo.TYPE_CLONED;
+ } else if (ui.isManagedProfile()) {
+ userType = UserIconInfo.TYPE_WORK;
+ }
+ }
+ } catch (Exception e) {
+ // Ignore
+ }
try (IconFactory iconFactory = IconFactory.obtain(context)) {
return iconFactory
.createBadgedIconBitmap(
icon,
- new IconOptions().setUser(user).setIsCloneProfile(isClone))
+ new IconOptions().setUser(new UserIconInfo(user, userType)))
.newIcon(context);
}
}
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/integ/Android.bp b/packages/SettingsLib/tests/integ/Android.bp
index b03c43c..4b4caf5 100644
--- a/packages/SettingsLib/tests/integ/Android.bp
+++ b/packages/SettingsLib/tests/integ/Android.bp
@@ -52,7 +52,7 @@
"flag-junit",
"mockito-target-minus-junit4",
"platform-test-annotations",
- "truth-prebuilt",
+ "truth",
"SettingsLibDeviceStateRotationLock",
"SettingsLibSettingsSpinner",
"SettingsLibUsageProgressBarPreference",
diff --git a/packages/SettingsLib/tests/robotests/Android.bp b/packages/SettingsLib/tests/robotests/Android.bp
index dd9cb9c..2d875cf 100644
--- a/packages/SettingsLib/tests/robotests/Android.bp
+++ b/packages/SettingsLib/tests/robotests/Android.bp
@@ -96,6 +96,6 @@
libs: [
"Robolectric_all-target_upstream",
"mockito-robolectric-prebuilt",
- "truth-prebuilt",
+ "truth",
],
}
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/SettingsLib/tests/unit/Android.bp b/packages/SettingsLib/tests/unit/Android.bp
index 19ab1c6..6d6e2ff 100644
--- a/packages/SettingsLib/tests/unit/Android.bp
+++ b/packages/SettingsLib/tests/unit/Android.bp
@@ -31,6 +31,6 @@
"SettingsLib",
"androidx.test.ext.junit",
"androidx.test.runner",
- "truth-prebuilt",
+ "truth",
],
}
diff --git a/packages/SettingsProvider/Android.bp b/packages/SettingsProvider/Android.bp
index 92ebe09..f4ca260 100644
--- a/packages/SettingsProvider/Android.bp
+++ b/packages/SettingsProvider/Android.bp
@@ -64,7 +64,7 @@
"SettingsLibDeviceStateRotationLock",
"SettingsLibDisplayUtils",
"platform-test-annotations",
- "truth-prebuilt",
+ "truth",
],
libs: [
"android.test.base",
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/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 91d2d1b..c6e9c03 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -143,6 +143,7 @@
Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,
Settings.Secure.SCREENSAVER_HOME_CONTROLS_ENABLED,
Settings.Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION,
+ Settings.Secure.VOLUME_DIALOG_DISMISS_TIMEOUT,
Settings.Secure.VOLUME_HUSH_GESTURE,
Settings.Secure.MANUAL_RINGER_TOGGLE_COUNT,
Settings.Secure.LOW_POWER_WARNING_ACKNOWLEDGED,
@@ -218,6 +219,7 @@
Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_ALWAYS_ON_ENABLED,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_JOYSTICK_ENABLED,
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_GESTURE,
Settings.Secure.ODI_CAPTIONS_VOLUME_UI_ENABLED,
Settings.Secure.NOTIFICATION_BUBBLES,
Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED,
@@ -244,6 +246,9 @@
Settings.Secure.ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED,
Settings.Secure.SEARCH_PRESS_HOLD_NAV_HANDLE_ENABLED,
Settings.Secure.SEARCH_LONG_PRESS_HOME_ENABLED,
- Settings.Secure.HUB_MODE_TUTORIAL_STATE
+ Settings.Secure.HUB_MODE_TUTORIAL_STATE,
+ Settings.Secure.STYLUS_BUTTONS_ENABLED,
+ Settings.Secure.STYLUS_HANDWRITING_ENABLED,
+ Settings.Secure.DEFAULT_NOTE_TASK_PROFILE
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index 248c60c..fe39c4f 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -101,5 +101,10 @@
Settings.System.CAMERA_FLASH_NOTIFICATION,
Settings.System.SCREEN_FLASH_NOTIFICATION,
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/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index bec1447..0727677 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -212,6 +212,7 @@
VALIDATORS.put(Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.SCREENSAVER_HOME_CONTROLS_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.VOLUME_DIALOG_DISMISS_TIMEOUT, NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.VOLUME_HUSH_GESTURE, NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(
Secure.ENABLED_NOTIFICATION_LISTENERS,
@@ -308,6 +309,10 @@
VALIDATORS.put(Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ACCESSIBILITY_MAGNIFICATION_ALWAYS_ON_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ACCESSIBILITY_MAGNIFICATION_JOYSTICK_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.ACCESSIBILITY_MAGNIFICATION_GESTURE,
+ new InclusiveIntegerRangeValidator(
+ Secure.ACCESSIBILITY_MAGNIFICATION_GESTURE_NONE,
+ Secure.ACCESSIBILITY_MAGNIFICATION_GESTURE_ALL));
VALIDATORS.put(
Secure.ACCESSIBILITY_BUTTON_TARGETS,
ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR);
@@ -390,5 +395,9 @@
BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.DND_CONFIGS_MIGRATED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.HUB_MODE_TUTORIAL_STATE, NON_NEGATIVE_INTEGER_VALIDATOR);
+ VALIDATORS.put(Secure.STYLUS_BUTTONS_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.STYLUS_HANDWRITING_ENABLED,
+ new DiscreteValueValidator(new String[] {"-1", "0", "1"}));
+ VALIDATORS.put(Secure.DEFAULT_NOTE_TASK_PROFILE, NON_NEGATIVE_INTEGER_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 29f27f7..eba74ab 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -21,6 +21,7 @@
import static android.provider.settings.validators.SettingsValidators.BOOLEAN_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.COMPONENT_NAME_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.LENIENT_IP_ADDRESS_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.NON_NEGATIVE_FLOAT_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.URI_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.VIBRATION_INTENSITY_VALIDATOR;
@@ -30,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;
@@ -236,5 +238,10 @@
VALIDATORS.put(System.CAMERA_FLASH_NOTIFICATION, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.SCREEN_FLASH_NOTIFICATION, BOOLEAN_VALIDATOR);
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/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 3c8d4bc..f06b31c 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1850,6 +1850,10 @@
SecureSettingsProto.Accessibility
.ACCESSIBILITY_MAGNIFICATION_JOYSTICK_ENABLED);
dumpSetting(s, p,
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_GESTURE,
+ SecureSettingsProto.Accessibility
+ .ACCESSIBILITY_MAGNIFICATION_GESTURE);
+ dumpSetting(s, p,
Settings.Secure.HEARING_AID_RINGTONE_ROUTING,
SecureSettingsProto.Accessibility.HEARING_AID_RINGTONE_ROUTING);
dumpSetting(s, p,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 40f7ba6..46cd725 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -121,6 +121,7 @@
import com.android.internal.accessibility.util.AccessibilityUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
+import com.android.internal.display.RefreshRateSettingsUtils;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.FrameworkStatsLog;
import com.android.providers.settings.SettingsState.Setting;
@@ -2054,12 +2055,14 @@
final Uri ringtoneUri = Uri.parse(value);
// Stream selected ringtone into cache, so it's available for playback
// when CE storage is still locked
- try (InputStream in = openRingtone(getContext(), ringtoneUri);
- OutputStream out = new FileOutputStream(cacheFile)) {
- FileUtils.copy(in, out);
- } catch (IOException e) {
- Slog.w(LOG_TAG, "Failed to cache ringtone: " + e);
- }
+ Binder.withCleanCallingIdentity(() -> {
+ try (InputStream in = openRingtone(getContext(), ringtoneUri);
+ OutputStream out = new FileOutputStream(cacheFile)) {
+ FileUtils.copy(in, out);
+ } catch (IOException e) {
+ Slog.w(LOG_TAG, "Failed to cache ringtone: " + e);
+ }
+ });
}
return true;
}
@@ -3876,7 +3879,7 @@
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 222;
+ private static final int SETTINGS_VERSION = 223;
private final int mUserId;
@@ -5933,10 +5936,6 @@
if (currentVersion == 218) {
// Version 219: Removed
- // TODO(b/211737588): Back up the Smooth Display setting
- // Future upgrades to the `peak_refresh_rate` and `min_refresh_rate` settings
- // should account for the database in a non-upgraded and upgraded (change id:
- // Ib2cb2dd100f06f5452083b7606109a486e795a0e) state.
currentVersion = 219;
}
@@ -6002,6 +6001,56 @@
currentVersion = 222;
}
+ // Version 222: Set peak refresh rate and min refresh rate to infinity if it's
+ // meant to be the highest possible refresh rate. This is needed so that we can
+ // back up and restore those settings on other devices. Other devices might have
+ // different highest possible refresh rates.
+ if (currentVersion == 222) {
+ final SettingsState systemSettings = getSystemSettingsLocked(userId);
+ final Setting peakRefreshRateSetting =
+ systemSettings.getSettingLocked(Settings.System.PEAK_REFRESH_RATE);
+ final Setting minRefreshRateSetting =
+ systemSettings.getSettingLocked(Settings.System.MIN_REFRESH_RATE);
+ float highestRefreshRate = RefreshRateSettingsUtils
+ .findHighestRefreshRateForDefaultDisplay(getContext());
+
+ if (!peakRefreshRateSetting.isNull()) {
+ try {
+ float peakRefreshRate =
+ Float.parseFloat(peakRefreshRateSetting.getValue());
+ if (Math.round(peakRefreshRate) == Math.round(highestRefreshRate)) {
+ systemSettings.insertSettingLocked(
+ Settings.System.PEAK_REFRESH_RATE,
+ String.valueOf(Float.POSITIVE_INFINITY),
+ /* tag= */ null,
+ /* makeDefault= */ false,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+ } catch (NumberFormatException e) {
+ // Do nothing. Leave the value as is.
+ }
+ }
+
+ if (!minRefreshRateSetting.isNull()) {
+ try {
+ float minRefreshRate =
+ Float.parseFloat(minRefreshRateSetting.getValue());
+ if (Math.round(minRefreshRate) == Math.round(highestRefreshRate)) {
+ systemSettings.insertSettingLocked(
+ Settings.System.MIN_REFRESH_RATE,
+ String.valueOf(Float.POSITIVE_INFINITY),
+ /* tag= */ null,
+ /* makeDefault= */ false,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+ } catch (NumberFormatException e) {
+ // Do nothing. Leave the value as is.
+ }
+ }
+
+ currentVersion = 223;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 9ddc976a..7bca944 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -98,8 +98,6 @@
Settings.System.VOLUME_VOICE, // deprecated since API 2?
Settings.System.WHEN_TO_MAKE_WIFI_CALLS, // bug?
Settings.System.WINDOW_ORIENTATION_LISTENER_LOG, // used for debugging only
- Settings.System.MIN_REFRESH_RATE, // depends on hardware capabilities
- Settings.System.PEAK_REFRESH_RATE, // depends on hardware capabilities
Settings.System.SCREEN_BRIGHTNESS_FLOAT,
Settings.System.SCREEN_BRIGHTNESS_FOR_ALS,
Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE,
@@ -735,7 +733,6 @@
Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS,
Settings.Secure.CONTENT_CAPTURE_ENABLED,
Settings.Secure.DEFAULT_INPUT_METHOD,
- Settings.Secure.DEFAULT_NOTE_TASK_PROFILE,
Settings.Secure.DEVICE_PAIRED,
Settings.Secure.DIALER_DEFAULT_APPLICATION,
Settings.Secure.DISABLED_PRINT_SERVICES,
@@ -805,8 +802,6 @@
Settings.Secure.SLEEP_TIMEOUT,
Settings.Secure.SMS_DEFAULT_APPLICATION,
Settings.Secure.SPELL_CHECKER_ENABLED, // Intentionally removed in Q
- Settings.Secure.STYLUS_BUTTONS_ENABLED,
- Settings.Secure.STYLUS_HANDWRITING_ENABLED,
Settings.Secure.TRUST_AGENTS_INITIALIZED,
Settings.Secure.KNOWN_TRUST_AGENTS_INITIALIZED,
Settings.Secure.TV_APP_USES_NON_SYSTEM_INPUTS,
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 3c57852..4954cb4 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -60,7 +60,9 @@
// except for SystemUI-core.
// Copied from compose/features/Android.bp.
static_libs: [
+ "CommunalLayoutLib",
"PlatformComposeCore",
+ "PlatformComposeSceneTransitionLayout",
"androidx.compose.runtime_runtime",
"androidx.compose.material3_material3",
@@ -466,7 +468,7 @@
"hamcrest-library",
"androidx.test.rules",
"testables",
- "truth-prebuilt",
+ "truth",
"monet",
"libmonet",
"dagger2",
@@ -583,7 +585,7 @@
"android.test.runner",
"android.test.base",
"android.test.mock",
- "truth-prebuilt",
+ "truth",
],
upstream: true,
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 9bfc4be..5881631 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -83,6 +83,7 @@
<uses-permission android:name="android.permission.PEERS_MAC_ADDRESS"/>
<uses-permission android:name="android.permission.READ_WIFI_CREDENTIAL"/>
<uses-permission android:name="android.permission.LOCATION_HARDWARE" />
+ <uses-permission android:name="android.permission.NETWORK_FACTORY" />
<!-- Physical hardware -->
<uses-permission android:name="android.permission.MANAGE_USB" />
<uses-permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS" />
@@ -1062,5 +1063,9 @@
<meta-data android:name="androidx.emoji2.text.EmojiCompatInitializer"
tools:node="remove" />
</provider>
+
+ <!-- Allow SystemUI to listen for the capabilities defined in the linked xml -->
+ <property android:name="android.net.PROPERTY_SELF_CERTIFIED_CAPABILITIES"
+ android:value="@xml/self_certified_network_capabilities_both" />
</application>
</manifest>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-eu/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-eu/strings.xml
index f6dcdd3..b314c8e 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-eu/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-eu/strings.xml
@@ -8,7 +8,7 @@
<string name="a11y_settings_label" msgid="3977714687248445050">"Erabilerraztasun-&#173;ezarpenak"</string>
<string name="power_label" msgid="7699720321491287839">"Bateria"</string>
<string name="power_utterance" msgid="7444296686402104807">"Bateria kontrolatzeko aukerak"</string>
- <string name="recent_apps_label" msgid="6583276995616385847">"Azken aplikazioak"</string>
+ <string name="recent_apps_label" msgid="6583276995616385847">"Azkenaldiko aplikazioak"</string>
<string name="lockscreen_label" msgid="648347953557887087">"Pantaila blokeatua"</string>
<string name="quick_settings_label" msgid="2999117381487601865">"Ezarpen bizkorrak"</string>
<string name="notifications_label" msgid="6829741046963013567">"Jakinarazpenak"</string>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
index 008732e..96e1e3f 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
@@ -63,6 +63,7 @@
private static final String TAG = "A11yMenuService";
private static final long BUFFER_MILLISECONDS_TO_PREVENT_UPDATE_FAILURE = 100L;
+ private static final long TAKE_SCREENSHOT_DELAY_MS = 100L;
private static final int BRIGHTNESS_UP_INCREMENT_GAMMA =
(int) Math.ceil(BrightnessUtils.GAMMA_SPACE_MAX * 0.11f);
@@ -301,7 +302,14 @@
} else if (viewTag == ShortcutId.ID_NOTIFICATION_VALUE.ordinal()) {
performGlobalActionInternal(GLOBAL_ACTION_NOTIFICATIONS);
} else if (viewTag == ShortcutId.ID_SCREENSHOT_VALUE.ordinal()) {
- performGlobalActionInternal(GLOBAL_ACTION_TAKE_SCREENSHOT);
+ if (Flags.a11yMenuHideBeforeTakingAction()) {
+ // Delay before taking a screenshot to give time for the UI to close.
+ mHandler.postDelayed(
+ () -> performGlobalActionInternal(GLOBAL_ACTION_TAKE_SCREENSHOT),
+ TAKE_SCREENSHOT_DELAY_MS);
+ } else {
+ performGlobalActionInternal(GLOBAL_ACTION_TAKE_SCREENSHOT);
+ }
}
if (!Flags.a11yMenuHideBeforeTakingAction()) {
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp b/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp
index 538ecb3..3fc351c 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp
+++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp
@@ -32,7 +32,7 @@
"androidx.test.ext.junit",
"compatibility-device-util-axt",
"platform-test-annotations",
- "truth-prebuilt",
+ "truth",
],
srcs: [
"src/**/*.java",
diff --git a/packages/SystemUI/aconfig/Android.bp b/packages/SystemUI/aconfig/Android.bp
index c1390b2..dc4208e 100644
--- a/packages/SystemUI/aconfig/Android.bp
+++ b/packages/SystemUI/aconfig/Android.bp
@@ -2,8 +2,7 @@
name: "com_android_systemui_flags",
package: "com.android.systemui",
srcs: [
- "systemui.aconfig",
- "accessibility.aconfig",
+ "*.aconfig",
],
}
diff --git a/packages/SystemUI/aconfig/communal.aconfig b/packages/SystemUI/aconfig/communal.aconfig
new file mode 100644
index 0000000..2c6ff97
--- /dev/null
+++ b/packages/SystemUI/aconfig/communal.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.systemui"
+
+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 437f8af..2509cfd 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -13,3 +13,19 @@
description: "Enables all the sysui classic flags that are marked as being in teamfood"
bug: "302578396"
}
+
+flag {
+ name: "notifications_footer_view_refactor"
+ namespace: "systemui"
+ description: "Enables the refactored version of the footer view in the notification shade "
+ "(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 0f2e4ba..4ea57a8 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -39,6 +39,7 @@
import android.view.WindowManager
import android.view.animation.Interpolator
import android.view.animation.PathInterpolator
+import androidx.annotation.AnyThread
import androidx.annotation.BinderThread
import androidx.annotation.UiThread
import com.android.app.animation.Interpolators
@@ -149,6 +150,10 @@
override fun onLaunchAnimationProgress(linearProgress: Float) {
listeners.forEach { it.onLaunchAnimationProgress(linearProgress) }
}
+
+ override fun onLaunchAnimationCancelled() {
+ listeners.forEach { it.onLaunchAnimationCancelled() }
+ }
}
/**
@@ -191,6 +196,7 @@
"ActivityLaunchAnimator.callback must be set before using this animator"
)
val runner = createRunner(controller)
+ val runnerDelegate = runner.delegate!!
val hideKeyguardWithAnimation = callback.isOnKeyguard() && !showOverLockscreen
// Pass the RemoteAnimationAdapter to the intent starter only if we are not hiding the
@@ -241,12 +247,15 @@
// If we expect an animation, post a timeout to cancel it in case the remote animation is
// never started.
if (willAnimate) {
- runner.delegate.postTimeout()
+ runnerDelegate.postTimeout()
// Hide the keyguard using the launch animation instead of the default unlock animation.
if (hideKeyguardWithAnimation) {
callback.hideKeyguardWithAnimation(runner)
}
+ } else {
+ // We need to make sure delegate references are dropped to avoid memory leaks.
+ runner.dispose()
}
}
@@ -344,6 +353,13 @@
*/
fun onLaunchAnimationEnd() {}
+ /**
+ * The animation was cancelled. Note that [onLaunchAnimationEnd] will still be called after
+ * this if the animation was already started, i.e. if [onLaunchAnimationStart] was called
+ * before the cancellation.
+ */
+ fun onLaunchAnimationCancelled() {}
+
/** Called when an activity launch animation made progress. */
fun onLaunchAnimationProgress(linearProgress: Float) {}
}
@@ -426,6 +442,39 @@
fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean? = null) {}
}
+ /**
+ * Invokes [onAnimationComplete] when animation is either cancelled or completed. Delegates all
+ * events to the passed [delegate].
+ */
+ @VisibleForTesting
+ inner class DelegatingAnimationCompletionListener(
+ private val delegate: Listener?,
+ private val onAnimationComplete: () -> Unit
+ ) : Listener {
+ var cancelled = false
+
+ override fun onLaunchAnimationStart() {
+ delegate?.onLaunchAnimationStart()
+ }
+
+ override fun onLaunchAnimationProgress(linearProgress: Float) {
+ delegate?.onLaunchAnimationProgress(linearProgress)
+ }
+
+ override fun onLaunchAnimationEnd() {
+ delegate?.onLaunchAnimationEnd()
+ if (!cancelled) {
+ onAnimationComplete.invoke()
+ }
+ }
+
+ override fun onLaunchAnimationCancelled() {
+ cancelled = true
+ delegate?.onLaunchAnimationCancelled()
+ onAnimationComplete.invoke()
+ }
+ }
+
@VisibleForTesting
inner class Runner(
controller: Controller,
@@ -436,11 +485,21 @@
listener: Listener? = null
) : IRemoteAnimationRunner.Stub() {
private val context = controller.launchContainer.context
- internal val delegate: AnimationDelegate
+
+ // This is being passed across IPC boundaries and cycles (through PendingIntentRecords,
+ // etc.) are possible. So we need to make sure we drop any references that might
+ // transitively cause leaks when we're done with animation.
+ @VisibleForTesting var delegate: AnimationDelegate?
init {
delegate =
- AnimationDelegate(controller, callback, listener, launchAnimator, disableWmTimeout)
+ AnimationDelegate(
+ controller,
+ callback,
+ DelegatingAnimationCompletionListener(listener, this::dispose),
+ launchAnimator,
+ disableWmTimeout
+ )
}
@BinderThread
@@ -451,14 +510,33 @@
nonApps: Array<out RemoteAnimationTarget>?,
finishedCallback: IRemoteAnimationFinishedCallback?
) {
+ val delegate = delegate
context.mainExecutor.execute {
- delegate.onAnimationStart(transit, apps, wallpapers, nonApps, finishedCallback)
+ if (delegate == null) {
+ Log.i(TAG, "onAnimationStart called after completion")
+ // Animation started too late and timed out already. We need to still
+ // signal back that we're done with it.
+ finishedCallback?.onAnimationFinished()
+ } else {
+ delegate.onAnimationStart(transit, apps, wallpapers, nonApps, finishedCallback)
+ }
}
}
@BinderThread
override fun onAnimationCancelled() {
- context.mainExecutor.execute { delegate.onAnimationCancelled() }
+ val delegate = delegate
+ context.mainExecutor.execute {
+ delegate ?: Log.wtf(TAG, "onAnimationCancelled called after completion")
+ delegate?.onAnimationCancelled()
+ }
+ }
+
+ @AnyThread
+ fun dispose() {
+ // Drop references to animation controller once we're done with the animation
+ // to avoid leaking.
+ context.mainExecutor.execute { delegate = null }
}
}
@@ -584,6 +662,7 @@
)
}
controller.onLaunchAnimationCancelled()
+ listener?.onLaunchAnimationCancelled()
return
}
@@ -814,13 +893,14 @@
return
}
- Log.i(TAG, "Remote animation timed out")
+ Log.wtf(TAG, "Remote animation timed out")
timedOut = true
if (DEBUG_LAUNCH_ANIMATION) {
Log.d(TAG, "Calling controller.onLaunchAnimationCancelled() [animation timed out]")
}
controller.onLaunchAnimationCancelled()
+ listener?.onLaunchAnimationCancelled()
}
@UiThread
@@ -842,6 +922,7 @@
)
}
controller.onLaunchAnimationCancelled()
+ listener?.onLaunchAnimationCancelled()
}
private fun IRemoteAnimationFinishedCallback.invoke() {
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/tests/Android.bp b/packages/SystemUI/compose/core/tests/Android.bp
index 52c6385..8e9c586 100644
--- a/packages/SystemUI/compose/core/tests/Android.bp
+++ b/packages/SystemUI/compose/core/tests/Android.bp
@@ -43,7 +43,7 @@
"androidx.compose.ui_ui-test-junit4",
"androidx.compose.ui_ui-test-manifest",
- "truth-prebuilt",
+ "truth",
],
kotlincflags: ["-Xjvm-default=all"],
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/Android.bp b/packages/SystemUI/compose/features/Android.bp
index e4426fe..796abf4b 100644
--- a/packages/SystemUI/compose/features/Android.bp
+++ b/packages/SystemUI/compose/features/Android.bp
@@ -33,6 +33,7 @@
static_libs: [
"SystemUI-core",
"PlatformComposeCore",
+ "PlatformComposeSceneTransitionLayout",
"androidx.compose.runtime_runtime",
"androidx.compose.animation_animation-graphics",
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/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
index c3a3752..ee310ab 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
@@ -36,13 +36,14 @@
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.view.isVisible
import com.android.compose.animation.scene.SceneScope
-import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.qualifiers.KeyguardRootView
import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
+import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.Direction
+import com.android.systemui.scene.shared.model.Edge
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
import com.android.systemui.scene.shared.model.UserAction
@@ -99,7 +100,9 @@
return buildMap {
up?.let { this[UserAction.Swipe(Direction.UP)] = SceneModel(up) }
left?.let { this[UserAction.Swipe(Direction.LEFT)] = SceneModel(left) }
- this[UserAction.Swipe(Direction.DOWN)] = SceneModel(SceneKey.Shade)
+ this[UserAction.Swipe(fromEdge = Edge.TOP, direction = Direction.DOWN)] =
+ SceneModel(SceneKey.QuickSettings)
+ this[UserAction.Swipe(direction = Direction.DOWN)] = SceneModel(SceneKey.Shade)
}
}
}
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/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
index 1f9c3e6..a33eac5 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
@@ -23,11 +23,13 @@
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.android.compose.animation.scene.SceneScope
+import com.android.compose.windowsizeclass.LocalWindowSizeClass
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.qs.footer.ui.compose.QuickSettings
@@ -37,6 +39,7 @@
import com.android.systemui.scene.shared.model.SceneModel
import com.android.systemui.scene.shared.model.UserAction
import com.android.systemui.scene.ui.composable.ComposableScene
+import com.android.systemui.shade.ui.composable.CollapsedShadeHeader
import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
import com.android.systemui.statusbar.phone.StatusBarIconController
import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager
@@ -98,12 +101,22 @@
.clickable(onClick = { viewModel.onContentClicked() })
.padding(start = 16.dp, end = 16.dp, bottom = 48.dp)
) {
- ExpandedShadeHeader(
- viewModel = viewModel.shadeHeaderViewModel,
- createTintedIconManager = createTintedIconManager,
- createBatteryMeterViewController = createBatteryMeterViewController,
- statusBarIconController = statusBarIconController,
- )
+ when (LocalWindowSizeClass.current.widthSizeClass) {
+ WindowWidthSizeClass.Compact ->
+ ExpandedShadeHeader(
+ viewModel = viewModel.shadeHeaderViewModel,
+ createTintedIconManager = createTintedIconManager,
+ createBatteryMeterViewController = createBatteryMeterViewController,
+ statusBarIconController = statusBarIconController,
+ )
+ else ->
+ CollapsedShadeHeader(
+ viewModel = viewModel.shadeHeaderViewModel,
+ createTintedIconManager = createTintedIconManager,
+ createBatteryMeterViewController = createBatteryMeterViewController,
+ statusBarIconController = statusBarIconController,
+ )
+ }
Spacer(modifier = Modifier.height(16.dp))
QuickSettings()
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
index 2ee461f..f35ea83 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
@@ -22,6 +22,7 @@
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.scene.shared.model.Direction
+import com.android.systemui.scene.shared.model.Edge
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
import com.android.systemui.scene.shared.model.UserAction
@@ -41,7 +42,12 @@
override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> =
MutableStateFlow<Map<UserAction, SceneModel>>(
mapOf(
- UserAction.Swipe(Direction.DOWN) to SceneModel(SceneKey.Shade),
+ UserAction.Swipe(
+ pointerCount = 2,
+ fromEdge = Edge.TOP,
+ direction = Direction.DOWN,
+ ) to SceneModel(SceneKey.QuickSettings),
+ UserAction.Swipe(direction = Direction.DOWN) to SceneModel(SceneKey.Shade),
)
)
.asStateFlow()
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 ef01266..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
@@ -35,15 +35,18 @@
import androidx.compose.ui.input.pointer.motionEventSpy
import androidx.compose.ui.input.pointer.pointerInput
import com.android.compose.animation.scene.Back
+import com.android.compose.animation.scene.Edge as SceneTransitionEdge
import com.android.compose.animation.scene.ObservableTransitionState as SceneTransitionObservableTransitionState
import com.android.compose.animation.scene.SceneKey as SceneTransitionSceneKey
import com.android.compose.animation.scene.SceneTransitionLayout
import com.android.compose.animation.scene.SceneTransitionLayoutState
import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserAction as SceneTransitionUserAction
import com.android.compose.animation.scene.observableTransitionState
import com.android.systemui.ribbon.ui.composable.BottomRightCornerRibbon
import com.android.systemui.scene.shared.model.Direction
+import com.android.systemui.scene.shared.model.Edge
import com.android.systemui.scene.shared.model.ObservableTransitionState
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
@@ -180,12 +183,24 @@
private fun UserAction.toTransitionUserAction(): SceneTransitionUserAction {
return when (this) {
is UserAction.Swipe ->
- when (this.direction) {
- Direction.LEFT -> Swipe.Left
- Direction.UP -> Swipe.Up
- Direction.RIGHT -> Swipe.Right
- Direction.DOWN -> Swipe.Down
- }
+ Swipe(
+ pointerCount = pointerCount,
+ fromEdge =
+ when (this.fromEdge) {
+ null -> null
+ Edge.LEFT -> SceneTransitionEdge.Left
+ Edge.TOP -> SceneTransitionEdge.Top
+ Edge.RIGHT -> SceneTransitionEdge.Right
+ Edge.BOTTOM -> SceneTransitionEdge.Bottom
+ },
+ direction =
+ when (this.direction) {
+ Direction.LEFT -> SwipeDirection.Left
+ Direction.UP -> SwipeDirection.Up
+ Direction.RIGHT -> SwipeDirection.Right
+ Direction.DOWN -> SwipeDirection.Down
+ }
+ )
is UserAction.Back -> Back
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
index 6629a25..4bbb78b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
@@ -30,6 +30,7 @@
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
+import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
@@ -50,6 +51,7 @@
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.ValueKey
import com.android.compose.animation.scene.animateSharedFloatAsState
+import com.android.compose.windowsizeclass.LocalWindowSizeClass
import com.android.settingslib.Utils
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
@@ -98,12 +100,12 @@
ShadeHeader.Keys.transitionProgress,
ShadeHeader.Elements.FormatPlaceholder
)
- val useExpandedFormat by
- remember(formatProgress) { derivedStateOf { formatProgress.value > 0.5f } }
val cutoutWidth = LocalDisplayCutout.current.width()
val cutoutLocation = LocalDisplayCutout.current.location
+ val useExpandedFormat = formatProgress.value > 0.5f || cutoutLocation != CutoutLocation.CENTER
+
// This layout assumes it is globally positioned at (0, 0) and is the
// same size as the screen.
Layout(
@@ -131,6 +133,14 @@
{
Row(horizontalArrangement = Arrangement.End) {
SystemIconContainer {
+ when (LocalWindowSizeClass.current.widthSizeClass) {
+ WindowWidthSizeClass.Medium,
+ WindowWidthSizeClass.Expanded ->
+ ShadeCarrierGroup(
+ viewModel = viewModel,
+ modifier = Modifier.align(Alignment.CenterVertically),
+ )
+ }
StatusIcons(
viewModel = viewModel,
createTintedIconManager = createTintedIconManager,
@@ -361,7 +371,8 @@
val iconContainer = StatusIconContainer(context, null)
val iconManager = createTintedIconManager(iconContainer, StatusBarLocation.QS)
iconManager.setTint(
- Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary)
+ Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary),
+ Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimaryInverse),
)
statusBarIconController.addIconGroup(iconManager)
diff --git a/packages/SystemUI/compose/scene/Android.bp b/packages/SystemUI/compose/scene/Android.bp
new file mode 100644
index 0000000..050d1d5
--- /dev/null
+++ b/packages/SystemUI/compose/scene/Android.bp
@@ -0,0 +1,39 @@
+// 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
+}
+
+android_library {
+ name: "PlatformComposeSceneTransitionLayout",
+ manifest: "AndroidManifest.xml",
+
+ srcs: [
+ "src/**/*.kt",
+ ],
+
+ static_libs: [
+ "androidx.compose.runtime_runtime",
+ "androidx.compose.material3_material3",
+ ],
+
+ kotlincflags: ["-Xjvm-default=all"],
+ use_resource_processor: true,
+}
diff --git a/packages/SystemUI/compose/scene/AndroidManifest.xml b/packages/SystemUI/compose/scene/AndroidManifest.xml
new file mode 100644
index 0000000..81131bb
--- /dev/null
+++ b/packages/SystemUI/compose/scene/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.compose.animation.scene">
+
+
+</manifest>
diff --git a/packages/SystemUI/compose/scene/OWNERS b/packages/SystemUI/compose/scene/OWNERS
new file mode 100644
index 0000000..33a59c2
--- /dev/null
+++ b/packages/SystemUI/compose/scene/OWNERS
@@ -0,0 +1,13 @@
+set noparent
+
+# Bug component: 1184816
+
+jdemeulenaere@google.com
+omarmt@google.com
+
+# SysUI Dr No's.
+# Don't send reviews here.
+dsandler@android.com
+cinek@google.com
+juliacr@google.com
+pixel@google.com
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/TEST_MAPPING b/packages/SystemUI/compose/scene/TEST_MAPPING
new file mode 100644
index 0000000..f644a23
--- /dev/null
+++ b/packages/SystemUI/compose/scene/TEST_MAPPING
@@ -0,0 +1,48 @@
+{
+ "presubmit": [
+ {
+ "name": "PlatformComposeSceneTransitionLayoutTests",
+ "options": [
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "PlatformComposeCoreTests",
+ "options": [
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "SystemUIComposeFeaturesTests",
+ "options": [
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "SystemUIComposeGalleryTests",
+ "options": [
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateSharedAsState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
similarity index 71%
rename from packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
rename to packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
index 566967f..041fc48 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
@@ -17,12 +17,10 @@
package com.android.compose.animation.scene
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.DisposableEffectResult
-import androidx.compose.runtime.DisposableEffectScope
import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.snapshots.Snapshot
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.lerp
import androidx.compose.ui.unit.Dp
@@ -45,6 +43,20 @@
}
/**
+ * Animate a shared Int value.
+ *
+ * @see MovableElementScope.animateSharedValueAsState
+ */
+@Composable
+fun MovableElementScope.animateSharedIntAsState(
+ value: Int,
+ debugName: String,
+ canOverflow: Boolean = true,
+): State<Int> {
+ return animateSharedValueAsState(value, debugName, ::lerp, canOverflow)
+}
+
+/**
* Animate a shared Float value.
*
* @see SceneScope.animateSharedValueAsState
@@ -60,6 +72,20 @@
}
/**
+ * Animate a shared Float value.
+ *
+ * @see MovableElementScope.animateSharedValueAsState
+ */
+@Composable
+fun MovableElementScope.animateSharedFloatAsState(
+ value: Float,
+ debugName: String,
+ canOverflow: Boolean = true,
+): State<Float> {
+ return animateSharedValueAsState(value, debugName, ::lerp, canOverflow)
+}
+
+/**
* Animate a shared Dp value.
*
* @see SceneScope.animateSharedValueAsState
@@ -75,6 +101,20 @@
}
/**
+ * Animate a shared Dp value.
+ *
+ * @see MovableElementScope.animateSharedValueAsState
+ */
+@Composable
+fun MovableElementScope.animateSharedDpAsState(
+ value: Dp,
+ debugName: String,
+ canOverflow: Boolean = true,
+): State<Dp> {
+ return animateSharedValueAsState(value, debugName, ::lerp, canOverflow)
+}
+
+/**
* Animate a shared Color value.
*
* @see SceneScope.animateSharedValueAsState
@@ -88,6 +128,19 @@
return animateSharedValueAsState(value, key, element, ::lerp, canOverflow = false)
}
+/**
+ * Animate a shared Color value.
+ *
+ * @see MovableElementScope.animateSharedValueAsState
+ */
+@Composable
+fun MovableElementScope.animateSharedColorAsState(
+ value: Color,
+ debugName: String,
+): State<Color> {
+ return animateSharedValueAsState(value, debugName, ::lerp, canOverflow = false)
+}
+
@Composable
internal fun <T> animateSharedValueAsState(
layoutImpl: SceneTransitionLayoutImpl,
@@ -98,33 +151,22 @@
lerp: (T, T, Float) -> T,
canOverflow: Boolean,
): State<T> {
- val sharedValue = remember(key) { Element.SharedValue(key, value) }
+ val sharedValue =
+ Snapshot.withoutReadObservation {
+ element.sceneValues.getValue(scene.key).sharedValues.getOrPut(key) {
+ Element.SharedValue(key, value)
+ } as Element.SharedValue<T>
+ }
+
if (value != sharedValue.value) {
sharedValue.value = value
}
- DisposableEffect(element, scene, sharedValue) {
- addSharedValueToElement(element, scene, sharedValue)
- }
-
return remember(layoutImpl, element, sharedValue, lerp, canOverflow) {
derivedStateOf { computeValue(layoutImpl, element, sharedValue, lerp, canOverflow) }
}
}
-private fun <T> DisposableEffectScope.addSharedValueToElement(
- element: Element,
- scene: Scene,
- sharedValue: Element.SharedValue<T>,
-): DisposableEffectResult {
- val sceneValues =
- element.sceneValues[scene.key] ?: error("Element $element is not present in $scene")
- val sharedValues = sceneValues.sharedValues
-
- sharedValues[sharedValue.key] = sharedValue
- return onDispose { sharedValues.remove(sharedValue.key) }
-}
-
private fun <T> computeValue(
layoutImpl: SceneTransitionLayoutImpl,
element: Element,
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
similarity index 100%
rename from packages/SystemUI/compose/core/src/com/android/compose/animation/scene/AnimateToScene.kt
rename to packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
similarity index 97%
rename from packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Element.kt
rename to packages/SystemUI/compose/scene/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/scene/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/ElementMatcher.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ElementMatcher.kt
similarity index 100%
rename from packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ElementMatcher.kt
rename to packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ElementMatcher.kt
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Key.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt
similarity index 91%
rename from packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Key.kt
rename to packages/SystemUI/compose/scene/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/scene/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/scene/src/com/android/compose/animation/scene/MovableElement.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
new file mode 100644
index 0000000..6dbeb69
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
@@ -0,0 +1,200 @@
+/*
+ * 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.State
+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"
+
+@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) }
+ val movableElementScope =
+ remember(layoutImpl, element, scene) {
+ MovableElementScopeImpl(layoutImpl, element, scene)
+ }
+
+ // 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 { movableElementScope.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
+ }
+}
+
+private class MovableElementScopeImpl(
+ private val layoutImpl: SceneTransitionLayoutImpl,
+ private val element: Element,
+ private val scene: Scene,
+) : MovableElementScope {
+ @Composable
+ override fun <T> animateSharedValueAsState(
+ value: T,
+ debugName: String,
+ lerp: (start: T, stop: T, fraction: Float) -> T,
+ canOverflow: Boolean,
+ ): State<T> {
+ val key = remember { ValueKey(debugName) }
+ return animateSharedValueAsState(layoutImpl, scene, element, key, value, lerp, canOverflow)
+ }
+}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ObservableTransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
similarity index 100%
rename from packages/SystemUI/compose/core/src/com/android/compose/animation/scene/ObservableTransitionState.kt
rename to packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Scene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
similarity index 91%
rename from packages/SystemUI/compose/core/src/com/android/compose/animation/scene/Scene.kt
rename to packages/SystemUI/compose/scene/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/scene/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/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
similarity index 71%
rename from packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
rename to packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index 39173d9..74e66d2 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/scene/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,14 +158,40 @@
): 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 {
+ @Composable
+ fun <T> animateSharedValueAsState(
+ value: T,
+ debugName: String,
+ lerp: (start: T, stop: T, fraction: Float) -> T,
+ canOverflow: Boolean,
+ ): State<T>
+}
+
/** An action performed by the user. */
sealed interface UserAction
/** The user navigated back, either using a gesture or by triggering a KEYCODE_BACK event. */
-object Back : UserAction
+data object Back : UserAction
/** The user swiped on the container. */
-enum class Swipe : UserAction {
+data class Swipe(
+ val direction: SwipeDirection,
+ val pointerCount: Int = 1,
+ val fromEdge: Edge? = null,
+) : UserAction {
+ companion object {
+ val Left = Swipe(SwipeDirection.Left)
+ val Up = Swipe(SwipeDirection.Up)
+ val Right = Swipe(SwipeDirection.Right)
+ val Down = Swipe(SwipeDirection.Down)
+ }
+}
+
+enum class SwipeDirection {
Up,
Down,
Left,
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
similarity index 98%
rename from packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
rename to packages/SystemUI/compose/scene/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/scene/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/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
similarity index 100%
rename from packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
rename to packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitions.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
similarity index 100%
rename from packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SceneTransitions.kt
rename to packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
similarity index 100%
rename from packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt
rename to packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/TransitionDsl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
similarity index 100%
rename from packages/SystemUI/compose/core/src/com/android/compose/animation/scene/TransitionDsl.kt
rename to packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/TransitionDslImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
similarity index 100%
rename from packages/SystemUI/compose/core/src/com/android/compose/animation/scene/TransitionDslImpl.kt
rename to packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
similarity index 100%
rename from packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
rename to packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
similarity index 100%
rename from packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
rename to packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
similarity index 100%
rename from packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
rename to packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Fade.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
similarity index 100%
rename from packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Fade.kt
rename to packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/PunchHole.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/PunchHole.kt
similarity index 100%
rename from packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/PunchHole.kt
rename to packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/PunchHole.kt
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/ScaleSize.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
similarity index 100%
rename from packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
rename to packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Transformation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
similarity index 100%
rename from packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Transformation.kt
rename to packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Translate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
similarity index 100%
rename from packages/SystemUI/compose/core/src/com/android/compose/animation/scene/transformation/Translate.kt
rename to packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/grid/Grids.kt b/packages/SystemUI/compose/scene/src/com/android/compose/grid/Grids.kt
similarity index 100%
rename from packages/SystemUI/compose/core/src/com/android/compose/grid/Grids.kt
rename to packages/SystemUI/compose/scene/src/com/android/compose/grid/Grids.kt
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/modifiers/ConditionalModifiers.kt b/packages/SystemUI/compose/scene/src/com/android/compose/modifiers/ConditionalModifiers.kt
similarity index 100%
rename from packages/SystemUI/compose/core/src/com/android/compose/modifiers/ConditionalModifiers.kt
rename to packages/SystemUI/compose/scene/src/com/android/compose/modifiers/ConditionalModifiers.kt
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
similarity index 100%
rename from packages/SystemUI/compose/core/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
rename to packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/nestedscroll/PriorityPostNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityPostNestedScrollConnection.kt
similarity index 100%
rename from packages/SystemUI/compose/core/src/com/android/compose/nestedscroll/PriorityPostNestedScrollConnection.kt
rename to packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityPostNestedScrollConnection.kt
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/ui/util/ListUtils.kt b/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/ListUtils.kt
similarity index 100%
rename from packages/SystemUI/compose/core/src/com/android/compose/ui/util/ListUtils.kt
rename to packages/SystemUI/compose/scene/src/com/android/compose/ui/util/ListUtils.kt
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/ui/util/MathHelpers.kt b/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/MathHelpers.kt
similarity index 100%
rename from packages/SystemUI/compose/core/src/com/android/compose/ui/util/MathHelpers.kt
rename to packages/SystemUI/compose/scene/src/com/android/compose/ui/util/MathHelpers.kt
diff --git a/packages/SystemUI/compose/scene/tests/Android.bp b/packages/SystemUI/compose/scene/tests/Android.bp
new file mode 100644
index 0000000..b53fae2
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/Android.bp
@@ -0,0 +1,50 @@
+// 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
+}
+
+android_test {
+ name: "PlatformComposeSceneTransitionLayoutTests",
+ manifest: "AndroidManifest.xml",
+ test_suites: ["device-tests"],
+ sdk_version: "current",
+ certificate: "platform",
+
+ srcs: [
+ "src/**/*.kt",
+ ],
+
+ static_libs: [
+ "PlatformComposeSceneTransitionLayout",
+
+ "androidx.test.runner",
+ "androidx.test.ext.junit",
+
+ "androidx.compose.runtime_runtime",
+ "androidx.compose.ui_ui-test-junit4",
+ "androidx.compose.ui_ui-test-manifest",
+
+ "truth",
+ ],
+
+ kotlincflags: ["-Xjvm-default=all"],
+ use_resource_processor: true,
+}
diff --git a/packages/SystemUI/compose/scene/tests/AndroidManifest.xml b/packages/SystemUI/compose/scene/tests/AndroidManifest.xml
new file mode 100644
index 0000000..1a9172e
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.compose.animation.scene.tests" >
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.compose.animation.scene.tests"
+ android:label="Tests for SceneTransitionLayout"/>
+
+</manifest>
\ No newline at end of file
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
new file mode 100644
index 0000000..7b7695e
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
@@ -0,0 +1,218 @@
+/*
+ * 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.layout.Box
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.SideEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.lerp
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.lerp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.ui.util.lerp
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class AnimatedSharedAsStateTest {
+ @get:Rule val rule = createComposeRule()
+
+ private data class Values(
+ val int: Int,
+ val float: Float,
+ val dp: Dp,
+ val color: Color,
+ )
+
+ private fun lerp(start: Values, stop: Values, fraction: Float): Values {
+ return Values(
+ int = lerp(start.int, stop.int, fraction),
+ float = lerp(start.float, stop.float, fraction),
+ dp = lerp(start.dp, stop.dp, fraction),
+ color = lerp(start.color, stop.color, fraction),
+ )
+ }
+
+ @Composable
+ private fun SceneScope.Foo(
+ targetValues: Values,
+ onCurrentValueChanged: (Values) -> Unit,
+ ) {
+ val key = TestElements.Foo
+ Box(Modifier.element(key)) {
+ val int by animateSharedIntAsState(targetValues.int, TestValues.Value1, key)
+ val float by animateSharedFloatAsState(targetValues.float, TestValues.Value2, key)
+ val dp by animateSharedDpAsState(targetValues.dp, TestValues.Value3, key)
+ val color by animateSharedColorAsState(targetValues.color, TestValues.Value4, key)
+
+ // Make sure we read the values during composition, so that we recompose and call
+ // onCurrentValueChanged() with the latest values.
+ val currentValues = Values(int, float, dp, color)
+ SideEffect { onCurrentValueChanged(currentValues) }
+ }
+ }
+
+ @Composable
+ private fun SceneScope.MovableFoo(
+ targetValues: Values,
+ onCurrentValueChanged: (Values) -> Unit,
+ ) {
+ val key = TestElements.Foo
+ MovableElement(key = key, Modifier) {
+ val int by
+ animateSharedIntAsState(targetValues.int, debugName = TestValues.Value1.debugName)
+ val float by
+ animateSharedFloatAsState(
+ targetValues.float,
+ debugName = TestValues.Value2.debugName
+ )
+ val dp by
+ animateSharedDpAsState(targetValues.dp, debugName = TestValues.Value3.debugName)
+ val color by
+ animateSharedColorAsState(
+ targetValues.color,
+ debugName = TestValues.Value4.debugName
+ )
+
+ // Make sure we read the values during composition, so that we recompose and call
+ // onCurrentValueChanged() with the latest values.
+ val currentValues = Values(int, float, dp, color)
+ SideEffect { onCurrentValueChanged(currentValues) }
+ }
+ }
+
+ @Test
+ fun animateSharedValues() {
+ val fromValues = Values(int = 0, float = 0f, dp = 0.dp, color = Color.Red)
+ val toValues = Values(int = 100, float = 100f, dp = 100.dp, color = Color.Blue)
+
+ var lastValueInFrom = fromValues
+ var lastValueInTo = toValues
+
+ rule.testTransition(
+ fromSceneContent = {
+ Foo(targetValues = fromValues, onCurrentValueChanged = { lastValueInFrom = it })
+ },
+ toSceneContent = {
+ Foo(targetValues = toValues, onCurrentValueChanged = { lastValueInTo = it })
+ },
+ transition = {
+ // The transition lasts 64ms = 4 frames.
+ spec = tween(durationMillis = 16 * 4, easing = LinearEasing)
+ },
+ fromScene = TestScenes.SceneA,
+ toScene = TestScenes.SceneB,
+ ) {
+ before {
+ assertThat(lastValueInFrom).isEqualTo(fromValues)
+
+ // to was not composed yet, so lastValueInTo was not set yet.
+ assertThat(lastValueInTo).isEqualTo(toValues)
+ }
+
+ at(16) {
+ // Given that we use Modifier.element() here, animateSharedXAsState is composed in
+ // both scenes and values should be interpolated with the transition fraction.
+ val expectedValues = lerp(fromValues, toValues, fraction = 0.25f)
+ assertThat(lastValueInFrom).isEqualTo(expectedValues)
+ assertThat(lastValueInTo).isEqualTo(expectedValues)
+ }
+
+ at(32) {
+ val expectedValues = lerp(fromValues, toValues, fraction = 0.5f)
+ assertThat(lastValueInFrom).isEqualTo(expectedValues)
+ assertThat(lastValueInTo).isEqualTo(expectedValues)
+ }
+
+ at(48) {
+ val expectedValues = lerp(fromValues, toValues, fraction = 0.75f)
+ assertThat(lastValueInFrom).isEqualTo(expectedValues)
+ assertThat(lastValueInTo).isEqualTo(expectedValues)
+ }
+
+ after {
+ assertThat(lastValueInFrom).isEqualTo(toValues)
+ assertThat(lastValueInTo).isEqualTo(toValues)
+ }
+ }
+ }
+
+ @Test
+ fun movableAnimateSharedValues() {
+ val fromValues = Values(int = 0, float = 0f, dp = 0.dp, color = Color.Red)
+ val toValues = Values(int = 100, float = 100f, dp = 100.dp, color = Color.Blue)
+
+ var lastValueInFrom = fromValues
+ var lastValueInTo = toValues
+
+ rule.testTransition(
+ fromSceneContent = {
+ MovableFoo(
+ targetValues = fromValues,
+ onCurrentValueChanged = { lastValueInFrom = it }
+ )
+ },
+ toSceneContent = {
+ MovableFoo(targetValues = toValues, onCurrentValueChanged = { lastValueInTo = it })
+ },
+ transition = {
+ // The transition lasts 64ms = 4 frames.
+ spec = tween(durationMillis = 16 * 4, easing = LinearEasing)
+ },
+ fromScene = TestScenes.SceneA,
+ toScene = TestScenes.SceneB,
+ ) {
+ before {
+ assertThat(lastValueInFrom).isEqualTo(fromValues)
+
+ // to was not composed yet, so lastValueInTo was not set yet.
+ assertThat(lastValueInTo).isEqualTo(toValues)
+ }
+
+ at(16) {
+ // Given that we use MovableElement here, animateSharedXAsState is composed only
+ // once, in the highest scene (in this case, in toScene).
+ assertThat(lastValueInFrom).isEqualTo(fromValues)
+ assertThat(lastValueInTo).isEqualTo(lerp(fromValues, toValues, fraction = 0.25f))
+ }
+
+ at(32) {
+ assertThat(lastValueInFrom).isEqualTo(fromValues)
+ assertThat(lastValueInTo).isEqualTo(lerp(fromValues, toValues, fraction = 0.5f))
+ }
+
+ at(48) {
+ assertThat(lastValueInFrom).isEqualTo(fromValues)
+ assertThat(lastValueInTo).isEqualTo(lerp(fromValues, toValues, fraction = 0.75f))
+ }
+
+ after {
+ assertThat(lastValueInFrom).isEqualTo(fromValues)
+ assertThat(lastValueInTo).isEqualTo(toValues)
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt
new file mode 100644
index 0000000..4204cd5
--- /dev/null
+++ b/packages/SystemUI/compose/scene/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/ObservableTransitionStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt
similarity index 100%
rename from packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt
rename to packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
similarity index 98%
rename from packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
rename to packages/SystemUI/compose/scene/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/scene/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/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
similarity index 99%
rename from packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
rename to packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
index 2232370..df3b72a 100644
--- a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
+++ b/packages/SystemUI/compose/scene/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,
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/TestTransition.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TestTransition.kt
similarity index 95%
rename from packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/TestTransition.kt
rename to packages/SystemUI/compose/scene/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/scene/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/core/tests/src/com/android/compose/animation/scene/TestValues.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TestValues.kt
similarity index 94%
rename from packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/TestValues.kt
rename to packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TestValues.kt
index 8357262..b4c393e 100644
--- a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/TestValues.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TestValues.kt
@@ -37,6 +37,9 @@
/** Value keys that can be reused by tests. */
object TestValues {
val Value1 = ValueKey("Value1")
+ val Value2 = ValueKey("Value2")
+ val Value3 = ValueKey("Value3")
+ val Value4 = ValueKey("Value4")
}
// We use a transition duration of 480ms here because it is a multiple of 16, the time of a frame in
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
similarity index 100%
rename from packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
rename to packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/TransitionDslTest.kt
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/transformation/AnchoredSizeTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/AnchoredSizeTest.kt
similarity index 100%
rename from packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/transformation/AnchoredSizeTest.kt
rename to packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/AnchoredSizeTest.kt
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/transformation/AnchoredTranslateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/AnchoredTranslateTest.kt
similarity index 100%
rename from packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/transformation/AnchoredTranslateTest.kt
rename to packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/AnchoredTranslateTest.kt
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/transformation/EdgeTranslateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/EdgeTranslateTest.kt
similarity index 100%
rename from packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/transformation/EdgeTranslateTest.kt
rename to packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/EdgeTranslateTest.kt
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/transformation/ScaleSizeTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/ScaleSizeTest.kt
similarity index 100%
rename from packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/transformation/ScaleSizeTest.kt
rename to packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/ScaleSizeTest.kt
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt
similarity index 99%
rename from packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt
rename to packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt
index 2af3638..e94eff3 100644
--- a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt
@@ -32,7 +32,6 @@
import com.android.compose.animation.scene.TestScenes
import com.android.compose.animation.scene.inScene
import com.android.compose.animation.scene.testTransition
-import com.android.compose.modifiers.size
import com.android.compose.test.assertSizeIsEqualTo
import com.android.compose.test.onEach
import org.junit.Rule
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/transformation/TranslateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/TranslateTest.kt
similarity index 100%
rename from packages/SystemUI/compose/core/tests/src/com/android/compose/animation/scene/transformation/TranslateTest.kt
rename to packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/TranslateTest.kt
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt
similarity index 100%
rename from packages/SystemUI/compose/core/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt
rename to packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/nestedscroll/PriorityPostNestedScrollConnectionTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityPostNestedScrollConnectionTest.kt
similarity index 100%
rename from packages/SystemUI/compose/core/tests/src/com/android/compose/nestedscroll/PriorityPostNestedScrollConnectionTest.kt
rename to packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityPostNestedScrollConnectionTest.kt
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/test/Selectors.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/Selectors.kt
similarity index 100%
rename from packages/SystemUI/compose/core/tests/src/com/android/compose/test/Selectors.kt
rename to packages/SystemUI/compose/scene/tests/src/com/android/compose/test/Selectors.kt
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/test/SizeAssertions.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/SizeAssertions.kt
similarity index 100%
rename from packages/SystemUI/compose/core/tests/src/com/android/compose/test/SizeAssertions.kt
rename to packages/SystemUI/compose/scene/tests/src/com/android/compose/test/SizeAssertions.kt
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/test/subjects/DpOffsetSubject.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/subjects/DpOffsetSubject.kt
similarity index 100%
rename from packages/SystemUI/compose/core/tests/src/com/android/compose/test/subjects/DpOffsetSubject.kt
rename to packages/SystemUI/compose/scene/tests/src/com/android/compose/test/subjects/DpOffsetSubject.kt
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-af/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-af/strings.xml
index f8b843b..4c2c627 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-af/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Verstek vir digitaal"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-am/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-am/strings.xml
index f8b843b..847d7a5 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-am/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"ዲጂታል ነባሪ"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-ar/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-ar/strings.xml
index f8b843b..57d1612 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-ar/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"رقمية تلقائية"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-as/strings.xml b/packages/SystemUI/customization/res/values-as/strings.xml
new file mode 100644
index 0000000..2f3b64b
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-as/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"ডিজিটেল ডিফ’ল্ট"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-az/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-az/strings.xml
index f8b843b..eb52f95 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-az/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Rəqəmsal defolt"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-b+sr+Latn/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-b+sr+Latn/strings.xml
index f8b843b..90e6678 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-b+sr+Latn/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Digitalni podrazumevani"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-be/strings.xml b/packages/SystemUI/customization/res/values-be/strings.xml
new file mode 100644
index 0000000..f327da2
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-be/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"электронны, стандартны шрыфт"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-bg/strings.xml b/packages/SystemUI/customization/res/values-bg/strings.xml
new file mode 100644
index 0000000..6e3754a
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-bg/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Стандартно дигитално"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-bn/strings.xml b/packages/SystemUI/customization/res/values-bn/strings.xml
new file mode 100644
index 0000000..adf1256
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-bn/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"ডিজিটাল ডিফল্ট"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-bs/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-bs/strings.xml
index f8b843b..8de04ab 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-bs/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Digitalno zadano"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-ca/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-ca/strings.xml
index f8b843b..967bb3f 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-ca/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Digital predeterminat"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-cs/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-cs/strings.xml
index f8b843b..45da4d7 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-cs/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Digitální výchozí"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-da/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-da/strings.xml
index f8b843b..3ffaa8b 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-da/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Standard (digital)"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-de/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-de/strings.xml
index f8b843b..64f95e0 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-de/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Digital (Standard)"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-el/strings.xml b/packages/SystemUI/customization/res/values-el/strings.xml
new file mode 100644
index 0000000..57134b5
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-el/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Ψηφιακή προεπιλογή"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-en-rAU/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-en-rAU/strings.xml
index f8b843b..a6110d5 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-en-rAU/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Digital default"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-en-rCA/strings.xml
similarity index 61%
rename from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
rename to packages/SystemUI/customization/res/values-en-rCA/strings.xml
index f8b843b..79919c0 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-en-rCA/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,9 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for clock_default_description (5309401440896597541) -->
+ <skip />
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-en-rGB/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-en-rGB/strings.xml
index f8b843b..a6110d5 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-en-rGB/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Digital default"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-en-rIN/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-en-rIN/strings.xml
index f8b843b..a6110d5 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-en-rIN/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Digital default"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-en-rXC/strings.xml b/packages/SystemUI/customization/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..7c540da
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-en-rXC/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Digital default"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-es-rUS/strings.xml b/packages/SystemUI/customization/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..59be786
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-es-rUS/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Configuración predeterminada digital"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-es/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-es/strings.xml
index f8b843b..03ef0e5 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-es/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Digital predeterminada"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-et/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-et/strings.xml
index f8b843b..fec793e 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-et/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Digitaalne vaikimisi"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-eu/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-eu/strings.xml
index f8b843b..a70c808 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-eu/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Digital lehenetsia"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-fa/strings.xml b/packages/SystemUI/customization/res/values-fa/strings.xml
new file mode 100644
index 0000000..6426d51
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-fa/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"دیجیتال پیشفرض"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-fi/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-fi/strings.xml
index f8b843b..9b19373 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-fi/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Digitaalinen (oletus)"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-fr-rCA/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-fr-rCA/strings.xml
index f8b843b..bbd1208 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-fr-rCA/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Numérique, par défaut"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-fr/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-fr/strings.xml
index f8b843b..5579a42 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-fr/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Numérique par défaut"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-gl/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-gl/strings.xml
index f8b843b..2da93c6 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-gl/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Predeterminada dixital"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-gu/strings.xml b/packages/SystemUI/customization/res/values-gu/strings.xml
new file mode 100644
index 0000000..c578a2e
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-gu/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"ડિજિટલ ડિફૉલ્ટ"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-hi/strings.xml b/packages/SystemUI/customization/res/values-hi/strings.xml
new file mode 100644
index 0000000..6080f80
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-hi/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"डिजिटल डिफ़ॉल्ट"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-hr/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-hr/strings.xml
index f8b843b..0a1440f 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-hr/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Digitalni zadani"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-hu/strings.xml b/packages/SystemUI/customization/res/values-hu/strings.xml
new file mode 100644
index 0000000..32618a8
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-hu/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Digitális, alapértelmezett"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-hy/strings.xml b/packages/SystemUI/customization/res/values-hy/strings.xml
new file mode 100644
index 0000000..d45afbf
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-hy/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Թվային, կանխադրված"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-in/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-in/strings.xml
index f8b843b..a6110d5 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-in/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Digital default"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-is/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-is/strings.xml
index f8b843b..5a370d6 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-is/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Stafræn, sjálfgefið"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-it/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-it/strings.xml
index f8b843b..2a5087d 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-it/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Digitale - predefinito"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-iw/strings.xml b/packages/SystemUI/customization/res/values-iw/strings.xml
new file mode 100644
index 0000000..ddd28f2
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-iw/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"דיגיטלי ברירת מחדל"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-ja/strings.xml b/packages/SystemUI/customization/res/values-ja/strings.xml
new file mode 100644
index 0000000..744604a
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-ja/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"デジタル デフォルト"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-ka/strings.xml b/packages/SystemUI/customization/res/values-ka/strings.xml
new file mode 100644
index 0000000..88ba1df
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-ka/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"ციფრული ნაგულისხმევი"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-kk/strings.xml b/packages/SystemUI/customization/res/values-kk/strings.xml
new file mode 100644
index 0000000..9ee6522
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-kk/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Цифрлық әдепкі"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-km/strings.xml b/packages/SystemUI/customization/res/values-km/strings.xml
new file mode 100644
index 0000000..bbc438a
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-km/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"លំនាំដើមឌីជីថល"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-kn/strings.xml b/packages/SystemUI/customization/res/values-kn/strings.xml
new file mode 100644
index 0000000..e67319d
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-kn/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"ಡಿಜಿಟಲ್ ಡೀಫಾಲ್ಟ್"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-ko/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-ko/strings.xml
index f8b843b..fa9103b 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-ko/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"디지털 기본"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-ky/strings.xml b/packages/SystemUI/customization/res/values-ky/strings.xml
new file mode 100644
index 0000000..76cc5e2
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-ky/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Демейки санариптик"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-lo/strings.xml b/packages/SystemUI/customization/res/values-lo/strings.xml
new file mode 100644
index 0000000..28f5000
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-lo/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"ດິຈິຕອນຕາມຄ່າເລີ່ມຕົ້ນ"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-lt/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-lt/strings.xml
index f8b843b..2fe731547 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-lt/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Skaitmeninis numatytasis"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-lv/strings.xml b/packages/SystemUI/customization/res/values-lv/strings.xml
new file mode 100644
index 0000000..e0b904a
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-lv/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Digitālais pulkstenis — noklusējums"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-mk/strings.xml b/packages/SystemUI/customization/res/values-mk/strings.xml
new file mode 100644
index 0000000..9b95a6e
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-mk/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Дигитален стандарден приказ"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-ml/strings.xml b/packages/SystemUI/customization/res/values-ml/strings.xml
new file mode 100644
index 0000000..7f6be8a
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-ml/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"ഡിജിറ്റൽ ഡിഫോൾട്ട്"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-mn/strings.xml b/packages/SystemUI/customization/res/values-mn/strings.xml
new file mode 100644
index 0000000..38369b6
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-mn/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Дижитал өгөгдмөл"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-mr/strings.xml b/packages/SystemUI/customization/res/values-mr/strings.xml
new file mode 100644
index 0000000..821ff10
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-mr/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"डिजिटल डीफॉल्टसह क्लॉक फेस"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-ms/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-ms/strings.xml
index f8b843b..2f61b47 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-ms/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Digital lalai"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-my/strings.xml b/packages/SystemUI/customization/res/values-my/strings.xml
new file mode 100644
index 0000000..3d137eb
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-my/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"ဒစ်ဂျစ်တယ်နာရီ မူရင်း"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-nb/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-nb/strings.xml
index f8b843b..6eb4373 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-nb/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Digital – standard"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-ne/strings.xml b/packages/SystemUI/customization/res/values-ne/strings.xml
new file mode 100644
index 0000000..c5b0877
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-ne/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"डिजिटल डिफल्ट"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-nl/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-nl/strings.xml
index f8b843b..4f46ab8 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-nl/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Standaard digitaal"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-or/strings.xml b/packages/SystemUI/customization/res/values-or/strings.xml
new file mode 100644
index 0000000..a74017f
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-or/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"ଡିଜିଟାଲ ଡିଫଲ୍ଟ"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-pa/strings.xml b/packages/SystemUI/customization/res/values-pa/strings.xml
new file mode 100644
index 0000000..a77661a
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-pa/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"ਡਿਜੀਟਲ ਡਿਫਾਲਟ"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-pl/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-pl/strings.xml
index f8b843b..6f5b6f2 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-pl/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Cyfrowa domyślna"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-pt-rPT/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-pt-rPT/strings.xml
index f8b843b..c6c3cc0 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-pt-rPT/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Predefinição digital"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-pt/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-pt/strings.xml
index f8b843b..bbe4355 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-pt/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Digital padrão"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-ro/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-ro/strings.xml
index f8b843b..ef163e9 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-ro/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Implicit digital"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-ru/strings.xml b/packages/SystemUI/customization/res/values-ru/strings.xml
new file mode 100644
index 0000000..5ee928e
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-ru/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Цифровые часы, стандартный"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-si/strings.xml b/packages/SystemUI/customization/res/values-si/strings.xml
new file mode 100644
index 0000000..caf9610
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-si/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"ඩිජිටල් පෙරනිමිය"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-sk/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-sk/strings.xml
index f8b843b..1843f97 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-sk/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Digitálne predvolené"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-sl/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-sl/strings.xml
index f8b843b..12df66f 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-sl/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Digitalna (privzeta)"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-sq/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-sq/strings.xml
index f8b843b..1fc9f25 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-sq/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Dixhitale e parazgjedhur"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-sr/strings.xml b/packages/SystemUI/customization/res/values-sr/strings.xml
new file mode 100644
index 0000000..6b127c9
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-sr/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Дигитални подразумевани"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-sv/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-sv/strings.xml
index f8b843b..84ad25c 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-sv/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Digital standard"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-sw/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-sw/strings.xml
index f8b843b..e2ec3de 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-sw/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Dijitali chaguomsingi"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-sw400dp/dimens.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-sw400dp/dimens.xml
index f8b843b..3c9e078 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-sw400dp/dimens.xml
@@ -1,5 +1,6 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -12,11 +13,8 @@
* 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";
+*/
+-->
+<resources>
+ <dimen name="presentation_clock_text_size">170dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-ta/strings.xml b/packages/SystemUI/customization/res/values-ta/strings.xml
new file mode 100644
index 0000000..f4eea07
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-ta/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"டிஜிட்டல் இயல்பு"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-te/strings.xml b/packages/SystemUI/customization/res/values-te/strings.xml
new file mode 100644
index 0000000..c7c77d5
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-te/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"డిజిటల్ ఆటోమేటిక్ సెట్టింగ్"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-th/strings.xml b/packages/SystemUI/customization/res/values-th/strings.xml
new file mode 100644
index 0000000..61d880e
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-th/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"ดิจิทัลแบบเริ่มต้น"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-tl/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-tl/strings.xml
index f8b843b..a3484a7 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-tl/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Digital na default"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-tr/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-tr/strings.xml
index f8b843b..a90e985 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-tr/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Dijital varsayılan"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-uk/strings.xml b/packages/SystemUI/customization/res/values-uk/strings.xml
new file mode 100644
index 0000000..ee9b77b
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-uk/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Цифровий, стандартний"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-ur/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-ur/strings.xml
index f8b843b..06a6a7c 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-ur/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"ڈیجیٹل ڈیفالٹ"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-uz/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-uz/strings.xml
index f8b843b..6b31b04 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-uz/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Raqamli soat, standart"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-vi/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-vi/strings.xml
index f8b843b..830b6e2 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-vi/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Mặt số mặc định"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-zh-rCN/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-zh-rCN/strings.xml
index f8b843b..747567e 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-zh-rCN/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"默认数字"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-zh-rHK/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-zh-rHK/strings.xml
index f8b843b..c19cc68 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-zh-rHK/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"數碼時鐘 (預設)"</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl b/packages/SystemUI/customization/res/values-zh-rTW/strings.xml
similarity index 60%
copy from core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
copy to packages/SystemUI/customization/res/values-zh-rTW/strings.xml
index f8b843b..6fcd313 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.aidl
+++ b/packages/SystemUI/customization/res/values-zh-rTW/strings.xml
@@ -1,5 +1,7 @@
-/*
- * Copyright (c) 2016, The Android Open Source Project
+<?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.
@@ -13,10 +15,8 @@
* 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";
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"數位預設"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values-zu/strings.xml b/packages/SystemUI/customization/res/values-zu/strings.xml
new file mode 100644
index 0000000..c87c250
--- /dev/null
+++ b/packages/SystemUI/customization/res/values-zu/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ */
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="clock_default_description" msgid="5309401440896597541">"Okuzenzakalelayo kwedijithali"</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/res/values/dimens.xml b/packages/SystemUI/customization/res/values/dimens.xml
index 8eb8132..c574d1f 100644
--- a/packages/SystemUI/customization/res/values/dimens.xml
+++ b/packages/SystemUI/customization/res/values/dimens.xml
@@ -17,6 +17,7 @@
-->
<resources>
<!-- Clock maximum font size (dp is intentional, to prevent any further scaling) -->
+ <dimen name="presentation_clock_text_size">50dp</dimen>
<dimen name="large_clock_text_size">150dp</dimen>
<dimen name="small_clock_text_size">86dp</dimen>
diff --git a/packages/SystemUI/customization/res/values/strings.xml b/packages/SystemUI/customization/res/values/strings.xml
new file mode 100644
index 0000000..897c842
--- /dev/null
+++ b/packages/SystemUI/customization/res/values/strings.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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- default clock face name [CHAR LIMIT=NONE]-->
+ <string name="clock_default_name">Default</string>
+
+ <!-- default clock face description [CHAR LIMIT=NONE]-->
+ <string name="clock_default_description">Digital default</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index b28920c..b076b2c 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -65,7 +65,13 @@
protected var onSecondaryDisplay: Boolean = false
override val events: DefaultClockEvents
- override val config = ClockConfig(DEFAULT_CLOCK_ID)
+ override val config: ClockConfig by lazy {
+ ClockConfig(
+ DEFAULT_CLOCK_ID,
+ resources.getString(R.string.clock_default_name),
+ resources.getString(R.string.clock_default_description)
+ )
+ }
init {
val parent = FrameLayout(ctx)
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
index 949641a..dd52e39 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
@@ -25,7 +25,6 @@
import com.android.systemui.plugins.ClockSettings
private val TAG = DefaultClockProvider::class.simpleName
-const val DEFAULT_CLOCK_NAME = "Default Clock"
const val DEFAULT_CLOCK_ID = "DEFAULT"
/** Provides the default system clock */
@@ -35,8 +34,7 @@
val resources: Resources,
val hasStepClockAnimation: Boolean = false
) : ClockProvider {
- override fun getClocks(): List<ClockMetadata> =
- listOf(ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME))
+ override fun getClocks(): List<ClockMetadata> = listOf(ClockMetadata(DEFAULT_CLOCK_ID))
override fun createClock(settings: ClockSettings): ClockController {
if (settings.clockId != DEFAULT_CLOCK_ID) {
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
index e2f4793..485c27e 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
@@ -192,15 +192,18 @@
/** Some data about a clock design */
data class ClockMetadata(
val clockId: ClockId,
- val name: String,
-) {
- constructor(clockId: ClockId) : this(clockId, clockId) {}
-}
+)
/** Render configuration for the full clock. Modifies the way systemUI behaves with this clock. */
data class ClockConfig(
val id: String,
+ /** Localized name of the clock */
+ val name: String,
+
+ /** Localized accessibility description for the clock */
+ val description: String,
+
/** Transition to AOD should move smartspace like large clock instead of small clock */
val useAlternateSmartspaceAODTransition: Boolean = false,
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/DarkIconDispatcher.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/DarkIconDispatcher.java
index a5e5aaa..a9d2ee3 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/DarkIconDispatcher.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/DarkIconDispatcher.java
@@ -71,7 +71,11 @@
*/
void applyDark(DarkReceiver object);
+ /** The default tint (applicable for dark backgrounds) is white */
int DEFAULT_ICON_TINT = Color.WHITE;
+ /** To support an icon which wants to create contrast, the default tint is black-on-white. */
+ int DEFAULT_INVERSE_ICON_TINT = Color.BLACK;
+
Rect sTmpRect = new Rect();
int[] sTmpInt2 = new int[2];
@@ -88,6 +92,18 @@
}
/**
+ * @return the tint to apply to a foreground, given that the background is tinted
+ * per {@link #getTint}
+ */
+ static int getInverseTint(Collection<Rect> tintAreas, View view, int inverseColor) {
+ if (isInAreas(tintAreas, view)) {
+ return inverseColor;
+ } else {
+ return DEFAULT_INVERSE_ICON_TINT;
+ }
+ }
+
+ /**
* @return true if more than half of the view area are in any of the given
* areas, false otherwise
*/
@@ -129,7 +145,40 @@
*/
@ProvidesInterface(version = DarkReceiver.VERSION)
interface DarkReceiver {
- int VERSION = 2;
+ int VERSION = 3;
+
+ /**
+ * @param areas list of regions on screen where the tint applies
+ * @param darkIntensity float representing the level of tint. In the range [0,1]
+ * @param tint the tint applicable as a foreground contrast to the dark regions. This value
+ * is interpolated between a default light and dark tone, and is therefore
+ * usable as-is, as long as the view is in one of the areas defined in
+ * {@code areas}.
+ *
+ * @see DarkIconDispatcher#isInArea(Rect, View) for utilizing {@code areas}
+ *
+ * Note: only one of {@link #onDarkChanged(ArrayList, float, int)} or
+ * {@link #onDarkChangedWithContrast(ArrayList, int, int)} need to be implemented, as both
+ * will be called in the same circumstances.
+ */
void onDarkChanged(ArrayList<Rect> areas, float darkIntensity, int tint);
+
+ /**
+ * New version of onDarkChanged, which describes a tint plus an optional contrastTint
+ * that can be used if the tint is applied to the background of an icon.
+ *
+ * We use the 2 here to avoid the case where an existing override of onDarkChanged
+ * might pass in parameters as bare numbers (e.g. 0 instead of 0f) which might get
+ * mistakenly cast to (int) and therefore trigger this method.
+ *
+ * @param areas list of areas where dark tint applies
+ * @param tint int describing the tint color to use
+ * @param contrastTint if desired, a contrasting color that can be used for a foreground
+ *
+ * Note: only one of {@link #onDarkChanged(ArrayList, float, int)} or
+ * {@link #onDarkChangedWithContrast(ArrayList, int, int)} need to be implemented, as both
+ * will be called in the same circumstances.
+ */
+ default void onDarkChangedWithContrast(ArrayList<Rect> areas, int tint, int contrastTint) {}
}
}
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-keyguard/layout/alternate_bouncer.xml b/packages/SystemUI/res-keyguard/layout/alternate_bouncer.xml
new file mode 100644
index 0000000..2187352
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/alternate_bouncer.xml
@@ -0,0 +1,34 @@
+<?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.
+ ~
+ -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:sysui="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/alternate_bouncer"
+ android:focusable="true"
+ android:clickable="true"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <com.android.systemui.scrim.ScrimView
+ android:id="@+id/alternate_bouncer_scrim"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:importantForAccessibility="no"
+ sysui:ignoreRightInset="true"
+ />
+</FrameLayout>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_presentation.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_presentation.xml
index 593f507f..f68ab81 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_presentation.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_presentation.xml
@@ -16,22 +16,30 @@
*/
-->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<androidx.constraintlayout.widget.ConstraintLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/presentation"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.android.keyguard.KeyguardStatusView
android:id="@+id/clock"
- android:layout_width="410dp"
- android:layout_height="wrap_content"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
android:layout_gravity="center"
- android:orientation="vertical">
+ android:orientation="vertical"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintDimensionRatio="1:1"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent">
+
<include
android:id="@+id/keyguard_clock_container"
layout="@layout/keyguard_clock_switch"
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="wrap_content"/>
</com.android.keyguard.KeyguardStatusView>
-</FrameLayout>
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/status_bar_mobile_signal_group_inner.xml b/packages/SystemUI/res-keyguard/layout/status_bar_mobile_signal_group_inner.xml
index a1e2dc3..a8017f6 100644
--- a/packages/SystemUI/res-keyguard/layout/status_bar_mobile_signal_group_inner.xml
+++ b/packages/SystemUI/res-keyguard/layout/status_bar_mobile_signal_group_inner.xml
@@ -52,15 +52,22 @@
android:visibility="gone"
/>
</FrameLayout>
- <ImageView
- android:id="@+id/mobile_type"
- android:layout_height="@dimen/status_bar_mobile_type_size"
+ <FrameLayout
+ android:id="@+id/mobile_type_container"
+ android:layout_height="@dimen/status_bar_mobile_container_height"
android:layout_width="wrap_content"
- android:layout_gravity="center_vertical"
- android:adjustViewBounds="true"
- android:paddingStart="2.5sp"
- android:paddingEnd="1sp"
- android:visibility="gone" />
+ android:layout_marginStart="2.5sp"
+ android:layout_marginEnd="1sp"
+ android:visibility="gone"
+ >
+ <ImageView
+ android:id="@+id/mobile_type"
+ android:layout_height="@dimen/status_bar_mobile_type_size"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:adjustViewBounds="true"
+ />
+ </FrameLayout>
<Space
android:id="@+id/mobile_roaming_space"
android:layout_height="match_parent"
diff --git a/packages/SystemUI/res-keyguard/values-bn/strings.xml b/packages/SystemUI/res-keyguard/values-bn/strings.xml
index 69f533c..67b4e4b 100644
--- a/packages/SystemUI/res-keyguard/values-bn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bn/strings.xml
@@ -125,7 +125,7 @@
<string name="clock_title_bubble" msgid="2204559396790593213">"বাবল"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"অ্যানালগ"</string>
<string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"চালিয়ে যেতে আপনার ডিভাইস আনলক করুন"</string>
- <string name="kg_prompt_unattended_update_pin" msgid="5979434876768801873">"পরে ইনস্টল আপডেট করতে পিন লিখুন"</string>
+ <string name="kg_prompt_unattended_update_pin" msgid="5979434876768801873">"পরে আপডেট ইনস্টল করতে পিন লিখুন"</string>
<string name="kg_prompt_unattended_update_password" msgid="8805664437604967210">"পরে আপডেট ইনস্টল করতে পাসওয়ার্ড লিখুন"</string>
<string name="kg_prompt_unattended_update_pattern" msgid="8580479377489546091">"পরে আপডেট ইনস্টল করতে প্যাটার্ন আঁকুন"</string>
<string name="kg_prompt_after_update_pin" msgid="7051709651908643013">"ডিভাইস আপডেট করা হয়েছে। চালিয়ে যেতে পিন লিখুন।"</string>
diff --git a/packages/SystemUI/res-keyguard/values-cs/strings.xml b/packages/SystemUI/res-keyguard/values-cs/strings.xml
index e075d85..573638b 100644
--- a/packages/SystemUI/res-keyguard/values-cs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-cs/strings.xml
@@ -125,9 +125,9 @@
<string name="clock_title_bubble" msgid="2204559396790593213">"Bublina"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analogové"</string>
<string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Pokud chcete pokračovat, odemkněte zařízení"</string>
- <string name="kg_prompt_unattended_update_pin" msgid="5979434876768801873">"Pokud aktualizaci chcete nainstalovat později, zadejte PIN"</string>
- <string name="kg_prompt_unattended_update_password" msgid="8805664437604967210">"Pokud aktualizaci chcete nainstalovat později, zadejte heslo"</string>
- <string name="kg_prompt_unattended_update_pattern" msgid="8580479377489546091">"Pokud aktualizaci chcete nainstalovat později, zadejte gesto"</string>
+ <string name="kg_prompt_unattended_update_pin" msgid="5979434876768801873">"Zadejte PIN a aktualizaci nainstalujte později"</string>
+ <string name="kg_prompt_unattended_update_password" msgid="8805664437604967210">"Zadejte heslo a aktualizaci nainstalujte později"</string>
+ <string name="kg_prompt_unattended_update_pattern" msgid="8580479377489546091">"Zadejte gesto a aktualizaci nainstalujte později"</string>
<string name="kg_prompt_after_update_pin" msgid="7051709651908643013">"Zařízení bylo aktualizováno. Pokud chcete pokračovat, zadejte PIN."</string>
<string name="kg_prompt_after_update_password" msgid="153703052501352094">"Zařízení bylo aktualizováno. Pokud chcete pokračovat, zadejte heslo."</string>
<string name="kg_prompt_after_update_pattern" msgid="1484084551298241992">"Zařízení bylo aktualizováno. Pokud chcete pokračovat, zadejte gesto."</string>
diff --git a/packages/SystemUI/res-keyguard/values-de/strings.xml b/packages/SystemUI/res-keyguard/values-de/strings.xml
index 117f7a9..5c5f264 100644
--- a/packages/SystemUI/res-keyguard/values-de/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-de/strings.xml
@@ -125,9 +125,9 @@
<string name="clock_title_bubble" msgid="2204559396790593213">"Bubble"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analog"</string>
<string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Gerät entsperren, um fortzufahren"</string>
- <string name="kg_prompt_unattended_update_pin" msgid="5979434876768801873">"Gib deine PIN ein, um das Update später zu installieren"</string>
- <string name="kg_prompt_unattended_update_password" msgid="8805664437604967210">"Gib dein Passwort ein, um das Update später zu installieren"</string>
- <string name="kg_prompt_unattended_update_pattern" msgid="8580479377489546091">"Zeichne dein Muster, um das Update später zu installieren"</string>
+ <string name="kg_prompt_unattended_update_pin" msgid="5979434876768801873">"PIN eingeben, um Update später zu installieren"</string>
+ <string name="kg_prompt_unattended_update_password" msgid="8805664437604967210">"Passwort eingeben, um Update später zu installieren"</string>
+ <string name="kg_prompt_unattended_update_pattern" msgid="8580479377489546091">"Muster zeichnen, um Update später zu installieren"</string>
<string name="kg_prompt_after_update_pin" msgid="7051709651908643013">"Gerät aktualisiert. Gib deine PIN ein, um fortzufahren."</string>
<string name="kg_prompt_after_update_password" msgid="153703052501352094">"Gerät aktualisiert. Gib dein Passwort ein, um fortzufahren."</string>
<string name="kg_prompt_after_update_pattern" msgid="1484084551298241992">"Gerät aktualisiert. Zeichne dein Muster, um fortzufahren."</string>
diff --git a/packages/SystemUI/res-keyguard/values-el/strings.xml b/packages/SystemUI/res-keyguard/values-el/strings.xml
index cd7637c..3a01da5 100644
--- a/packages/SystemUI/res-keyguard/values-el/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-el/strings.xml
@@ -125,9 +125,9 @@
<string name="clock_title_bubble" msgid="2204559396790593213">"Συννεφάκι"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Αναλογικό"</string>
<string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Ξεκλειδώστε τη συσκευή σας για να συνεχίσετε"</string>
- <string name="kg_prompt_unattended_update_pin" msgid="5979434876768801873">"Εισαγάγετε το PIN για να εγκαταστήσετε την ενημέρωση αργότερα"</string>
+ <string name="kg_prompt_unattended_update_pin" msgid="5979434876768801873">"Εισαγωγή PIN για εγκατάσταση ενημέρωσης αργότερα"</string>
<string name="kg_prompt_unattended_update_password" msgid="8805664437604967210">"Εισαγ. τον κωδ. πρόσβασης για να εγκαταστήσετε την ενημέρωση αργότερα"</string>
- <string name="kg_prompt_unattended_update_pattern" msgid="8580479377489546091">"Σχεδιάστε το μοτίβο για να εγκαταστήσετε την ενημέρωση αργότερα"</string>
+ <string name="kg_prompt_unattended_update_pattern" msgid="8580479377489546091">"Σχεδιάστε το μοτίβο για εγκατάσταση της ενημέρωσης αργότερα"</string>
<string name="kg_prompt_after_update_pin" msgid="7051709651908643013">"Η συσκευή ενημερώθηκε. Εισαγάγετε το PIN για να συνεχίσετε."</string>
<string name="kg_prompt_after_update_password" msgid="153703052501352094">"Η συσκευή ενημερώθηκε. Εισαγάγ. τον κωδ. πρόσβασης για να συνεχίσετε."</string>
<string name="kg_prompt_after_update_pattern" msgid="1484084551298241992">"Η συσκευή ενημερώθηκε. Σχεδιάστε το μοτίβο για να συνεχίσετε."</string>
diff --git a/packages/SystemUI/res-keyguard/values-fa/strings.xml b/packages/SystemUI/res-keyguard/values-fa/strings.xml
index 4815815..ae3f04a 100644
--- a/packages/SystemUI/res-keyguard/values-fa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fa/strings.xml
@@ -125,9 +125,9 @@
<string name="clock_title_bubble" msgid="2204559396790593213">"حباب"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"آنالوگ"</string>
<string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"برای ادامه، قفل دستگاهتان را باز کنید"</string>
- <string name="kg_prompt_unattended_update_pin" msgid="5979434876768801873">"برای نصب بهروزرسانی در فرصتی دیگر، پین را وارد کنید"</string>
- <string name="kg_prompt_unattended_update_password" msgid="8805664437604967210">"برای نصب بهروزرسانی در فرصتی دیگر، گذرواژه را وارد کنید"</string>
- <string name="kg_prompt_unattended_update_pattern" msgid="8580479377489546091">"برای نصب بهروزرسانی در فرصتی دیگر، الگو را وارد کنید"</string>
+ <string name="kg_prompt_unattended_update_pin" msgid="5979434876768801873">"پین را وارد کنید و بهروزرسانی را در فرصتی دیگر انجام دهید"</string>
+ <string name="kg_prompt_unattended_update_password" msgid="8805664437604967210">"گذرواژه را وارد کنید و بهروزرسانی را در فرصتی دیگر انجام دهید"</string>
+ <string name="kg_prompt_unattended_update_pattern" msgid="8580479377489546091">"الگو را وارد کنید و بهروزرسانی را در فرصتی دیگر انجام دهید"</string>
<string name="kg_prompt_after_update_pin" msgid="7051709651908643013">"دستگاه بهروز شد. برای ادامه، پین را وارد کنید."</string>
<string name="kg_prompt_after_update_password" msgid="153703052501352094">"دستگاه بهروز شد. برای ادامه، گذرواژه را وارد کنید."</string>
<string name="kg_prompt_after_update_pattern" msgid="1484084551298241992">"دستگاه بهروز شد. برای ادامه، الگو را وارد کنید."</string>
diff --git a/packages/SystemUI/res-keyguard/values-fi/strings.xml b/packages/SystemUI/res-keyguard/values-fi/strings.xml
index 02d41d8..050df99 100644
--- a/packages/SystemUI/res-keyguard/values-fi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fi/strings.xml
@@ -127,7 +127,7 @@
<string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Jatka avaamalla laitteen lukitus"</string>
<string name="kg_prompt_unattended_update_pin" msgid="5979434876768801873">"Jos haluat asentaa päivityksen myöhemmin, lisää PIN-koodi"</string>
<string name="kg_prompt_unattended_update_password" msgid="8805664437604967210">"Jos haluat asentaa päivityksen myöhemmin, lisää salasana"</string>
- <string name="kg_prompt_unattended_update_pattern" msgid="8580479377489546091">"Jos haluat asentaa päivityksen myöhemmin, piirrä kuvio"</string>
+ <string name="kg_prompt_unattended_update_pattern" msgid="8580479377489546091">"Salli päivitys myöhemmin piirtämällä kuvio"</string>
<string name="kg_prompt_after_update_pin" msgid="7051709651908643013">"Laite päivitetty. Jatka lisäämällä PIN-koodi."</string>
<string name="kg_prompt_after_update_password" msgid="153703052501352094">"Laite päivitetty. Jatka lisäämällä salasana."</string>
<string name="kg_prompt_after_update_pattern" msgid="1484084551298241992">"Laite päivitetty. Jatka piirtämällä kuvio."</string>
diff --git a/packages/SystemUI/res-keyguard/values-ro/strings.xml b/packages/SystemUI/res-keyguard/values-ro/strings.xml
index e5be788..4309b56 100644
--- a/packages/SystemUI/res-keyguard/values-ro/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ro/strings.xml
@@ -127,7 +127,7 @@
<string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"Deblochează dispozitivul pentru a continua"</string>
<string name="kg_prompt_unattended_update_pin" msgid="5979434876768801873">"Introdu codul PIN pentru a instala actualizarea mai târziu"</string>
<string name="kg_prompt_unattended_update_password" msgid="8805664437604967210">"Introdu parola pentru a instala actualizarea mai târziu"</string>
- <string name="kg_prompt_unattended_update_pattern" msgid="8580479377489546091">"Desenează modelul pentru a instala actualizarea mai târziu"</string>
+ <string name="kg_prompt_unattended_update_pattern" msgid="8580479377489546091">"Desenează pentru a instala actualizarea mai târziu"</string>
<string name="kg_prompt_after_update_pin" msgid="7051709651908643013">"Dispozitivul s-a actualizat. Introdu codul PIN pentru a continua."</string>
<string name="kg_prompt_after_update_password" msgid="153703052501352094">"Dispozitivul s-a actualizat. Introdu parola pentru a continua."</string>
<string name="kg_prompt_after_update_pattern" msgid="1484084551298241992">"Dispozitivul s-a actualizat. Desenează modelul pentru a continua."</string>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
index 59261a3..4c65832 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
@@ -125,9 +125,9 @@
<string name="clock_title_bubble" msgid="2204559396790593213">"泡泡"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"指针"</string>
<string name="keyguard_unlock_to_continue" msgid="7509503484250597743">"解锁设备才能继续操作"</string>
- <string name="kg_prompt_unattended_update_pin" msgid="5979434876768801873">"需要输入 PIN 码才能稍后安装更新"</string>
- <string name="kg_prompt_unattended_update_password" msgid="8805664437604967210">"需要输入密码才能稍后安装更新"</string>
- <string name="kg_prompt_unattended_update_pattern" msgid="8580479377489546091">"需要绘制解锁图案才能稍后安装更新"</string>
+ <string name="kg_prompt_unattended_update_pin" msgid="5979434876768801873">"请输入 PIN 码,系统稍后会安装更新"</string>
+ <string name="kg_prompt_unattended_update_password" msgid="8805664437604967210">"请输入密码,系统稍后会安装更新"</string>
+ <string name="kg_prompt_unattended_update_pattern" msgid="8580479377489546091">"请绘制解锁图案,系统稍后会安装更新"</string>
<string name="kg_prompt_after_update_pin" msgid="7051709651908643013">"设备已更新。您需要输入 PIN 码才能继续。"</string>
<string name="kg_prompt_after_update_password" msgid="153703052501352094">"设备已更新。您需要输入密码才能继续。"</string>
<string name="kg_prompt_after_update_pattern" msgid="1484084551298241992">"设备已更新。您需要绘制解锁图案才能继续。"</string>
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/mobile_network_type_background.xml b/packages/SystemUI/res/drawable/mobile_network_type_background.xml
new file mode 100644
index 0000000..db25c89
--- /dev/null
+++ b/packages/SystemUI/res/drawable/mobile_network_type_background.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ ~
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle"
+ >
+ <corners
+ android:topLeftRadius="0dp"
+ android:topRightRadius="@dimen/status_bar_mobile_container_corner_radius"
+ android:bottomRightRadius="0dp"
+ android:bottomLeftRadius="@dimen/status_bar_mobile_container_corner_radius"/>
+ <solid android:color="#FFF" />
+ <padding
+ android:left="2sp"
+ android:right="2sp"/>
+</shape>
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/layout/connected_display_dialog.xml b/packages/SystemUI/res/layout/connected_display_dialog.xml
index a51c55e..8cfcb68 100644
--- a/packages/SystemUI/res/layout/connected_display_dialog.xml
+++ b/packages/SystemUI/res/layout/connected_display_dialog.xml
@@ -15,8 +15,9 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:id="@+id/cd_bottom_sheet"
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingHorizontal="@dimen/dialog_side_padding"
@@ -26,11 +27,14 @@
<ImageView
android:id="@+id/connected_display_dialog_icon"
- android:layout_width="@dimen/screenrecord_logo_size"
- android:layout_height="@dimen/screenrecord_logo_size"
+ android:layout_width="@dimen/connected_display_dialog_logo_size"
+ android:layout_height="@dimen/connected_display_dialog_logo_size"
+ android:background="@drawable/circular_background"
+ android:backgroundTint="?androidprv:attr/materialColorPrimary"
android:importantForAccessibility="no"
+ android:padding="6dp"
android:src="@drawable/stat_sys_connected_display"
- android:tint="?androidprv:attr/materialColorPrimary" />
+ android:tint="?androidprv:attr/materialColorOnPrimary" />
<TextView
android:id="@+id/connected_display_dialog_title"
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
index ec006c5..16eba22 100644
--- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -403,7 +403,7 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="start|center_vertical"
- android:orientation="vertical">
+ android:orientation="horizontal">
<Button
android:id="@+id/apm_button"
android:layout_width="wrap_content"
@@ -414,12 +414,7 @@
style="@style/Widget.Dialog.Button.BorderButton"
android:clickable="true"
android:focusable="true"/>
- </LinearLayout>
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="center_vertical">
<Button
android:id="@+id/share_wifi_button"
android:layout_width="wrap_content"
@@ -430,8 +425,14 @@
android:ellipsize="end"
android:clickable="true"
android:focusable="true"
- android:layout_alignParentLeft="true"
android:visibility="gone"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dp"
+ android:layout_gravity="end|center_vertical">
<Button
android:id="@+id/done_button"
android:layout_width="wrap_content"
@@ -441,9 +442,8 @@
android:maxLines="1"
android:ellipsize="end"
android:clickable="true"
- android:focusable="true"
- android:layout_alignParentRight="true"/>
- </RelativeLayout>
+ android:focusable="true"/>
+ </LinearLayout>
</LinearLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_footer.xml b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
index b00908f..c1bac31 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_footer.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
@@ -15,7 +15,7 @@
-->
<!-- Extends Framelayout -->
-<com.android.systemui.statusbar.notification.row.FooterView
+<com.android.systemui.statusbar.notification.footer.ui.view.FooterView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
@@ -76,4 +76,4 @@
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
-</com.android.systemui.statusbar.notification.row.FooterView>
+</com.android.systemui.statusbar.notification.footer.ui.view.FooterView>
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index d4b73a4..acee425 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -130,6 +130,11 @@
android:inflatedId="@+id/multi_shade"
android:layout="@layout/multi_shade" />
+ <include layout="@layout/alternate_bouncer"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="invisible" />
+
<com.android.systemui.biometrics.AuthRippleView
android:id="@+id/auth_ripple"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 937e97a..24846d9 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Stel versteknotasapp in Instellings"</string>
<string name="install_app" msgid="5066668100199613936">"Installeer app"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Sinkroniseer wedersyds na eksterne skerm?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Sinkroniseer skerm wedersyds"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Maak toe"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofoon en kamera"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Onlangse appgebruik"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Sien onlangse toegang"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index de2fda4..23def28 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"በቅንብሮች ውስጥ ነባሪ የማስታወሻዎች መተግበሪያን ያቀናብሩ"</string>
<string name="install_app" msgid="5066668100199613936">"መተግበሪያን ጫን"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ወደ ውጫዊ ማሳያ ይንጸባረቅ?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"ማሳያን አንጸባርቅ"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"አሰናብት"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"ማይክሮፎን እና ካሜራ"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"የቅርብ ጊዜ የመተግበሪያ አጠቃቀም"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"የቅርብ ጊዜ መዳረሻን አሳይ"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 79b671c..80d63a2 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"يمكنك ضبط تطبيق تدوين الملاحظات التلقائي في \"الإعدادات\"."</string>
<string name="install_app" msgid="5066668100199613936">"تثبيت التطبيق"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"هل تريد بث محتوى جهازك على الشاشة الخارجية؟"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"بث المحتوى على الشاشة"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"إغلاق"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"الميكروفون والكاميرا"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"آخر استخدام في التطبيقات"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"عرض آخر استخدام في التطبيقات"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index ac30f18..c845773 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ছেটিঙত টোকাৰ ডিফ’ল্ট এপ্ ছেট কৰক"</string>
<string name="install_app" msgid="5066668100199613936">"এপ্টো ইনষ্টল কৰক"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"বাহ্যিক ডিছপ্লে’লৈ মিৰ’ৰ কৰিবনে?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"ডিছপ্লে’ মিৰ’ৰ কৰক"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"অগ্ৰাহ্য কৰক"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"মাইক্ৰ’ফ’ন আৰু কেমেৰা"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"শেহতীয়া এপৰ ব্যৱহাৰ"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"শেহতীয়া এক্সেছ চাওক"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 3ca61dc..5aa26ba 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Ayarlarda defolt qeydlər tətbiqi ayarlayın"</string>
<string name="install_app" msgid="5066668100199613936">"Tətbiqi quraşdırın"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Xarici displeyə əks etdirilsin?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Displeyi əks etdirin"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"İmtina edin"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon və kamera"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Son tətbiq istifadəsi"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Son girişə baxın"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index b51d4b3..cd36884 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Podesite podrazumevanu aplikaciju za beleške u Podešavanjima"</string>
<string name="install_app" msgid="5066668100199613936">"Instaliraj aplikaciju"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Želite li da preslikate na spoljnji ekran?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Preslikaj ekran"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Odbaci"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon i kamera"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Nedavno koristila aplikacija"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Prikaži nedavni pristup"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 35a84b0..1b03c8b 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Задайце ў Наладах стандартную праграму для нататак"</string>
<string name="install_app" msgid="5066668100199613936">"Усталяваць праграму"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Адлюстраваць на знешнім дысплеі?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Адлюстраваць дысплэй"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Закрыць"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Мікрафон і камера"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Нядаўна выкарыстоўваліся праграмамі"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Паглядзець нядаўні доступ"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 6b212e5..4ed1ad9 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Задайте стандартно приложение за бележки от настройките"</string>
<string name="install_app" msgid="5066668100199613936">"Инсталиране на приложението"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Да се дублира ли на външния екран?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Огледално копиране на дисплея"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Отхвърляне"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Микрофон и камера"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Скорошно използване на приложението"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Вижте скорошния достъп"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 5b5ac734..426d38d 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"\'সেটিংস\' থেকে ডিফল্ট নোট নেওয়ার অ্যাপ সেট করুন"</string>
<string name="install_app" msgid="5066668100199613936">"অ্যাপ ইনস্টল করুন"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"এক্সটার্নাল ডিসপ্লে আয়না?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"ডিসপ্লে দেখান"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"বাতিল করুন"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"মাইক্রোফোন ও ক্যামেরা"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"সম্প্রতি ব্যবহার করা অ্যাপ"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"সাম্প্রতিক অ্যাক্সেস দেখুন"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 33330b7..4eed7b89 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Postavite zadanu aplikaciju za bilješke u Postavkama"</string>
<string name="install_app" msgid="5066668100199613936">"Instaliraj aplikaciju"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Preslikati na vanjski ekran?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Preslikaj ekran"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Odbaci"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon i kamera"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Nedavno korištenje aplikacije"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Prikaži nedavni pristup"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 45bb342..b55a79a 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Defineix l\'aplicació de notes predeterminada a Configuració"</string>
<string name="install_app" msgid="5066668100199613936">"Instal·la l\'aplicació"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Vols replicar-ho a la pantalla externa?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Duplica la pantalla"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Ignora"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Micròfon i càmera"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Ús recent de l\'aplicació"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Mostra l\'accés recent"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 0d7d23a..05b0419 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Výchozí aplikaci pro poznámky nastavíte v Nastavení"</string>
<string name="install_app" msgid="5066668100199613936">"Nainstalovat aplikaci"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Zrcadlit na externí displej?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Zrcadlit displej"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Zavřít"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon a fotoaparát"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Nedávné použití aplikacemi"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Zobrazit nedávný přístup"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 4f6b88e..5971034 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Angiv standardapp til noter i Indstillinger"</string>
<string name="install_app" msgid="5066668100199613936">"Installer app"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Vil du spejle til ekstern skærm?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Spejl skærm"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Luk"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon og kamera"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Seneste brug af apps"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Se seneste adgang"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 07aa14f..c773f71 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Standard-Notizen-App in den Einstellungen einrichten"</string>
<string name="install_app" msgid="5066668100199613936">"App installieren"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Auf externen Bildschirm spiegeln?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Bildschirm spiegeln"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Schließen"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon & Kamera"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Kürzliche App-Nutzung"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Kürzliche Zugriffe ansehen"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 2a8e51c..b6c95aa 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Ορίστε την προεπιλεγμένη εφαρμογή σημειώσεων στις Ρυθμίσεις"</string>
<string name="install_app" msgid="5066668100199613936">"Εγκατάσταση εφαρμογής"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Κατοπτρισμός σε εξωτερική οθόνη;"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Κατοπτρισμός οθόνης"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Παράβλεψη"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Μικρόφωνο και Κάμερα"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Πρόσφατη χρήση εφαρμογής"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Εμφάνιση πρόσφατης πρόσβασης"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 8647adb..6c2b230 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Set default notes app in Settings"</string>
<string name="install_app" msgid="5066668100199613936">"Install app"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Mirror to external display?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Mirror display"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Dismiss"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Microphone and Camera"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Recent app use"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"See recent access"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 8647adb..6c2b230 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Set default notes app in Settings"</string>
<string name="install_app" msgid="5066668100199613936">"Install app"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Mirror to external display?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Mirror display"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Dismiss"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Microphone and Camera"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Recent app use"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"See recent access"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 8647adb..6c2b230 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Set default notes app in Settings"</string>
<string name="install_app" msgid="5066668100199613936">"Install app"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Mirror to external display?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Mirror display"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Dismiss"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Microphone and Camera"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Recent app use"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"See recent access"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index d3ea858..52514d2 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Configura la app de notas predeterminada en Configuración"</string>
<string name="install_app" msgid="5066668100199613936">"Instalar app"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"¿Quieres duplicar a la pantalla externa?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Duplicar pantalla"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Descartar"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Micrófono y cámara"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Uso reciente en apps"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Ver accesos recientes"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 284fad4..4a3c064 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Configura la aplicación de notas predeterminada en Ajustes"</string>
<string name="install_app" msgid="5066668100199613936">"Instalar aplicación"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"¿Replicar en pantalla externa?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Replicar pantalla"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Cerrar"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Micrófono y cámara"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Uso reciente en aplicaciones"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Ver acceso reciente"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 2b79ee5..08b489d 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Määrake seadetes märkmete vaikerakendus."</string>
<string name="install_app" msgid="5066668100199613936">"Installi rakendus"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Kas peegeldada välisekraanile?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Ekraani peegeldamine"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Loobu"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon ja kaamera"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Rakenduste hiljutine kasutamine"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Kuva hiljutine juurdepääs"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 68698ea..19495bc 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -676,8 +676,8 @@
<string name="group_system_go_back" msgid="8838454003680364227">"Atzera: itzuli aurreko egoerara (atzera egiteko botoia)"</string>
<string name="group_system_access_home_screen" msgid="1857344316928441909">"Atzitu hasierako pantaila"</string>
<string name="group_system_overview_open_apps" msgid="6897128761003265350">"Ikusi irekitako aplikazioen ikuspegi orokorra"</string>
- <string name="group_system_cycle_forward" msgid="9202444850838205990">"Joan azken aplikazioetako batetik bestera (aurrera)"</string>
- <string name="group_system_cycle_back" msgid="5163464503638229131">"Joan azken aplikazioetako batetik bestera (atzera)"</string>
+ <string name="group_system_cycle_forward" msgid="9202444850838205990">"Joan azkenaldian erabilitako aplikazio batetik bestera (aurrera)"</string>
+ <string name="group_system_cycle_back" msgid="5163464503638229131">"Joan azkenaldian erabilitako aplikazio batetik bestera (atzera)"</string>
<string name="group_system_access_all_apps_search" msgid="488070738028991753">"Atzitu aplikazio guztien zerrenda eta bilatu (adibidez, bilatzeko aukeraren edo Exekutatzeko tresna aplikazioaren bidez)"</string>
<string name="group_system_hide_reshow_taskbar" msgid="3809304065624351131">"Ezkutatu eta erakutsi (berriro) zereginen barra"</string>
<string name="group_system_access_system_settings" msgid="7961639365383008053">"Atzitu sistemaren ezarpenak"</string>
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Ezarri oharren aplikazio lehenetsia ezarpenetan"</string>
<string name="install_app" msgid="5066668100199613936">"Instalatu aplikazioa"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Kanpoko pantailan islatu nahi duzu?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Islatu pantaila"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Baztertu"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofonoa eta kamera"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Aplikazioen azken erabilera"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Ikusi azkenaldiko sarbidea"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 7199240..68f6c1d 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"برنامه پیشفرض یادداشت را در «تنظیمات» تنظیم کنید"</string>
<string name="install_app" msgid="5066668100199613936">"نصب برنامه"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"در نمایشگر خارجی پخش شود؟"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"بازتاباندن صفحهنمایش"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"بستن"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"میکروفون و دوربین"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"استفاده اخیر از برنامه"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"دیدن دسترسی اخیر"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 073ea60..934abad 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Aseta oletusmuistiinpanosovellus Asetuksista"</string>
<string name="install_app" msgid="5066668100199613936">"Asenna sovellus"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Peilataanko ulkoiselle näytölle?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Peilaa näyttö"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Ohita"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofoni ja kamera"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Sovellusten viimeaikainen käyttö"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Katso viimeaikainen käyttö"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 52bcd76..fe90569 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Définir l\'application de prise de notes par défaut dans les Paramètres"</string>
<string name="install_app" msgid="5066668100199613936">"Installer l\'application"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Dupliquer l\'écran sur un moniteur externe?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Dupliquer l\'écran"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Fermer"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Microphone et appareil photo"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Utilisation récente par les applications"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Afficher l\'accès récent"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 4362d3c..27a4bb6 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Définir une appli de notes par défaut dans les paramètres"</string>
<string name="install_app" msgid="5066668100199613936">"Installer l\'appli"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Mirroring sur écran externe ?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Dupliquer l\'écran"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Fermer"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Micro et caméra"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Utilisation récente par les applis"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Consulter les accès récents"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 1b836a8..2056c2f 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Establece a aplicación de notas predeterminada en Configuración"</string>
<string name="install_app" msgid="5066668100199613936">"Instalar aplicación"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Queres proxectar contido nunha pantalla externa?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Replicar pantalla"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Pechar"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Micrófono e cámara"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Uso recente por parte de aplicacións"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Ver acceso recente"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 132ee2e..84be50a 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"સેટિંગમાં નોંધની ડિફૉલ્ટ ઍપ સેટ કરો"</string>
<string name="install_app" msgid="5066668100199613936">"ઍપ ઇન્સ્ટૉલ કરો"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"શું બાહ્ય ડિસ્પ્લે પર મિરર કરીએ?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"મિરર ડિસ્પ્લે"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"છોડી દો"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"માઇક્રોફોન અને કૅમેરા"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"તાજેતરનો ઍપનો વપરાશ"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"તાજેતરનો ઍક્સેસ મેનેજ કરો"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index f942de5..2b0019f 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"सेटिंग में जाकर, नोट लेने की सुविधा देने वाले ऐप्लिकेशन को डिफ़ॉल्ट के तौर पर सेट करें"</string>
<string name="install_app" msgid="5066668100199613936">"ऐप्लिकेशन इंस्टॉल करें"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"बाहरी डिसप्ले को अन्य डिवाइस पर दिखाना है?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"मिरर डिसप्ले"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"खारिज करें"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"माइक्रोफ़ोन और कैमरा"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"हाल ही में इस्तेमाल करने वाला ऐप्लिकेशन"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"हाल में ऐक्सेस करने वाले ऐप"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index f7c68e1..23899fc 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Postavite zadanu aplikaciju za bilješke u postavkama"</string>
<string name="install_app" msgid="5066668100199613936">"Instalacija"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Želite li zrcaliti na vanjski zaslon?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Zrcaljenje zaslona"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Odbaci"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon i kamera"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Nedavna upotreba aplikacije"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Pogledajte nedavni pristup"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index c1b38d9..e5b17d9 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Állítson be alapértelmezett jegyzetkészítő alkalmazást a Beállításokban"</string>
<string name="install_app" msgid="5066668100199613936">"Alkalmazás telepítése"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Tükrözi a kijelzőt a külső képernyőre?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Kijelző tükrözése"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Elvetés"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon és kamera"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Legutóbbi alkalmazáshasználat"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Legutóbbi hozzáférés"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 7139a1f..4a1c291 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Կարգավորեք նշումների կանխադրված հավելված Կարգավորումներում"</string>
<string name="install_app" msgid="5066668100199613936">"Տեղադրել հավելվածը"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Հայելապատճենե՞լ արտաքին էկրանին"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Հայելապատճենել էկրանը"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Փակել"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Խոսափող և տեսախցիկ"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Հավելվածի վերջին օգտագործումը"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Տեսնել վերջին օգտագործումը"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 44aafde..18a7668 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Setel aplikasi catatan default di Setelan"</string>
<string name="install_app" msgid="5066668100199613936">"Instal aplikasi"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Cerminkan ke layar eksternal?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Cerminkan layar"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Tutup"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon & Kamera"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Penggunaan aplikasi baru-baru ini"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Lihat akses terbaru"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 1fda572a..70bed46 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Stilltu sjálfgefið glósuforrit í stillingunum"</string>
<string name="install_app" msgid="5066668100199613936">"Setja upp forrit"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Spegla yfir á ytri skjá?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Spegla skjá"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Hunsa"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Hljóðnemi og myndavél"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Nýlega notað af forriti"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Sjá nýlegan aðgang"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index ed8885f..baffdb9 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Imposta l\'app per le note predefinita nelle Impostazioni"</string>
<string name="install_app" msgid="5066668100199613936">"Installa app"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Vuoi eseguire il mirroring al display esterno?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Esegui il mirroring del display"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Chiudi"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Microfono e fotocamera"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Uso recente da app"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Vedi accesso recente"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 51f5452..968a982 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"צריך להגדיר את אפליקציית ברירת המחדל לפתקים ב\'הגדרות\'"</string>
<string name="install_app" msgid="5066668100199613936">"התקנת האפליקציה"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"לשקף למסך חיצוני?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"תצוגת מראה"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"סגירה"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"מיקרופון ומצלמה"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"נעשה שימוש לאחרונה באפליקציות"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"צפייה בהרשאות הגישה האחרונות"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index e77aed9..798d42a 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"[設定] でデフォルトのメモアプリを設定してください"</string>
<string name="install_app" msgid="5066668100199613936">"アプリをインストール"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"外部ディスプレイにミラーリングしますか?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"ディスプレイをミラーリングする"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"閉じる"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"マイクとカメラ"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"最近のアプリの使用状況"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"最近のアクセスを表示"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 8d7deec..f21a2a4 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"დააყენეთ ნაგულისხმევი შენიშვნების აპი პარამეტრებში"</string>
<string name="install_app" msgid="5066668100199613936">"აპის ინსტალაცია"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"აირეკლოს გარე ეკრანზე?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"ეკრანის არეკვლა"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"დახურვა"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"მიკროფონი და კამერა"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"აპის ბოლოდროინდელი გამოყენება"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"ბოლო წვდომის ნახვა"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index fc4fe8a..746c02e 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Параметрлерден әдепкі жазба қолданбасын орнатыңыз."</string>
<string name="install_app" msgid="5066668100199613936">"Қолданбаны орнату"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Сыртқы экран арқылы да көрсету керек пе?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Айна дисплей"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Қабылдамау"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Микрофон және камера"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Соңғы рет қолданбаның датчикті пайдалануы"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Соңғы рет пайдаланғандар"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 10c291d..9f3b991 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"កំណត់កម្មវិធីកំណត់ចំណាំលំនាំដើមនៅក្នុងការកំណត់"</string>
<string name="install_app" msgid="5066668100199613936">"ដំឡើងកម្មវិធី"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"បញ្ចាំងទៅឧបករណ៍បញ្ចាំងខាងក្រៅឬ?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"បញ្ចាំងទៅផ្ទាំងអេក្រង់"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"ច្រានចោល"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"មីក្រូហ្វូន និងកាមេរ៉ា"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"ការប្រើប្រាស់កម្មវិធីថ្មីៗនេះ"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"មើលការចូលប្រើនាពេលថ្មីៗនេះ"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 7716980..79eef72 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ಸೆಟ್ಟಿಂಗ್ಗಳಲ್ಲಿ ಡೀಫಾಲ್ಟ್ ಟಿಪ್ಪಣಿಗಳ ಆ್ಯಪ್ ಅನ್ನು ಸೆಟ್ ಮಾಡಿ"</string>
<string name="install_app" msgid="5066668100199613936">"ಆ್ಯಪ್ ಇನ್ಸ್ಟಾಲ್ ಮಾಡಿ"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ಬಾಹ್ಯ ಡಿಸ್ಪ್ಲೇಗೆ ಪ್ರತಿಬಿಂಬಿಸಬೇಕೆ?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"ಮಿರರ್ ಡಿಸ್ಪ್ಲೇ"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"ವಜಾಗೊಳಿಸಿ"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"ಮೈಕ್ರೊಫೋನ್ ಮತ್ತು ಕ್ಯಾಮರಾ"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"ಇತ್ತೀಚಿನ ಆ್ಯಪ್ ಬಳಕೆ"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"ಇತ್ತೀಚಿನ ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ನೋಡಿ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index c2fbf09..7985e4c 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"설정에서 기본 메모 앱 설정"</string>
<string name="install_app" msgid="5066668100199613936">"앱 설치"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"외부 디스플레이로 미러링하시겠습니까?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"디스플레이 미러링"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"닫기"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"마이크 및 카메라"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"최근 앱 사용"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"최근 액세스 보기"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 8ab4cb7..ded1f75 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Параметрлерден демейки кыска жазуулар колдонмосун тууралаңыз"</string>
<string name="install_app" msgid="5066668100199613936">"Колдонмону орнотуу"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Тышкы экранга чыгарасызбы?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Тышкы экран"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Жабуу"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Микрофон жана камера"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Жакында колдонмолордо иштетилген"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Акыркы пайдалануусун көрүү"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index a1585f2..1e61b8a 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ຕັ້ງຄ່າແອັບຈົດບັນທຶກເລີ່ມຕົ້ນໃນການຕັ້ງຄ່າ"</string>
<string name="install_app" msgid="5066668100199613936">"ຕິດຕັ້ງແອັບ"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ສາຍໃສ່ຈໍສະແດງຜົນພາຍນອກບໍ?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"ຈໍສະແດງຜົນແບບສະທ້ອນ"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"ປິດໄວ້"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"ໄມໂຄຣໂຟນ ແລະ ກ້ອງຖ່າຍຮູບ"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"ການໃຊ້ແອັບຫຼ້າສຸດ"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"ເບິ່ງສິດເຂົ້າເຖິງຫຼ້າສຸດ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index c5f649c..06c1369 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Nustatykite numatytąją užrašų programą Nustatymuose"</string>
<string name="install_app" msgid="5066668100199613936">"Įdiegti programą"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Bendrinti ekrano vaizdą išoriniame ekrane?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Bendrinti ekrano vaizdą"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Atsisakyti"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofonas ir fotoaparatas"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Pastarasis programos naudojimas"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Žr. pastarąją prieigą"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index d493609..fdf3028 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Iestatījumos iestatiet noklusējuma piezīmju lietotni."</string>
<string name="install_app" msgid="5066668100199613936">"Instalēt lietotni"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Vai spoguļot ārējā displejā?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Spoguļot displeju"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Nerādīt"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofons un kamera"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Nesen izmantoja lietotnes"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Skatīt neseno piekļuvi"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 46193b3..80ef7bb 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Поставете стандардна апликација за белешки во „Поставки“"</string>
<string name="install_app" msgid="5066668100199613936">"Инсталирајте ја апликацијата"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Да се синхронизира на надворешниот екран?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Пресликај екран"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Отфрли"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Микрофон и камера"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Неодамнешно користење на апликација"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Видете го скорешниот пристап"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index d631538..5b1c80f 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ക്രമീകരണത്തിൽ കുറിപ്പുകൾക്കുള്ള ഡിഫോൾട്ട് ആപ്പ് സജ്ജീകരിക്കുക"</string>
<string name="install_app" msgid="5066668100199613936">"ആപ്പ് ഇൻസ്റ്റാൾ ചെയ്യൂ"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ബാഹ്യ ഡിസ്പ്ലേയിലേക്ക് മിറർ ചെയ്യണോ?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"മിറർ ഡിസ്പ്ലേ"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"ഡിസ്മിസ് ചെയ്യുക"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"മൈക്രോഫോണും ക്യാമറയും"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"അടുത്തിടെയുള്ള ആപ്പ് ഉപയോഗം"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"അടുത്തിടെയുള്ള ആക്സസ് കാണുക"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index b16b22a..3f65931 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Тохиргоонд тэмдэглэлийн өгөгдмөл апп тохируулна уу"</string>
<string name="install_app" msgid="5066668100199613936">"Аппыг суулгах"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Гадны дэлгэцэд тусгал үүсгэх үү?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Дэлгэцийн тусгал үүсгэх"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Хаах"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Микрофон болон камер"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Аппын саяхны ашиглалт"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Саяхны хандалтыг харах"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 16fac23..238aaca 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"सेटिंग्ज मध्ये डीफॉल्ट टिपा अॅप सेट करा"</string>
<string name="install_app" msgid="5066668100199613936">"अॅप इंस्टॉल करा"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"बाह्य डिस्प्लेवर मिरर करायचे आहे का?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"डिस्प्ले मिरर करा"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"डिसमिस करा"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"मायक्रोफोन आणि कॅमेरा"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"अलीकडील अॅप वापर"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"अलीकडील अॅक्सेस पहा"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 7cafdcd..8c764f1 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Tetapkan apl nota lalai dalam Tetapan"</string>
<string name="install_app" msgid="5066668100199613936">"Pasang apl"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Paparkan pada paparan luaran?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Segerakkan paparan"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Ketepikan"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon & Kamera"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Penggunaan apl terbaharu"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Lihat akses terbaharu"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 620124e..883b9e9 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ဆက်တင်များတွင် မူရင်းမှတ်စုများအက်ပ် သတ်မှတ်ပါ"</string>
<string name="install_app" msgid="5066668100199613936">"အက်ပ် ထည့်သွင်းရန်"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ပြင်ပဖန်သားပြင်သို့ စကရင်ပွားမလား။"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"ဖန်သားပြင်ကို စကရင်ပွားရန်"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"ပယ်ရန်"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"မိုက်ခရိုဖုန်းနှင့် ကင်မရာ"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"လတ်တလော အက်ပ်အသုံးပြုမှု"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"လတ်တလောအသုံးပြုမှုကို ကြည့်ရန်"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 722cc12..53a4c52 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Du kan velge en standardapp for notater i Innstillinger"</string>
<string name="install_app" msgid="5066668100199613936">"Installer appen"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Vil du speile til en ekstern skjerm?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Speil skjermen"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Lukk"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon og kamera"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Nylig appbruk"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Se nylig tilgang"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 74e5c6f..3b31b3a 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"सेटिङमा गई नोट बनाउने डिफल्ट एप तोक्नुहोस्"</string>
<string name="install_app" msgid="5066668100199613936">"एप इन्स्टल गर्नुहोस्"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"बाह्य डिस्प्लेमा मिरर गर्ने हो?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"डिस्प्ले मिरर गर्नुहोस्"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"खारेज गर्नुहोस्"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"माइक्रोफोन तथा क्यामेरा"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"एपको हालसालैको प्रयोग"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"हालसालै एक्सेस गर्ने एप हेर्नुहोस्"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 6d97a9f..664af5e 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Standaard notitie-app instellen in Instellingen"</string>
<string name="install_app" msgid="5066668100199613936">"App installeren"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Spiegelen naar extern scherm?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Scherm spiegelen"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Sluiten"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Microfoon en camera"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Recent app-gebruik"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Recente toegang bekijken"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index a6e14f9..db9e0c5 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ସେଟିଂସରେ ଡିଫଲ୍ଟ ନୋଟ୍ସ ଆପ ସେଟ କରନ୍ତୁ"</string>
<string name="install_app" msgid="5066668100199613936">"ଆପ ଇନଷ୍ଟଲ କରନ୍ତୁ"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ଏକ୍ସଟର୍ନଲ ଡିସପ୍ଲେକୁ ମିରର କରିବେ?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"ଡିସପ୍ଲେ ମିରର କରନ୍ତୁ"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"ଖାରଜ କରନ୍ତୁ"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"ମାଇକ୍ରୋଫୋନ ଏବଂ କେମେରା"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"ବର୍ତ୍ତମାନର ଆପ ବ୍ୟବହାର"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"ବର୍ତ୍ତମାନର ଆକ୍ସେସ ଦେଖନ୍ତୁ"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index faca7b1..591c5e7 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਜਾ ਕੇ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਨੋਟ ਐਪ ਨੂੰ ਸੈੱਟ ਕਰੋ"</string>
<string name="install_app" msgid="5066668100199613936">"ਐਪ ਸਥਾਪਤ ਕਰੋ"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ਕੀ ਬਾਹਰੀ ਡਿਸਪਲੇ \'ਤੇ ਪ੍ਰਤਿਬਿੰਬਿਤ ਕਰਨਾ ਹੈ?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"ਡਿਸਪਲੇ ਨੂੰ ਪ੍ਰਤਿਬਿੰਬਿਤ ਕਰੋ"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"ਖਾਰਜ ਕਰੋ"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਅਤੇ ਕੈਮਰਾ"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"ਹਾਲ ਹੀ ਵਿੱਚ ਵਰਤੀ ਗਈ ਐਪ"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"ਹਾਲੀਆ ਪਹੁੰਚ ਦੇਖੋ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 3bb539a..1951221 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Ustaw domyślną aplikację do obsługi notatek w Ustawieniach"</string>
<string name="install_app" msgid="5066668100199613936">"Zainstaluj aplikację"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Powielić na wyświetlaczu zewnętrznym?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Powielaj obraz"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Zamknij"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon i aparat"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Aplikacje korzystające w ostatnim czasie"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Zobacz ostatni dostęp"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 8f93b73..693a3a1 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Defina o app de notas padrão nas Configurações"</string>
<string name="install_app" msgid="5066668100199613936">"Instalar o app"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Espelhar para a tela externa?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Espelhar tela"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Dispensar"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Microfone e câmera"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Uso recente do app"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Consultar acessos recentes"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 02ee098..c4f1f67 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Predefina a app de notas nas Definições"</string>
<string name="install_app" msgid="5066668100199613936">"Instalar app"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Espelhar para o ecrã externo?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Espelhar ecrã"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Ignorar"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Microfone e câmara"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Utilização recente da app"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Ver acesso recente"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 8f93b73..693a3a1 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Defina o app de notas padrão nas Configurações"</string>
<string name="install_app" msgid="5066668100199613936">"Instalar o app"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Espelhar para a tela externa?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Espelhar tela"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Dispensar"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Microfone e câmera"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Uso recente do app"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Consultar acessos recentes"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 14cdeac..a942332 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Setează aplicația prestabilită de note din Setări"</string>
<string name="install_app" msgid="5066668100199613936">"Instalează aplicația"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Oglindești pe ecranul extern?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Afișare în oglindă"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Închide"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Microfon și cameră"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Utilizare recentă în aplicații"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Vezi accesarea recentă"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index c5379c2..68601d6 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Задайте стандартное приложение для заметок в настройках."</string>
<string name="install_app" msgid="5066668100199613936">"Установить приложение"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Дублировать на внешний дисплей?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Дублировать дисплей"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Закрыть"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Микрофон и камера"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Недавнее использование приложениями"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Посмотреть недавний доступ"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index d53f0eb..86cb3c3 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"සැකසීම් තුළ පෙරනිමි සටහන් යෙදුම සකසන්න"</string>
<string name="install_app" msgid="5066668100199613936">"යෙදුම ස්ථාපනය කරන්න"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"බාහිර සංදර්ශකයට දර්පණය කරන්න ද?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"සංදර්ශකය දර්පණය කරන්න"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"අස් කරන්න"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"මයික්රොෆෝනය සහ කැමරාව"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"මෑත යෙදුම් භාවිතය"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"මෑත ප්රවේශය බලන්න"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 7fa0d07..56b9f75 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Nastavte predvolenú aplikáciu na poznámky v Nastaveniach"</string>
<string name="install_app" msgid="5066668100199613936">"Inštalovať aplikáciu"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Chcete zrkadliť na externú obrazovku?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Zrkadliť obrazovku"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Zavrieť"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofón a fotoaparát"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Nedávne využitie aplikácie"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Zobraziť nedávny prístup"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 87c18f4..b72f750 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Nastavite privzeto aplikacijo za zapiske v nastavitvah."</string>
<string name="install_app" msgid="5066668100199613936">"Namesti aplikacijo"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Želite zrcaliti v zunanji zaslon?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Zrcali zaslon"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Opusti"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon in fotoaparat"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Nedavna uporaba v aplikacijah"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Ogled nedavnih dostopov"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 3e1471a..92f6f9c 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Cakto aplikacionin e parazgjedhur të shënimeve te \"Cilësimet\""</string>
<string name="install_app" msgid="5066668100199613936">"Instalo aplikacionin"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Të pasqyrohet në ekranin e jashtëm?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Pasqyro ekranin"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Hiq"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofoni dhe kamera"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Përdorimi i fundit i aplikacionit"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Shiko qasjen e fundit"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 07897d7..7c0d9be 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Подесите подразумевану апликацију за белешке у Подешавањима"</string>
<string name="install_app" msgid="5066668100199613936">"Инсталирај апликацију"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Желите ли да пресликате на спољњи екран?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Пресликај екран"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Одбаци"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Микрофон и камера"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Недавно користила апликација"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Прикажи недавни приступ"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index aee168e..323ce11f 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Ställ in en standardapp för anteckningar i inställningarna"</string>
<string name="install_app" msgid="5066668100199613936">"Installera appen"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Vill du spegla till extern skärm?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Spegla skärm"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Ignorera"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon och kamera"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Senaste appanvändning"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Se senaste åtkomst"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index be3badd..304910a 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Teua programu chaguomsingi ya madokezo katika Mipangilio"</string>
<string name="install_app" msgid="5066668100199613936">"Sakinisha programu"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Ungependa kuonyesha kwenye skrini ya nje?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Akisi skrini"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Ondoa"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Maikrofoni na Kamera"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Matumizi ya programu hivi majuzi"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Angalia ufikiaji wa majuzi"</string>
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index ea3c012..1f671ac 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -37,6 +37,9 @@
<bool name="config_use_large_screen_shade_header">true</bool>
+ <!-- Whether to show bottom sheets edge to edge -->
+ <bool name="config_edgeToEdgeBottomSheetDialog">false</bool>
+
<!-- A collection of defaults for the quick affordances on the lock screen. Each item must be a
string with two parts: the ID of the slot and the comma-delimited list of affordance IDs,
separated by a colon ':' character. For example: <item>bottom_end:home,wallet</item>. The
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 785c4d5..3ac5e2e 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"குறிப்பு எடுப்பதற்கான இயல்புநிலை ஆப்ஸை அமைப்புகளில் அமையுங்கள்"</string>
<string name="install_app" msgid="5066668100199613936">"ஆப்ஸை நிறுவுங்கள்"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"வெளிப்புறக் காட்சிக்கு மிரர் செய்யவா?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"டிஸ்பிளேயை மிரர் செய்"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"வேண்டாம்"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"மைக்ரோஃபோனும் கேமராவும்"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"சமீபத்திய ஆப்ஸ் பயன்பாடு"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"சமீபத்திய அணுகலைக் காட்டு"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 96d77b7..0526f95 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"సెట్టింగ్లలో ఆటోమేటిక్గా ఉండేలా ఒక నోట్స్ యాప్ను సెట్ చేసుకోండి"</string>
<string name="install_app" msgid="5066668100199613936">"యాప్ను ఇన్స్టాల్ చేయండి"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"బాహ్య డిస్ప్లేను మిర్రర్ చేయాలా?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"మిర్రర్ డిస్ప్లే"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"విస్మరించండి"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"మైక్రోఫోన్ & కెమెరా"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"ఇటీవలి యాప్ వినియోగం"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"ఇటీవలి యాక్సెస్ను చూడండి"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index f8fbfbe..8949360 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"กำหนดแอปการจดบันทึกเริ่มต้นในการตั้งค่า"</string>
<string name="install_app" msgid="5066668100199613936">"ติดตั้งแอป"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"มิเรอร์ไปยังจอแสดงผลภายนอกไหม"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"มิเรอร์จอแสดงผล"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"ปิด"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"ไมโครโฟนและกล้อง"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"การใช้แอปครั้งล่าสุด"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"ดูการเข้าถึงล่าสุด"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 435398d..e5c11df 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Magtakda ng default na app sa pagtatala sa Mga Setting"</string>
<string name="install_app" msgid="5066668100199613936">"I-install ang app"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"I-mirror sa external na display?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"I-mirror ang display"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"I-dismiss"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Mikropono at Camera"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Kamakailang paggamit ng app"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Tingnan ang kamakailang access"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index d0ddbec..3552d92 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Ayarlar\'ı kullanarak varsayılan notlar ayarlayın"</string>
<string name="install_app" msgid="5066668100199613936">"Uygulamayı yükle"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Harici ekrana yansıtılsın mı?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Ekranı yansıt"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Kapat"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon ve Kamera"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Son uygulama kullanımı"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Son erişimi göster"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 1489dc7..2e6aea9 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Призначте стандартний додаток для нотаток у налаштуваннях"</string>
<string name="install_app" msgid="5066668100199613936">"Установити додаток"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Дублювати на зовнішньому екрані?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Дублювати екран"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Закрити"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Мікрофон і камера"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Нещодавнє використання додатками"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Переглянути нещодавній доступ"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 9d69a5e..2e2fc41 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ترتیبات میں ڈیفالٹ نوٹس ایپ سیٹ کریں"</string>
<string name="install_app" msgid="5066668100199613936">"ایپ انسٹال کریں"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"بیرونی ڈسپلے پر مرر کریں؟"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"ڈسپلے کو دو طرفہ مطابقت پذیر بنائیں"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"برخاست کریں"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"مائیکروفون اور کیمرا"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"حالیہ ایپ کا استعمال"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"حالیہ رسائی دیکھیں"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 31b216b..a8654d5 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Standart qaydlar ilovasini Sozlamalar orqali tanlang"</string>
<string name="install_app" msgid="5066668100199613936">"Ilovani oʻrnatish"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Tashqi displeyda aks ettirilsinmi?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Displeyni akslantirish"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Yopish"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon va kamera"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Ilovadan oxirgi foydalanish"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Oxirgi ruxsatni koʻrish"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 814e0a6..5443745 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Đặt ứng dụng ghi chú mặc định trong phần Cài đặt"</string>
<string name="install_app" msgid="5066668100199613936">"Cài đặt ứng dụng"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Đồng bộ hoá hai chiều sang màn hình ngoài?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Phản chiếu màn hình"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Đóng"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Micrô và máy ảnh"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Hoạt động sử dụng gần đây của ứng dụng"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Xem hoạt động truy cập gần đây"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 8b2caf0..44c163b 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"在设置中设置默认记事应用"</string>
<string name="install_app" msgid="5066668100199613936">"安装应用"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"镜像到外接显示屏?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"镜像显示"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"关闭"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"麦克风和摄像头"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"近期应用对手机传感器的使用情况"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"查看近期使用情况"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 8a160c3..f45b2fe 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"在「設定」中指定預設筆記應用程式"</string>
<string name="install_app" msgid="5066668100199613936">"安裝應用程式"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"要鏡像投射至外部顯示屏嗎?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"鏡像顯示"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"關閉"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"麥克風和相機"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"近期應用程式使用情況"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"查看近期存取記錄"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 38b81a0..5a5bb9d 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"在「設定」中指定預設記事應用程式"</string>
<string name="install_app" msgid="5066668100199613936">"安裝應用程式"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"要以鏡像方式投放至外部螢幕嗎?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"鏡像顯示"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"關閉"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"麥克風和相機"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"最近曾使用感應器的應用程式"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"查看近期存取記錄"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index fc70417..8b6bfae 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -1183,10 +1183,8 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Setha i-app yamanothi azenzakalelayo Kumsethingi"</string>
<string name="install_app" msgid="5066668100199613936">"Faka i-app"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Fanisa nesibonisi sangaphandle?"</string>
- <!-- no translation found for mirror_display (2515262008898122928) -->
- <skip />
- <!-- no translation found for dismiss_dialog (2195508495854675882) -->
- <skip />
+ <string name="mirror_display" msgid="2515262008898122928">"Isibonisi sokufanisa"</string>
+ <string name="dismiss_dialog" msgid="2195508495854675882">"Chitha"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Imakrofoni Nekhamera"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Ukusetshenziswa kwakamuva kwe-app"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Bona ukufinyelela kwakamuva"</string>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 1add90f..75e71e4 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -947,6 +947,9 @@
<!-- Flag controlling whether visual query attention detection has been enabled. -->
<bool name="config_enableVisualQueryAttentionDetection">false</bool>
+ <!-- Whether to show bottom sheets edge to edge -->
+ <bool name="config_edgeToEdgeBottomSheetDialog">true</bool>
+
<!--
Whether the scene container framework is enabled.
@@ -954,4 +957,15 @@
bouncer, lockscreen, shade, and quick settings.
-->
<bool name="config_sceneContainerFrameworkEnabled">true</bool>
+
+ <!--
+ Time in milliseconds the user has to touch the side FPS sensor to successfully authenticate
+ TODO(b/302332976) Get this value from the HAL if they can provide an API for it.
+ -->
+ <integer name="config_restToUnlockDuration">300</integer>
+
+ <!--
+ Width in pixels of the Side FPS sensor.
+ -->
+ <integer name="config_sfpsSensorWidth">200</integer>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 0ee5da2..1b09c6a 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -177,6 +177,12 @@
<!-- Size of the view displaying the mobile signal icon in the status bar. This value should
match the viewport height of mobile signal drawables such as ic_lte_mobiledata -->
<dimen name="status_bar_mobile_type_size">16sp</dimen>
+ <!-- Size of the view that contains the network type. Should be equal to
+ status_bar_mobile_type_size + 2, to account for 1sp top and bottom padding -->
+ <dimen name="status_bar_mobile_container_height">18sp</dimen>
+ <!-- Corner radius for the background of the network type indicator. Should be equal to
+ status_bar_mobile_container_height / 2 -->
+ <dimen name="status_bar_mobile_container_corner_radius">9sp</dimen>
<!-- Size of the view displaying the mobile roam icon in the status bar. This value should
match the viewport size of drawable stat_sys_roaming -->
<dimen name="status_bar_mobile_roam_size">8sp</dimen>
@@ -846,12 +852,17 @@
<!-- Amount the button should shake when it's not long-pressed for long enough. -->
<dimen name="keyguard_affordance_shake_amplitude">8dp</dimen>
- <dimen name="keyguard_affordance_horizontal_offset">32dp</dimen>
+ <dimen name="keyguard_affordance_horizontal_offset">16dp</dimen>
<dimen name="keyguard_affordance_vertical_offset">32dp</dimen>
<!-- Value should be at least sum of 'keyguard_affordance_width' +
'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>
@@ -1350,6 +1361,9 @@
<dimen name="screenrecord_options_padding_bottom">16dp</dimen>
<dimen name="screenrecord_buttons_margin_top">20dp</dimen>
+ <!-- Connected display dialog -->
+ <dimen name="connected_display_dialog_logo_size">48dp</dimen>
+
<!-- Keyguard user switcher -->
<dimen name="kg_user_switcher_text_size">16sp</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..7e44adf 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -711,8 +711,8 @@
<!-- QuickSettings: Cast detail panel, default device description [CHAR LIMIT=NONE] -->
<!-- QuickSettings: Cast detail panel, text when there are no items [CHAR LIMIT=NONE] -->
<string name="quick_settings_cast_detail_empty_text">No devices available</string>
- <!-- QuickSettings: Cast unavailable, text when not connected to WiFi [CHAR LIMIT=NONE] -->
- <string name="quick_settings_cast_no_wifi">Wi\u2011Fi not connected</string>
+ <!-- QuickSettings: Cast unavailable, text when not connected to WiFi or ethernet[CHAR LIMIT=NONE] -->
+ <string name="quick_settings_cast_no_network">No Wi\u2011Fi or Ethernet connection</string>
<!-- QuickSettings: Brightness dialog title [CHAR LIMIT=NONE] -->
<string name="quick_settings_brightness_dialog_title">Brightness</string>
<!-- QuickSettings: Label for the toggle that controls whether display inversion is enabled. [CHAR LIMIT=NONE] -->
@@ -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/res/xml/self_certified_network_capabilities_both.xml b/packages/SystemUI/res/xml/self_certified_network_capabilities_both.xml
new file mode 100644
index 0000000..4b430e1
--- /dev/null
+++ b/packages/SystemUI/res/xml/self_certified_network_capabilities_both.xml
@@ -0,0 +1,20 @@
+<?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.
+ ~
+ -->
+<network-capabilities-declaration xmlns:android="http://schemas.android.com/apk/res/android">
+ <uses-network-capability android:name="NET_CAPABILITY_PRIORITIZE_LATENCY"/>
+</network-capabilities-declaration>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/DisplaySpecific.kt b/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/DisplaySpecific.kt
new file mode 100644
index 0000000..57a49c8
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/dagger/qualifiers/DisplaySpecific.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.dagger.qualifiers
+
+import java.lang.annotation.Documented
+import java.lang.annotation.Retention
+import java.lang.annotation.RetentionPolicy.RUNTIME
+import javax.inject.Qualifier
+
+/** Annotates a class that is display specific. */
+@Qualifier @Documented @Retention(RUNTIME) annotation class DisplaySpecific
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
index 5d036fb..b44bf39 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
@@ -71,7 +71,6 @@
private AnimatedVectorDrawable mAnimatedDrawable;
private boolean mIsShowing;
- private boolean mCanShow = true;
private int mDisplayRotation;
private boolean mIsTaskbarVisible = false;
@@ -150,7 +149,7 @@
@Override
public boolean show() {
- if (!mCanShow || mIsShowing) {
+ if (mIsShowing) {
return false;
}
@@ -222,14 +221,6 @@
}
@Override
- public void setCanShowRotationButton(boolean canShow) {
- mCanShow = canShow;
- if (!mCanShow) {
- hide();
- }
- }
-
- @Override
public void onTaskbarStateChanged(boolean taskbarVisible, boolean taskbarStashed) {
mIsTaskbarVisible = taskbarVisible;
mIsTaskbarStashed = taskbarStashed;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButton.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButton.java
index 89f71eb..42dda0a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButton.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButton.java
@@ -36,7 +36,6 @@
default boolean isVisible() {
return false;
}
- default void setCanShowRotationButton(boolean canShow) {}
default void onTaskbarStateChanged(boolean taskbarVisible, boolean taskbarStashed) {}
default void updateIcon(int lightIconColor, int darkIconColor) { }
default void setOnClickListener(View.OnClickListener onClickListener) { }
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/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
index 0094820..a6e04ce 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
@@ -23,6 +23,7 @@
import android.window.PictureInPictureSurfaceTransaction;
import android.window.TaskSnapshot;
+import com.android.internal.os.IResultReceiver;
import com.android.systemui.shared.recents.model.ThumbnailData;
public class RecentsAnimationControllerCompat {
@@ -89,11 +90,16 @@
* @param sendUserLeaveHint determines whether userLeaveHint will be set true to the previous
* app.
*/
- public void finish(boolean toHome, boolean sendUserLeaveHint) {
+ public void finish(boolean toHome, boolean sendUserLeaveHint, IResultReceiver finishCb) {
try {
- mAnimationController.finish(toHome, sendUserLeaveHint);
+ mAnimationController.finish(toHome, sendUserLeaveHint, finishCb);
} catch (RemoteException e) {
Log.e(TAG, "Failed to finish recents animation", e);
+ try {
+ finishCb.send(0, null);
+ } catch (Exception ex) {
+ // Local call, can ignore
+ }
}
}
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/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 2f3c1f2..01a75d9 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -34,6 +34,7 @@
import com.android.systemui.customization.R
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.DisplaySpecific
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags.DOZING_MIGRATION_1
@@ -79,7 +80,7 @@
private val batteryController: BatteryController,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val configurationController: ConfigurationController,
- @Main private val resources: Resources,
+ @DisplaySpecific private val resources: Resources,
private val context: Context,
@Main private val mainExecutor: DelayableExecutor,
@Background private val bgExecutor: Executor,
@@ -205,19 +206,19 @@
private var isRegistered = false
private var disposableHandle: DisposableHandle? = null
private val regionSamplingEnabled = featureFlags.isEnabled(REGION_SAMPLING)
-
+ private var largeClockOnSecondaryDisplay = false
private fun updateColors() {
if (regionSamplingEnabled) {
clock?.let { clock ->
smallRegionSampler?.let {
- smallClockIsDark = it.currentRegionDarkness().isDark
- clock.smallClock.events.onRegionDarknessChanged(smallClockIsDark)
+ val isRegionDark = it.currentRegionDarkness().isDark
+ clock.smallClock.events.onRegionDarknessChanged(isRegionDark)
}
largeRegionSampler?.let {
- largeClockIsDark = it.currentRegionDarkness().isDark
- clock.largeClock.events.onRegionDarknessChanged(largeClockIsDark)
+ val isRegionDark = it.currentRegionDarkness().isDark
+ clock.largeClock.events.onRegionDarknessChanged(isRegionDark)
}
}
return
@@ -225,12 +226,12 @@
val isLightTheme = TypedValue()
context.theme.resolveAttribute(android.R.attr.isLightTheme, isLightTheme, true)
- smallClockIsDark = isLightTheme.data == 0
- largeClockIsDark = isLightTheme.data == 0
+ val isRegionDark = isLightTheme.data == 0
clock?.run {
- smallClock.events.onRegionDarknessChanged(smallClockIsDark)
- largeClock.events.onRegionDarknessChanged(largeClockIsDark)
+ Log.i(TAG, "Region isDark: $isRegionDark")
+ smallClock.events.onRegionDarknessChanged(isRegionDark)
+ largeClock.events.onRegionDarknessChanged(isRegionDark)
}
}
protected open fun createRegionSampler(
@@ -260,9 +261,6 @@
get() = isKeyguardVisible && dozeAmount < DOZE_TICKRATE_THRESHOLD
private var cachedWeatherData: WeatherData? = null
- private var smallClockIsDark = true
- private var largeClockIsDark = true
-
private val configListener =
object : ConfigurationController.ConfigurationListener {
override fun onThemeChanged() {
@@ -381,6 +379,19 @@
?.removeOnAttachStateChangeListener(largeClockOnAttachStateChangeListener)
}
+ /**
+ * Sets this clock as showing in a secondary display.
+ *
+ * Not that this is not necessarily needed, as we could get the displayId from [Context]
+ * directly and infere [largeClockOnSecondaryDisplay] from the id being different than the
+ * default display one. However, if we do so, current screenshot tests would not work, as they
+ * pass an activity context always from the default display.
+ */
+ fun setLargeClockOnSecondaryDisplay(onSecondaryDisplay: Boolean) {
+ largeClockOnSecondaryDisplay = onSecondaryDisplay
+ updateFontSizes()
+ }
+
private fun updateTimeListeners() {
smallTimeListener?.stop()
largeTimeListener?.stop()
@@ -403,9 +414,15 @@
smallClock.events.onFontSettingChanged(
resources.getDimensionPixelSize(R.dimen.small_clock_text_size).toFloat()
)
- largeClock.events.onFontSettingChanged(
- resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat()
- )
+ largeClock.events.onFontSettingChanged(getLargeClockSizePx())
+ }
+ }
+
+ private fun getLargeClockSizePx(): Float {
+ return if (largeClockOnSecondaryDisplay) {
+ resources.getDimensionPixelSize(R.dimen.presentation_clock_text_size).toFloat()
+ } else {
+ resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat()
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
index d677a15..dec7d79 100644
--- a/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
@@ -68,10 +68,13 @@
clock = requireViewById(R.id.clock)
keyguardStatusViewController =
- keyguardStatusViewComponentFactory.build(clock).keyguardStatusViewController.apply {
- setDisplayedOnSecondaryDisplay()
- init()
- }
+ keyguardStatusViewComponentFactory
+ .build(clock, display)
+ .keyguardStatusViewController
+ .apply {
+ setDisplayedOnSecondaryDisplay()
+ init()
+ }
}
/** [ConnectedDisplayKeyguardPresentation] factory. */
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/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index ff8e489..3c8301f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -84,7 +84,6 @@
private final DumpManager mDumpManager;
private final ClockEventController mClockEventController;
private final LogBuffer mLogBuffer;
-
private FrameLayout mSmallClockFrame; // top aligned clock
private FrameLayout mLargeClockFrame; // centered clock
@@ -265,6 +264,7 @@
if (mShownOnSecondaryDisplay) {
mView.setLargeClockOnSecondaryDisplay(true);
+ mClockEventController.setLargeClockOnSecondaryDisplay(true);
displayClock(LARGE, /* animate= */ false);
hideSliceViewAndNotificationIconContainer();
return;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 9c015fe..8a6f101 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -419,7 +419,7 @@
mClock.post(mMoveTextRunnable);
mKeyguardClockSwitchController = mKeyguardStatusViewComponentFactory
- .build(findViewById(R.id.clock))
+ .build(findViewById(R.id.clock), getDisplay())
.getKeyguardClockSwitchController();
mKeyguardClockSwitchController.setOnlyClock(true);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 51c0676..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);
}
/**
@@ -139,7 +121,7 @@
case PROMPT_REASON_USER_REQUEST:
return R.string.kg_prompt_after_user_lockdown_password;
case PROMPT_REASON_PREPARE_FOR_UPDATE:
- return R.string.kg_prompt_unattended_update_password;
+ return R.string.kg_prompt_reason_timeout_password;
case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT:
return R.string.kg_prompt_reason_timeout_password;
case PROMPT_REASON_TRUSTAGENT_EXPIRED:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index 714ba81..57151ae 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -321,7 +321,7 @@
resId = R.string.kg_prompt_after_user_lockdown_pattern;
break;
case PROMPT_REASON_PREPARE_FOR_UPDATE:
- resId = R.string.kg_prompt_unattended_update_pattern;
+ resId = R.string.kg_prompt_reason_timeout_pattern;
break;
case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT:
resId = R.string.kg_prompt_reason_timeout_pattern;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index 9d6d033..681aa70 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -123,7 +123,7 @@
case PROMPT_REASON_USER_REQUEST:
return R.string.kg_prompt_after_user_lockdown_pin;
case PROMPT_REASON_PREPARE_FOR_UPDATE:
- return R.string.kg_prompt_unattended_update_pin;
+ return R.string.kg_prompt_reason_timeout_pin;
case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT:
return R.string.kg_prompt_reason_timeout_pin;
case PROMPT_REASON_TRUSTAGENT_EXPIRED:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index 8717a53..d2d0517 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -31,6 +31,7 @@
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
@@ -39,9 +40,9 @@
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.res.R;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.res.R;
public class KeyguardSimPinViewController
extends KeyguardPinBasedInputViewController<KeyguardSimPinView> {
@@ -324,7 +325,11 @@
} else {
SubscriptionInfo info = mKeyguardUpdateMonitor.getSubscriptionInfoForSubId(mSubId);
CharSequence displayName = info != null ? info.getDisplayName() : ""; // don't crash
- msg = rez.getString(R.string.kg_sim_pin_instructions_multi, displayName);
+ if (!TextUtils.isEmpty(displayName)) {
+ msg = rez.getString(R.string.kg_sim_pin_instructions_multi, displayName);
+ } else {
+ msg = rez.getString(R.string.kg_sim_pin_instructions);
+ }
if (info != null) {
color = info.getIconTint();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
index 248b7af..b52a36b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -29,6 +29,7 @@
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.text.TextUtils;
import android.util.Log;
import android.view.WindowManager;
import android.widget.ImageView;
@@ -36,9 +37,9 @@
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.res.R;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.res.R;
public class KeyguardSimPukViewController
extends KeyguardPinBasedInputViewController<KeyguardSimPukView> {
@@ -206,7 +207,11 @@
} else {
SubscriptionInfo info = mKeyguardUpdateMonitor.getSubscriptionInfoForSubId(mSubId);
CharSequence displayName = info != null ? info.getDisplayName() : "";
- msg = rez.getString(R.string.kg_puk_enter_puk_hint_multi, displayName);
+ if (!TextUtils.isEmpty(displayName)) {
+ msg = rez.getString(R.string.kg_puk_enter_puk_hint_multi, displayName);
+ } else {
+ msg = rez.getString(R.string.kg_puk_enter_puk_hint);
+ }
if (info != null) {
color = info.getIconTint();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index f9cc03ee..d848602 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -18,7 +18,6 @@
import static java.util.Collections.emptySet;
-import android.animation.LayoutTransition;
import android.content.Context;
import android.graphics.Canvas;
import android.os.Build;
@@ -79,14 +78,6 @@
mKeyguardSlice = findViewById(R.id.keyguard_slice_view);
mMediaHostContainer = findViewById(R.id.status_view_media_container);
- if (mMediaHostContainer != null) {
- LayoutTransition mediaLayoutTransition = new LayoutTransition();
- ((ViewGroup) mMediaHostContainer).setLayoutTransition(mediaLayoutTransition);
- mediaLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
- mediaLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
- mediaLayoutTransition.disableTransitionType(LayoutTransition.APPEARING);
- mediaLayoutTransition.disableTransitionType(LayoutTransition.DISAPPEARING);
- }
updateDark();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 67b7052..79642bd 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -23,7 +23,6 @@
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
import android.animation.Animator;
-import android.animation.LayoutTransition;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.content.res.Configuration;
@@ -50,14 +49,15 @@
import com.android.keyguard.KeyguardClockSwitch.ClockSize;
import com.android.keyguard.logging.KeyguardLogger;
import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
+import com.android.systemui.animation.ViewHierarchyAnimator;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
-import com.android.systemui.power.shared.model.ScreenPowerState;
import com.android.systemui.plugins.ClockController;
import com.android.systemui.power.domain.interactor.PowerInteractor;
+import com.android.systemui.power.shared.model.ScreenPowerState;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
@@ -175,27 +175,10 @@
return;
}
- final LayoutTransition mediaLayoutTransition =
- ((ViewGroup) mediaHostContainer).getLayoutTransition();
- if (mediaLayoutTransition == null) return;
-
- mediaLayoutTransition.enableTransitionType(LayoutTransition.CHANGING);
+ ViewHierarchyAnimator.Companion.animateNextUpdate(mediaHostContainer,
+ Interpolators.STANDARD, /* duration= */ 500L,
+ /* animateChildren= */ false);
});
-
- mediaHostContainer.addOnLayoutChangeListener(
- (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
- final LayoutTransition mediaLayoutTransition =
- ((ViewGroup) mediaHostContainer).getLayoutTransition();
- if (mediaLayoutTransition == null) return;
- if (!mediaLayoutTransition.isTransitionTypeEnabled(
- LayoutTransition.CHANGING)) {
- return;
- }
- // Note: when this is called, the LayoutTransition is already been set up.
- // Disables the LayoutTransition until it's explicitly enabled again.
- mediaLayoutTransition.disableTransitionType(LayoutTransition.CHANGING);
- }
- );
}
mDumpManager.registerDumpable(getInstanceName(), this);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 3f5ec7d..b7bb35e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -189,6 +189,8 @@
import com.android.systemui.util.Assert;
import com.android.systemui.util.settings.SecureSettings;
+import dalvik.annotation.optimization.NeverCompile;
+
import com.google.android.collect.Lists;
import java.io.PrintWriter;
@@ -1248,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 =
@@ -1276,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++) {
@@ -1297,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 {
@@ -1325,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;
@@ -1342,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;
@@ -4430,6 +4437,7 @@
}
@SuppressLint("MissingPermission")
+ @NeverCompile
@Override
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("KeyguardUpdateMonitor state:");
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/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
index 1d2d77f..40d0be1 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
@@ -18,6 +18,7 @@
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Color;
@@ -68,6 +69,7 @@
private boolean mUseBackground = false;
private float mDozeAmount = 0f;
+ @SuppressLint("ClickableViewAccessibility")
public LockIconView(Context context, AttributeSet attrs) {
super(context, attrs);
mSensorRect = new RectF();
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 9b00b5f..83da80f 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -29,6 +29,7 @@
import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -57,11 +58,11 @@
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.AuthRippleController;
import com.android.systemui.biometrics.UdfpsController;
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
+import com.android.systemui.bouncer.domain.interactor.BouncerInteractor;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
@@ -73,12 +74,16 @@
import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlags;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import dagger.Lazy;
+
import java.io.PrintWriter;
import java.util.Objects;
import java.util.function.Consumer;
@@ -127,6 +132,8 @@
@NonNull private final KeyguardTransitionInteractor mTransitionInteractor;
@NonNull private final KeyguardInteractor mKeyguardInteractor;
@NonNull private final View.AccessibilityDelegate mAccessibilityDelegate;
+ @NonNull private final Lazy<BouncerInteractor> mBouncerInteractor;
+ @NonNull private final SceneContainerFlags mSceneContainerFlags;
// Tracks the velocity of a touch to help filter out the touches that move too fast.
private VelocityTracker mVelocityTracker;
@@ -203,7 +210,9 @@
@NonNull KeyguardInteractor keyguardInteractor,
@NonNull FeatureFlags featureFlags,
PrimaryBouncerInteractor primaryBouncerInteractor,
- Context context
+ Context context,
+ Lazy<BouncerInteractor> bouncerInteractor,
+ SceneContainerFlags sceneContainerFlags
) {
mStatusBarStateController = statusBarStateController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -232,6 +241,8 @@
dumpManager.registerDumpable(TAG, this);
mResources = resources;
mContext = context;
+ mBouncerInteractor = bouncerInteractor;
+ mSceneContainerFlags = sceneContainerFlags;
mAccessibilityDelegate = new View.AccessibilityDelegate() {
private final AccessibilityNodeInfo.AccessibilityAction mAccessibilityAuthenticateHint =
@@ -256,6 +267,7 @@
}
/** Sets the LockIconView to the controller and rebinds any that depend on it. */
+ @SuppressLint("ClickableViewAccessibility")
public void setLockIconView(LockIconView lockIconView) {
mView = lockIconView;
mView.setImageDrawable(mIcon);
@@ -305,6 +317,8 @@
if (lockIconView.isAttachedToWindow()) {
registerCallbacks();
}
+
+ lockIconView.setOnTouchListener((view, motionEvent) -> onTouchEvent(motionEvent));
}
private void registerCallbacks() {
@@ -635,19 +649,18 @@
};
/**
- * Handles the touch if it is within the lock icon view and {@link #isActionable()} is true.
+ * Handles the touch if {@link #isActionable()} is true.
* Subsequently, will trigger {@link #onLongPress()} if a touch is continuously in the lock icon
* area for {@link #mLongPressTimeout} ms.
*
* Touch speed debouncing mimics logic from the velocity tracker in {@link UdfpsController}.
*/
- public boolean onTouchEvent(MotionEvent event, Runnable onGestureDetectedRunnable) {
- if (!onInterceptTouchEvent(event)) {
+ private boolean onTouchEvent(MotionEvent event) {
+ if (!actionableDownEventStartedOnView(event)) {
cancelTouches();
return false;
}
- mOnGestureDetectedRunnable = onGestureDetectedRunnable;
switch(event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_HOVER_ENTER:
@@ -679,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();
@@ -701,11 +714,24 @@
}
/**
- * Intercepts the touch if the onDown event and current event are within this lock icon view's
- * bounds.
+ * 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 boolean onInterceptTouchEvent(MotionEvent event) {
- if (!inLockIconArea(event) || !isActionable()) {
+ 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;
}
@@ -716,7 +742,8 @@
return mDownDetected;
}
- private void onLongPress() {
+ @VisibleForTesting
+ protected void onLongPress() {
cancelTouches();
if (mFalsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) {
Log.v(TAG, "lock icon long-press rejected by the falsing manager.");
@@ -729,14 +756,15 @@
mAuthRippleController.showUnlockRipple(FINGERPRINT);
}
updateVisibility();
- if (mOnGestureDetectedRunnable != null) {
- mOnGestureDetectedRunnable.run();
- }
// play device entry haptic (consistent with UDFPS controller longpress)
vibrateOnLongPress();
- mKeyguardViewController.showPrimaryBouncer(/* scrim */ true);
+ if (mSceneContainerFlags.isEnabled()) {
+ mBouncerInteractor.get().showOrUnlockDevice(null);
+ } else {
+ mKeyguardViewController.showPrimaryBouncer(/* scrim */ true);
+ }
}
@@ -751,12 +779,6 @@
}
}
- private boolean inLockIconArea(MotionEvent event) {
- mView.getHitRect(mSensorTouchLocation);
- return mSensorTouchLocation.contains((int) event.getX(), (int) event.getY())
- && mView.getVisibility() == View.VISIBLE;
- }
-
private boolean isActionable() {
if (mIsBouncerShowing) {
Log.v(TAG, "lock icon long-press ignored, bouncer already showing.");
@@ -834,6 +856,19 @@
}
};
+ /**
+ * Whether the lock icon will handle a touch while dozing.
+ */
+ public boolean willHandleTouchWhileDozing(MotionEvent event) {
+ // is in lock icon area
+ mView.getHitRect(mSensorTouchLocation);
+ final boolean inLockIconArea =
+ mSensorTouchLocation.contains((int) event.getX(), (int) event.getY())
+ && mView.getVisibility() == View.VISIBLE;
+
+ return inLockIconArea && actionableDownEventStartedOnView(event);
+ }
+
private final View.OnClickListener mA11yClickListener = v -> onLongPress();
private final AccessibilityManager.AccessibilityStateChangeListener
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardDisplayModule.kt b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardDisplayModule.kt
new file mode 100644
index 0000000..1f145d8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardDisplayModule.kt
@@ -0,0 +1,42 @@
+package com.android.keyguard.dagger
+
+import android.content.Context
+import android.content.res.Resources
+import android.view.Display
+import com.android.systemui.dagger.qualifiers.DisplaySpecific
+import com.android.systemui.util.kotlin.getOrNull
+import dagger.BindsOptionalOf
+import dagger.Module
+import dagger.Provides
+import java.util.Optional
+
+/**
+ * Binds display specific context and resources.
+ *
+ * When a [Display] is available in the scope, binds a [DisplaySpecific] [Context] and [Resources].
+ * When not available, the default display context and resources are used.
+ */
+@Module
+abstract class KeyguardDisplayModule {
+
+ @BindsOptionalOf abstract fun optionalDisplay(): Display
+
+ companion object {
+ @Provides
+ @DisplaySpecific
+ fun getDisplayContext(context: Context, optionalDisplay: Optional<Display>): Context {
+ val display = optionalDisplay.getOrNull() ?: return context
+ return if (context.displayId == display.displayId) {
+ context
+ } else {
+ context.createDisplayContext(display)
+ }
+ }
+
+ @Provides
+ @DisplaySpecific
+ fun getDisplayResources(@DisplaySpecific context: Context): Resources {
+ return context.resources
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java
index d342377..f3014f1 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java
@@ -16,6 +16,8 @@
package com.android.keyguard.dagger;
+import android.view.Display;
+
import com.android.keyguard.KeyguardClockSwitchController;
import com.android.keyguard.KeyguardStatusView;
import com.android.keyguard.KeyguardStatusViewController;
@@ -28,13 +30,17 @@
*
* TODO: unify this with {@link KeyguardStatusBarViewComponent}
*/
-@Subcomponent(modules = {KeyguardStatusViewModule.class})
+@Subcomponent(modules = {KeyguardStatusViewModule.class, KeyguardDisplayModule.class})
@KeyguardStatusViewScope
public interface KeyguardStatusViewComponent {
/** Simple factory for {@link KeyguardStatusViewComponent}. */
@Subcomponent.Factory
interface Factory {
- KeyguardStatusViewComponent build(@BindsInstance KeyguardStatusView presentation);
+ /** Creates {@link KeyguardStatusViewComponent} for a given display. */
+ KeyguardStatusViewComponent build(
+ @BindsInstance KeyguardStatusView presentation,
+ @BindsInstance Display display
+ );
}
/** Builds a {@link com.android.keyguard.KeyguardClockSwitchController}. */
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 0180384..1a34cc4 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -15,129 +15,60 @@
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.row.NotificationGutsManager;
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 java.util.concurrent.Executor;
+import dagger.Lazy;
+
import java.util.function.Consumer;
import javax.inject.Inject;
import javax.inject.Named;
-import dagger.Lazy;
-
/**
* Class to handle ugly dependencies throughout sysui until we determine the
* long-term dependency injection solution.
@@ -155,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.
@@ -172,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.
@@ -198,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 =
@@ -212,134 +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<NotificationGutsManager> mNotificationGutsManager;
@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;
@@ -362,184 +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(NotificationGutsManager.class, mNotificationGutsManager::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/GuestResumeSessionReceiver.java b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
index 4541384..b573fad 100644
--- a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
@@ -28,6 +28,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.qs.QSUserSwitcherEvent;
import com.android.systemui.settings.UserTracker;
@@ -46,6 +47,7 @@
/**
* Manages notification when a guest session is resumed.
*/
+@SysUISingleton
public class GuestResumeSessionReceiver {
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 4af2c74..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;
@@ -93,6 +93,8 @@
import com.android.systemui.util.concurrency.ThreadFactory;
import com.android.systemui.util.settings.SecureSettings;
+import dalvik.annotation.optimization.NeverCompile;
+
import kotlin.Pair;
import java.io.PrintWriter;
@@ -163,7 +165,7 @@
ScreenDecorHwcLayer mScreenDecorHwcLayer;
private WindowManager mWindowManager;
private int mRotation;
- private SettingObserver mColorInversionSetting;
+ private UserSettingObserver mColorInversionSetting;
@Nullable
private DelayableExecutor mExecutor;
private Handler mHandler;
@@ -684,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
@@ -1088,6 +1090,7 @@
}
}
+ @NeverCompile
@Override
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("ScreenDecorations state:");
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/biometrics/domain/BiometricsDomainLayerModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/BiometricsDomainLayerModule.kt
index a590dccd..b9b2fd8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/BiometricsDomainLayerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/BiometricsDomainLayerModule.kt
@@ -23,8 +23,6 @@
import com.android.systemui.biometrics.domain.interactor.LogContextInteractorImpl
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractorImpl
-import com.android.systemui.biometrics.domain.interactor.SideFpsOverlayInteractor
-import com.android.systemui.biometrics.domain.interactor.SideFpsOverlayInteractorImpl
import com.android.systemui.dagger.SysUISingleton
import dagger.Binds
import dagger.Module
@@ -49,10 +47,4 @@
@Binds
@SysUISingleton
fun bindsLogContextInteractor(impl: LogContextInteractorImpl): LogContextInteractor
-
- @Binds
- @SysUISingleton
- fun providesSideFpsOverlayInteractor(
- impl: SideFpsOverlayInteractorImpl
- ): SideFpsOverlayInteractor
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt
index f36a3ec..a317a06 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt
@@ -54,6 +54,9 @@
/** Current rotation of the display */
val currentRotation: StateFlow<DisplayRotation>
+ /** Display change event indicating a change to the given displayId has occurred. */
+ val displayChanges: Flow<Int>
+
/** Called on configuration changes, used to keep the display state in sync */
fun onConfigurationChanged(newConfig: Configuration)
}
@@ -74,6 +77,8 @@
screenSizeFoldProvider = foldProvider
}
+ override val displayChanges = displayRepository.displayChangeEvent
+
override val isFolded: Flow<Boolean> =
conflatedCallbackFlow {
val sendFoldStateUpdate = { state: Boolean ->
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractor.kt
deleted file mode 100644
index 75ae061..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractor.kt
+++ /dev/null
@@ -1,62 +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.biometrics.domain.interactor
-
-import android.hardware.biometrics.SensorLocationInternal
-import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
-import com.android.systemui.dagger.SysUISingleton
-import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.combine
-
-/** Business logic for SideFps overlay offsets. */
-interface SideFpsOverlayInteractor {
-
- /** The displayId of the current display. */
- val displayId: Flow<String>
-
- /** Overlay offsets corresponding to given displayId. */
- val overlayOffsets: Flow<SensorLocationInternal>
-
- /** Called on display changes, used to keep the display state in sync */
- fun onDisplayChanged(displayId: String)
-}
-
-@SysUISingleton
-class SideFpsOverlayInteractorImpl
-@Inject
-constructor(fingerprintPropertyRepository: FingerprintPropertyRepository) :
- SideFpsOverlayInteractor {
-
- private val _displayId: MutableStateFlow<String> = MutableStateFlow("")
- override val displayId: Flow<String> = _displayId.asStateFlow()
-
- override val overlayOffsets: Flow<SensorLocationInternal> =
- combine(displayId, fingerprintPropertyRepository.sensorLocations) { displayId, offsets ->
- offsets[displayId] ?: SensorLocationInternal.DEFAULT
- }
-
- override fun onDisplayChanged(displayId: String) {
- _displayId.value = displayId
- }
-
- companion object {
- private const val TAG = "SideFpsOverlayInteractorImpl"
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt
new file mode 100644
index 0000000..f85203e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt
@@ -0,0 +1,136 @@
+/*
+ * 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.biometrics.domain.interactor
+
+import android.content.Context
+import android.hardware.biometrics.SensorLocationInternal
+import android.view.WindowManager
+import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
+import com.android.systemui.biometrics.domain.model.SideFpsSensorLocation
+import com.android.systemui.biometrics.shared.model.DisplayRotation
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
+import com.android.systemui.biometrics.shared.model.isDefaultOrientation
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class SideFpsSensorInteractor
+@Inject
+constructor(
+ private val context: Context,
+ fingerprintPropertyRepository: FingerprintPropertyRepository,
+ windowManager: WindowManager,
+ displayStateInteractor: DisplayStateInteractor,
+ featureFlags: FeatureFlagsClassic,
+) {
+
+ private val sensorForCurrentDisplay =
+ combine(
+ displayStateInteractor.displayChanges,
+ fingerprintPropertyRepository.sensorLocations,
+ ::Pair
+ )
+ .map { (_, locations) -> locations[context.display?.uniqueId] }
+ .filterNotNull()
+
+ val isAvailable: Flow<Boolean> =
+ fingerprintPropertyRepository.sensorType.map { it == FingerprintSensorType.POWER_BUTTON }
+
+ val authenticationDuration: Flow<Long> =
+ flowOf(context.resources?.getInteger(R.integer.config_restToUnlockDuration)?.toLong() ?: 0L)
+
+ val isProlongedTouchRequiredForAuthentication: Flow<Boolean> =
+ isAvailable.flatMapLatest { sfpsAvailable ->
+ if (sfpsAvailable) {
+ // todo (b/305236201) also add the settings check here.
+ flowOf(featureFlags.isEnabled(Flags.REST_TO_UNLOCK))
+ } else {
+ flowOf(false)
+ }
+ }
+
+ val sensorLocation: Flow<SideFpsSensorLocation> =
+ combine(displayStateInteractor.currentRotation, sensorForCurrentDisplay, ::Pair).map {
+ (rotation, sensorLocation: SensorLocationInternal) ->
+ val isSensorVerticalInDefaultOrientation = sensorLocation.sensorLocationY != 0
+ // device dimensions in the current rotation
+ val size = windowManager.maximumWindowMetrics.bounds
+ val isDefaultOrientation = rotation.isDefaultOrientation()
+ // Width and height are flipped is device is not in rotation_0 or rotation_180
+ // Flipping it to the width and height of the device in default orientation.
+ val displayWidth = if (isDefaultOrientation) size.width() else size.height()
+ val displayHeight = if (isDefaultOrientation) size.height() else size.width()
+ val sensorWidth = context.resources?.getInteger(R.integer.config_sfpsSensorWidth) ?: 0
+
+ val (sensorLeft, sensorTop) =
+ if (isSensorVerticalInDefaultOrientation) {
+ when (rotation) {
+ DisplayRotation.ROTATION_0 -> {
+ Pair(displayWidth, sensorLocation.sensorLocationY)
+ }
+ DisplayRotation.ROTATION_90 -> {
+ Pair(sensorLocation.sensorLocationY, 0)
+ }
+ DisplayRotation.ROTATION_180 -> {
+ Pair(0, displayHeight - sensorLocation.sensorLocationY - sensorWidth)
+ }
+ DisplayRotation.ROTATION_270 -> {
+ Pair(
+ displayHeight - sensorLocation.sensorLocationY - sensorWidth,
+ displayWidth
+ )
+ }
+ }
+ } else {
+ when (rotation) {
+ DisplayRotation.ROTATION_0 -> {
+ Pair(sensorLocation.sensorLocationX, 0)
+ }
+ DisplayRotation.ROTATION_90 -> {
+ Pair(0, displayWidth - sensorLocation.sensorLocationX - sensorWidth)
+ }
+ DisplayRotation.ROTATION_180 -> {
+ Pair(
+ displayWidth - sensorLocation.sensorLocationX - sensorWidth,
+ displayHeight
+ )
+ }
+ DisplayRotation.ROTATION_270 -> {
+ Pair(displayHeight, sensorLocation.sensorLocationX)
+ }
+ }
+ }
+
+ SideFpsSensorLocation(
+ left = sensorLeft,
+ top = sensorTop,
+ width = sensorWidth,
+ isSensorVerticalInDefaultOrientation = isSensorVerticalInDefaultOrientation
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/SideFpsSensorLocation.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/SideFpsSensorLocation.kt
new file mode 100644
index 0000000..35f8e3b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/SideFpsSensorLocation.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.biometrics.domain.model
+
+data class SideFpsSensorLocation(
+ /** Pixel offset from the left of the screen */
+ val left: Int,
+ /** Pixel offset from the top of the screen */
+ val top: Int,
+ /** Width in pixels of the SFPS sensor */
+ val width: Int,
+ /**
+ * Whether the sensor is vertical when the device is in its default orientation (Rotation_0 or
+ * Rotation_180)
+ */
+ val isSensorVerticalInDefaultOrientation: Boolean
+)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/DisplayRotation.kt b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/DisplayRotation.kt
index 10a3e91..336404c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/DisplayRotation.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/DisplayRotation.kt
@@ -10,6 +10,9 @@
ROTATION_270,
}
+fun DisplayRotation.isDefaultOrientation() =
+ this == DisplayRotation.ROTATION_0 || this == DisplayRotation.ROTATION_180
+
/** Converts [Surface.Rotation] to corresponding [DisplayRotation] */
fun Int.toDisplayRotation(): DisplayRotation =
when (this) {
@@ -19,3 +22,12 @@
Surface.ROTATION_270 -> DisplayRotation.ROTATION_270
else -> throw IllegalArgumentException("Invalid DisplayRotation value: $this")
}
+
+/** Converts [DisplayRotation] to corresponding [Surface.Rotation] */
+fun DisplayRotation.toRotation(): Int =
+ when (this) {
+ DisplayRotation.ROTATION_0 -> Surface.ROTATION_0
+ DisplayRotation.ROTATION_90 -> Surface.ROTATION_90
+ DisplayRotation.ROTATION_180 -> Surface.ROTATION_180
+ DisplayRotation.ROTATION_270 -> Surface.ROTATION_270
+ }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java
index c0ee71c..0dfaf0f 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java
@@ -18,8 +18,15 @@
import android.view.MotionEvent;
+import javax.inject.Inject;
+
/** */
public class FalsingCollectorFake implements FalsingCollector {
+
+ @Inject
+ public FalsingCollectorFake() {
+ }
+
@Override
public void onSuccessfulUnlock() {
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
index f6e0296..53b6879 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
@@ -15,7 +15,10 @@
class CommunalRepositoryImpl
@Inject
constructor(
- featureFlags: FeatureFlagsClassic,
+ private val featureFlags: FeatureFlagsClassic,
) : CommunalRepository {
- override val isCommunalEnabled = featureFlags.isEnabled(Flags.COMMUNAL_SERVICE_ENABLED)
+ override val isCommunalEnabled: Boolean
+ get() =
+ featureFlags.isEnabled(Flags.COMMUNAL_SERVICE_ENABLED) &&
+ featureFlags.isEnabled(Flags.COMMUNAL_HUB)
}
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/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 9fb8da3..04bb6ae 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -28,11 +28,13 @@
class CommunalInteractor
@Inject
constructor(
- communalRepository: CommunalRepository,
+ private val communalRepository: CommunalRepository,
widgetRepository: CommunalWidgetRepository,
) {
+
/** Whether communal features are enabled. */
- val isCommunalEnabled: Boolean = communalRepository.isCommunalEnabled
+ val isCommunalEnabled: Boolean
+ get() = communalRepository.isCommunalEnabled
/** A flow of info about the widget to be displayed, or null if widget is unavailable. */
val appWidgetInfo: Flow<CommunalAppWidgetInfo?> = widgetRepository.stopwatchAppWidgetInfo
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/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 04a9cae..d57f31f 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -67,6 +67,7 @@
import android.media.AudioManager;
import android.media.IAudioService;
import android.media.MediaRouter2Manager;
+import android.media.projection.IMediaProjectionManager;
import android.media.projection.MediaProjectionManager;
import android.media.session.MediaSessionManager;
import android.net.ConnectivityManager;
@@ -414,6 +415,13 @@
}
@Provides
+ @Singleton
+ static IMediaProjectionManager provideIMediaProjectionManager() {
+ return IMediaProjectionManager.Stub.asInterface(
+ ServiceManager.getService(Context.MEDIA_PROJECTION_SERVICE));
+ }
+
+ @Provides
static MediaRouter2Manager provideMediaRouter2Manager(Context context) {
return MediaRouter2Manager.getInstance(context);
}
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/ReferenceSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
index 046ccf16..a90980f 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
@@ -18,7 +18,6 @@
import com.android.systemui.globalactions.ShutdownUiModule;
import com.android.systemui.keyguard.CustomizationProvider;
-import com.android.systemui.shade.ShadeModule;
import com.android.systemui.statusbar.NotificationInsetsModule;
import com.android.systemui.statusbar.QsFrameTranslateModule;
@@ -33,7 +32,6 @@
DependencyProvider.class,
NotificationInsetsModule.class,
QsFrameTranslateModule.class,
- ShadeModule.class,
ShutdownUiModule.class,
SystemUIBinder.class,
SystemUIModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 9e5fd55..1dd4abf 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -21,12 +21,9 @@
import android.content.Context;
import android.hardware.SensorPrivacyManager;
-import android.os.Handler;
-import com.android.internal.logging.UiEventLogger;
import com.android.keyguard.KeyguardViewController;
import com.android.systemui.battery.BatterySaverModule;
-import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerImpl;
import com.android.systemui.doze.DozeHost;
@@ -34,7 +31,6 @@
import com.android.systemui.navigationbar.NavigationBarControllerModule;
import com.android.systemui.navigationbar.gestural.GestureModule;
import com.android.systemui.plugins.qs.QSFactory;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.dagger.PowerModule;
import com.android.systemui.qs.dagger.QSModule;
import com.android.systemui.qs.tileimpl.QSFactoryImpl;
@@ -45,7 +41,7 @@
import com.android.systemui.screenshot.ReferenceScreenshotModule;
import com.android.systemui.settings.dagger.MultiUserUtilsModule;
import com.android.systemui.shade.NotificationShadeWindowControllerImpl;
-import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.shade.ShadeModule;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyboardShortcutsModule;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -53,20 +49,13 @@
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.dagger.StartCentralSurfacesModule;
import com.android.systemui.statusbar.events.StatusBarEventsModule;
-import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
-import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.phone.DozeServiceHost;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.phone.HeadsUpModule;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragmentStartableModule;
-import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.AospPolicyModule;
-import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyControllerImpl;
import com.android.systemui.statusbar.policy.SensorPrivacyController;
@@ -100,11 +89,13 @@
BatterySaverModule.class,
CollapsedStatusBarFragmentStartableModule.class,
GestureModule.class,
+ HeadsUpModule.class,
MediaModule.class,
MultiUserUtilsModule.class,
NavigationBarControllerModule.class,
PowerModule.class,
QSModule.class,
+ ShadeModule.class,
ReferenceScreenshotModule.class,
RotationLockModule.class,
SceneContainerFrameworkModule.class,
@@ -161,38 +152,6 @@
return true;
}
- @SysUISingleton
- @Provides
- static HeadsUpManagerPhone provideHeadsUpManagerPhone(
- Context context,
- HeadsUpManagerLogger headsUpManagerLogger,
- StatusBarStateController statusBarStateController,
- KeyguardBypassController bypassController,
- GroupMembershipManager groupManager,
- VisualStabilityProvider visualStabilityProvider,
- ConfigurationController configurationController,
- @Main Handler handler,
- AccessibilityManagerWrapper accessibilityManagerWrapper,
- UiEventLogger uiEventLogger,
- ShadeExpansionStateManager shadeExpansionStateManager) {
- return new HeadsUpManagerPhone(
- context,
- headsUpManagerLogger,
- statusBarStateController,
- bypassController,
- groupManager,
- visualStabilityProvider,
- configurationController,
- handler,
- accessibilityManagerWrapper,
- uiEventLogger,
- shadeExpansionStateManager
- );
- }
-
- @Binds
- abstract HeadsUpManager bindHeadsUpManagerPhone(HeadsUpManagerPhone headsUpManagerPhone);
-
@Provides
@SysUISingleton
static Recents provideRecents(Context context, RecentsImplementation recentsImplementation,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index d89332d..5d6949b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -37,6 +37,7 @@
import com.android.systemui.keyguard.KeyguardViewConfigurator
import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.keyguard.data.quickaffordance.MuteQuickAffordanceCoreStartable
+import com.android.systemui.keyguard.ui.binder.AlternateBouncerBinder
import com.android.systemui.keyguard.ui.binder.KeyguardDismissActionBinder
import com.android.systemui.keyguard.ui.binder.KeyguardDismissBinder
import com.android.systemui.log.SessionTracker
@@ -92,6 +93,11 @@
@ClassKey(AuthController::class)
abstract fun bindAuthController(service: AuthController): CoreStartable
+ @Binds
+ @IntoMap
+ @ClassKey(AlternateBouncerBinder::class)
+ abstract fun bindAlternateBouncerBinder(impl: AlternateBouncerBinder): CoreStartable
+
/** Inject into BiometricNotificationService */
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 4b6ad6d..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;
@@ -56,6 +55,7 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.FlagsModule;
import com.android.systemui.keyboard.KeyboardModule;
+import com.android.systemui.keyevent.data.repository.KeyEventRepositoryModule;
import com.android.systemui.keyguard.ui.view.layout.blueprints.KeyguardBlueprintModule;
import com.android.systemui.log.dagger.LogModule;
import com.android.systemui.log.dagger.MonitorLog;
@@ -181,6 +181,7 @@
FalsingModule.class,
FlagsModule.class,
FooterActionsModule.class,
+ KeyEventRepositoryModule.class,
KeyboardModule.class,
KeyguardBlueprintModule.class,
LetterboxModule.class,
@@ -298,9 +299,6 @@
abstract UdfpsDisplayModeProvider optionalUdfpsDisplayModeProvider();
@BindsOptionalOf
- abstract AlternateUdfpsTouchProvider optionalUdfpsTouchProvider();
-
- @BindsOptionalOf
abstract FingerprintInteractiveToAuthProvider optionalFingerprintInteractiveToAuthProvider();
@BindsOptionalOf
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt
index 5b85ad0..1e29e1f 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt
@@ -2,7 +2,7 @@
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
@@ -51,7 +51,7 @@
* When this is `false`, an automatically-triggered face unlock shouldn't automatically dismiss
* the lockscreen.
*/
- fun isBypassEnabled(): Boolean
+ val isBypassEnabled: StateFlow<Boolean>
}
/** Encapsulates application state for device entry. */
@@ -68,7 +68,7 @@
) : DeviceEntryRepository {
override val isUnlocked =
- ConflatedCallbackFlow.conflatedCallbackFlow {
+ conflatedCallbackFlow {
val callback =
object : KeyguardStateController.Callback {
override fun onUnlockedChanged() {
@@ -112,7 +112,24 @@
}
}
- override fun isBypassEnabled() = keyguardBypassController.bypassEnabled
+ override val isBypassEnabled: StateFlow<Boolean> =
+ conflatedCallbackFlow {
+ val listener =
+ object : KeyguardBypassController.OnBypassStateChangedListener {
+ override fun onBypassStateChanged(isEnabled: Boolean) {
+ trySend(isEnabled)
+ }
+ }
+ keyguardBypassController.registerOnBypassStateChangedListener(listener)
+ awaitClose {
+ keyguardBypassController.unregisterOnBypassStateChangedListener(listener)
+ }
+ }
+ .stateIn(
+ applicationScope,
+ SharingStarted.Eagerly,
+ initialValue = keyguardBypassController.bypassEnabled,
+ )
companion object {
private const val TAG = "DeviceEntryRepositoryImpl"
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
index 5612c9a..e96e318 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
@@ -106,5 +106,5 @@
* authentication challenge via face unlock or fingerprint sensor can automatically bypass the
* lock screen.
*/
- fun isBypassEnabled() = repository.isBypassEnabled()
+ val isBypassEnabled: StateFlow<Boolean> = repository.isBypassEnabled
}
diff --git a/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialog.kt b/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialog.kt
index 7510cf6c..d19efbd 100644
--- a/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialog.kt
@@ -15,14 +15,12 @@
*/
package com.android.systemui.display.ui.view
-import android.app.Dialog
import android.content.Context
import android.os.Bundle
-import android.view.Gravity
import android.view.View
-import android.view.WindowManager
import android.widget.TextView
import com.android.systemui.res.R
+import com.android.systemui.statusbar.phone.SystemUIBottomSheetDialog
/**
* Dialog used to decide what to do with a connected display.
@@ -35,7 +33,7 @@
private val onStartMirroringClickListener: View.OnClickListener,
private val onCancelMirroring: View.OnClickListener,
theme: Int = R.style.Theme_SystemUI_Dialog,
-) : Dialog(context, theme) {
+) : SystemUIBottomSheetDialog(context, theme) {
private lateinit var mirrorButton: TextView
private lateinit var dismissButton: TextView
@@ -43,13 +41,8 @@
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- window?.apply {
- setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL)
- addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS)
- setGravity(Gravity.BOTTOM)
- }
setContentView(R.layout.connected_display_dialog)
- setCanceledOnTouchOutside(true)
+
mirrorButton =
requireViewById<TextView>(R.id.enable_display).apply {
setOnClickListener(onStartMirroringClickListener)
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java
index 14db14e..1c2ff4b 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;
@@ -57,15 +56,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
@@ -322,8 +321,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) {
@@ -357,8 +355,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 63f1dda..b7c3662 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -147,11 +147,6 @@
"lockscreen_custom_clocks"
)
- // TODO(b/275694445): Tracking Bug
- @JvmField
- val LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING =
- releasedFlag("lockscreen_without_secure_lock_when_dreaming")
-
// TODO(b/286092087): Tracking Bug
@JvmField
val ENABLE_SYSTEM_UI_DREAM_CONTROLLER = unreleasedFlag("enable_system_ui_dream_controller")
@@ -312,6 +307,11 @@
@JvmField
val WALLPAPER_PICKER_PREVIEW_ANIMATION = releasedFlag("wallpaper_picker_preview_animation")
+ /** Flag to enable rest to unlock feature. */
+ // TODO(b/303672286): Tracking bug
+ @JvmField
+ val REST_TO_UNLOCK: UnreleasedFlag = unreleasedFlag("rest_to_unlock")
+
/**
* TODO(b/278086361): Tracking bug
* Complete rewrite of the interactions between System UI and Window Manager involving keyguard
@@ -334,7 +334,7 @@
/** Flag to use a separate view for the alternate bouncer. */
// TODO(b/300440924): Tracking bug
@JvmField
- val ALTERNATE_BOUNCER_REFACTOR: UnreleasedFlag = unreleasedFlag("alternate_bouncer_view")
+ val ALTERNATE_BOUNCER_VIEW: UnreleasedFlag = unreleasedFlag("alternate_bouncer_view")
// 300 - power menu
// TODO(b/254512600): Tracking Bug
@@ -404,6 +404,9 @@
@JvmField val SIGNAL_CALLBACK_DEPRECATION =
unreleasedFlag("signal_callback_deprecation", teamfood = true)
+ // TODO(b/301610137): Tracking bug
+ @JvmField val NEW_NETWORK_SLICE_UI = unreleasedFlag("new_network_slice_ui", teamfood = true)
+
// TODO(b/265892345): Tracking Bug
val PLUG_IN_STATUS_BAR_CHIP = releasedFlag("plug_in_status_bar_chip")
@@ -476,6 +479,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")
@@ -670,8 +676,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")
@@ -789,8 +793,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/flags/ViewRefactorFlag.kt b/packages/SystemUI/src/com/android/systemui/flags/RefactorFlag.kt
similarity index 71%
rename from packages/SystemUI/src/com/android/systemui/flags/ViewRefactorFlag.kt
rename to packages/SystemUI/src/com/android/systemui/flags/RefactorFlag.kt
index eaecda5..3fe6806 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/ViewRefactorFlag.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/RefactorFlag.kt
@@ -28,27 +28,23 @@
* flag-disabled builds, but with a check that should crash eng builds or tests when the
* expectation is violated.
*
- * The constructors prefer that you provide a [FeatureFlags] instance, but does not require it,
+ * The constructors require that you provide a [FeatureFlags] instance. If you're using this in a
+ * View class, it's acceptable to ue the [forView] constructor methods, which do not require one,
* falling back to [Dependency.get]. This fallback should ONLY be used to flag-guard code changes
- * inside views where injecting flag values after initialization can be error-prone.
+ * inside Views where injecting flag values after initialization can be error-prone.
*/
-class ViewRefactorFlag
+class RefactorFlag
private constructor(
private val injectedFlags: FeatureFlags?,
- private val flag: BooleanFlag,
+ private val flagName: Any,
private val readFlagValue: (FeatureFlags) -> Boolean
) {
- @JvmOverloads
constructor(
- flags: FeatureFlags? = null,
+ flags: FeatureFlags,
flag: UnreleasedFlag
) : this(flags, flag, { it.isEnabled(flag) })
- @JvmOverloads
- constructor(
- flags: FeatureFlags? = null,
- flag: ReleasedFlag
- ) : this(flags, flag, { it.isEnabled(flag) })
+ constructor(flags: FeatureFlags, flag: ReleasedFlag) : this(flags, flag, { it.isEnabled(flag) })
/** Whether the flag is enabled. Called to switch between an old behavior and a new behavior. */
val isEnabled by lazy {
@@ -69,7 +65,8 @@
* }
* ````
*/
- fun assertDisabled() = check(!isEnabled) { "Code path not supported when $flag is enabled." }
+ fun assertDisabled() =
+ check(!isEnabled) { "Code path not supported when $flagName is enabled." }
/**
* Called to ensure code is only run when the flag is enabled. This protects users from the
@@ -87,13 +84,25 @@
*/
fun expectEnabled(): Boolean {
if (!isEnabled) {
- val message = "Code path not supported when $flag is disabled."
+ val message = "Code path not supported when $flagName is disabled."
Log.wtf(TAG, message, Exception(message))
}
return isEnabled
}
- private companion object {
- private const val TAG = "ViewRefactorFlag"
+ companion object {
+ private const val TAG = "RefactorFlag"
+
+ /** Construct a [RefactorFlag] within View construction where injection is impossible. */
+ @JvmStatic
+ @JvmOverloads
+ fun forView(flag: UnreleasedFlag, flags: FeatureFlags? = null) =
+ RefactorFlag(flags, flag) { it.isEnabled(flag) }
+
+ /** Construct a [RefactorFlag] within View construction where injection is impossible. */
+ @JvmStatic
+ @JvmOverloads
+ fun forView(flag: ReleasedFlag, flags: FeatureFlags? = null) =
+ RefactorFlag(flags, flag) { it.isEnabled(flag) }
}
}
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/keyevent/data/repository/KeyEventRepository.kt b/packages/SystemUI/src/com/android/systemui/keyevent/data/repository/KeyEventRepository.kt
new file mode 100644
index 0000000..5bc5d0b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyevent/data/repository/KeyEventRepository.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.keyevent.data.repository
+
+import android.view.KeyEvent
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.CommandQueue
+import javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+
+/** Defines interface for classes that encapsulate application state for key event presses. */
+interface KeyEventRepository {
+ /** Observable for whether the power button key is pressed/down or not. */
+ val isPowerButtonDown: Flow<Boolean>
+}
+
+@SysUISingleton
+class KeyEventRepositoryImpl
+@Inject
+constructor(
+ val commandQueue: CommandQueue,
+) : KeyEventRepository {
+ override val isPowerButtonDown: Flow<Boolean> = conflatedCallbackFlow {
+ val callback =
+ object : CommandQueue.Callbacks {
+ override fun handleSystemKey(event: KeyEvent) {
+ if (event.keyCode == KeyEvent.KEYCODE_POWER) {
+ trySendWithFailureLogging(event.isDown, TAG, "updated isPowerButtonDown")
+ }
+ }
+ }
+ commandQueue.addCallback(callback)
+ awaitClose { commandQueue.removeCallback(callback) }
+ }
+
+ companion object {
+ private const val TAG = "KeyEventRepositoryImpl"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyevent/data/repository/KeyEventRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/keyevent/data/repository/KeyEventRepositoryModule.kt
new file mode 100644
index 0000000..afba5db
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyevent/data/repository/KeyEventRepositoryModule.kt
@@ -0,0 +1,25 @@
+/*
+ * 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.keyevent.data.repository
+
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface KeyEventRepositoryModule {
+ @Binds fun keyEventRepository(impl: KeyEventRepositoryImpl): KeyEventRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractor.kt
index 3f2f67db..9949fa5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractor.kt
@@ -13,55 +13,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package com.android.systemui.keyevent.domain.interactor
-import android.view.KeyEvent
-import com.android.systemui.back.domain.interactor.BackActionInteractor
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.domain.interactor.KeyguardKeyEventInteractor
+import com.android.systemui.keyevent.data.repository.KeyEventRepository
import javax.inject.Inject
/**
- * Sends key events to the appropriate interactors and then acts upon key events that haven't
- * already been handled but should be handled by SystemUI.
+ * Business logic for all key event state. This includes all key events, regardless of whether
+ * they've been handled or not by a consumer.
+ *
+ * For key events that SysUI wants to properly handle, see [SysUIKeyEventHandler].
*/
@SysUISingleton
class KeyEventInteractor
@Inject
constructor(
- private val backActionInteractor: BackActionInteractor,
- private val keyguardKeyEventInteractor: KeyguardKeyEventInteractor,
+ repository: KeyEventRepository,
) {
- fun dispatchKeyEvent(event: KeyEvent): Boolean {
- if (keyguardKeyEventInteractor.dispatchKeyEvent(event)) {
- return true
- }
-
- when (event.keyCode) {
- KeyEvent.KEYCODE_BACK -> {
- if (event.handleAction()) {
- backActionInteractor.onBackRequested()
- }
- return true
- }
- }
- return false
- }
-
- fun interceptMediaKey(event: KeyEvent): Boolean {
- return keyguardKeyEventInteractor.interceptMediaKey(event)
- }
-
- fun dispatchKeyEventPreIme(event: KeyEvent): Boolean {
- return keyguardKeyEventInteractor.dispatchKeyEventPreIme(event)
- }
-
- companion object {
- // Most actions shouldn't be handled on the down event and instead handled on subsequent
- // key events like ACTION_UP.
- fun KeyEvent.handleAction(): Boolean {
- return action != KeyEvent.ACTION_DOWN
- }
- }
+ val isPowerButtonDown = repository.isPowerButtonDown
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyevent/domain/interactor/SysUIKeyEventHandler.kt b/packages/SystemUI/src/com/android/systemui/keyevent/domain/interactor/SysUIKeyEventHandler.kt
new file mode 100644
index 0000000..1febc79
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyevent/domain/interactor/SysUIKeyEventHandler.kt
@@ -0,0 +1,69 @@
+/*
+ * 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.keyevent.domain.interactor
+
+import android.view.KeyEvent
+import com.android.systemui.back.domain.interactor.BackActionInteractor
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.KeyguardKeyEventInteractor
+import javax.inject.Inject
+
+/**
+ * Sends key events to the appropriate interactors and then acts upon key events that haven't
+ * already been handled but should be handled by SystemUI.
+ *
+ * To observe any key event states, see [KeyEventInteractor].
+ */
+@SysUISingleton
+class SysUIKeyEventHandler
+@Inject
+constructor(
+ private val backActionInteractor: BackActionInteractor,
+ private val keyguardKeyEventInteractor: KeyguardKeyEventInteractor,
+) {
+ fun dispatchKeyEvent(event: KeyEvent): Boolean {
+ if (keyguardKeyEventInteractor.dispatchKeyEvent(event)) {
+ return true
+ }
+
+ when (event.keyCode) {
+ KeyEvent.KEYCODE_BACK -> {
+ if (event.handleAction()) {
+ backActionInteractor.onBackRequested()
+ }
+ return true
+ }
+ }
+ return false
+ }
+
+ fun interceptMediaKey(event: KeyEvent): Boolean {
+ return keyguardKeyEventInteractor.interceptMediaKey(event)
+ }
+
+ fun dispatchKeyEventPreIme(event: KeyEvent): Boolean {
+ return keyguardKeyEventInteractor.dispatchKeyEventPreIme(event)
+ }
+
+ companion object {
+ // Most actions shouldn't be handled on the down event and instead handled on subsequent
+ // key events like ACTION_UP.
+ fun KeyEvent.handleAction(): Boolean {
+ return action != KeyEvent.ACTION_DOWN
+ }
+ }
+}
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/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index fde92b8..0bac40b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -451,7 +451,8 @@
if (!keyguardStateController.isKeyguardGoingAway &&
willUnlockWithInWindowLauncherAnimations) {
try {
- launcherUnlockController?.setUnlockAmount(1f, true /* forceIfAnimating */)
+ launcherUnlockController?.setUnlockAmount(1f,
+ biometricUnlockControllerLazy.get().isWakeAndUnlock /* forceIfAnimating */)
} catch (e: DeadObjectException) {
Log.e(TAG, "launcherUnlockAnimationController was dead, but non-null in " +
"onKeyguardGoingAwayChanged(). Catching exception as this should mean " +
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index f500017..86bf368 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -82,7 +82,8 @@
val statusViewComponent =
keyguardStatusViewComponentFactory.build(
LayoutInflater.from(context).inflate(R.layout.keyguard_status_view, null)
- as KeyguardStatusView
+ as KeyguardStatusView,
+ context.display
)
val controller = statusViewComponent.keyguardStatusViewController
controller.init()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 7678f4d..39742a0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -171,6 +171,8 @@
import com.android.systemui.wallpapers.data.repository.WallpaperRepository;
import com.android.wm.shell.keyguard.KeyguardTransitions;
+import dagger.Lazy;
+
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -180,7 +182,8 @@
import java.util.concurrent.Executor;
import java.util.function.Consumer;
-import dagger.Lazy;
+
+
import kotlinx.coroutines.CoroutineDispatcher;
/**
@@ -1933,11 +1936,7 @@
public void onDreamingStarted() {
mUpdateMonitor.dispatchDreamingStarted();
synchronized (this) {
- final boolean alwaysShowKeyguard =
- mFeatureFlags.isEnabled(Flags.LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING);
- if (mDeviceInteractive
- && (alwaysShowKeyguard ||
- mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser()))) {
+ if (mDeviceInteractive) {
doKeyguardLaterLocked();
}
}
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 03c166c..081edd1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -28,6 +28,7 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardViewController;
import com.android.keyguard.ViewMediatorCallback;
+import com.android.keyguard.dagger.KeyguardDisplayModule;
import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent;
import com.android.keyguard.dagger.KeyguardStatusBarViewComponent;
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
@@ -38,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;
@@ -95,11 +97,13 @@
KeyguardUserSwitcherComponent.class},
includes = {
CommunalRepositoryModule.class,
+ CommunalTutorialRepositoryModule.class,
CommunalWidgetRepositoryModule.class,
FalsingModule.class,
KeyguardDataQuickAffordanceModule.class,
KeyguardRepositoryModule.class,
KeyguardFaceAuthModule.class,
+ KeyguardDisplayModule.class,
StartKeyguardTransitionModule.class,
ResourceTrimmerModule.class,
})
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
index 3ccf446..d06f31f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
@@ -32,6 +32,7 @@
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
@SysUISingleton
class FromAlternateBouncerTransitionInteractor
@@ -129,11 +130,11 @@
override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator {
return ValueAnimator().apply {
interpolator = Interpolators.LINEAR
- duration = TRANSITION_DURATION_MS
+ duration = TRANSITION_DURATION_MS.inWholeMilliseconds
}
}
companion object {
- private const val TRANSITION_DURATION_MS = 300L
+ val TRANSITION_DURATION_MS = 300.milliseconds
}
}
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/KeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt
index e57c919..89aca76 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt
@@ -61,6 +61,7 @@
fun onSwipeUpOnBouncer()
fun onPrimaryBouncerUserInput()
fun onAccessibilityAction()
+ fun onWalletLaunched()
}
/**
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 d6987629..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
@@ -21,7 +21,7 @@
import android.view.KeyEvent
import com.android.systemui.back.domain.interactor.BackActionInteractor
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor.Companion.handleAction
+import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler.Companion.handleAction
import com.android.systemui.media.controls.util.MediaSessionLegacyHelperWrapper
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.domain.interactor.PowerInteractor
@@ -37,7 +37,6 @@
constructor(
private val context: Context,
private val statusBarStateController: StatusBarStateController,
- private val keyguardInteractor: KeyguardInteractor,
private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
private val shadeController: ShadeController,
private val mediaSessionLegacyHelperWrapper: MediaSessionLegacyHelperWrapper,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
index 596a1c0..f38bb2b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
@@ -61,4 +61,5 @@
override fun onSwipeUpOnBouncer() {}
override fun onPrimaryBouncerUserInput() {}
override fun onAccessibilityAction() {}
+ override fun onWalletLaunched() = Unit
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
index ef1d5ac..797dec2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
@@ -24,6 +24,7 @@
import com.android.systemui.CoreStartable
import com.android.systemui.biometrics.data.repository.FacePropertyRepository
import com.android.systemui.biometrics.shared.model.LockoutMode
+import com.android.systemui.biometrics.shared.model.SensorStrength
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dagger.SysUISingleton
@@ -33,7 +34,6 @@
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
-import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.TransitionState
@@ -44,6 +44,7 @@
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.kotlin.pairwise
import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
@@ -56,7 +57,6 @@
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.yield
-import javax.inject.Inject
/**
* Encapsulates business logic related face authentication being triggered for device entry from
@@ -79,7 +79,6 @@
private val deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
private val userRepository: UserRepository,
private val facePropertyRepository: FacePropertyRepository,
- private val keyguardRepository: KeyguardRepository,
private val faceWakeUpTriggersConfig: FaceWakeUpTriggersConfig,
private val powerInteractor: PowerInteractor,
) : CoreStartable, KeyguardFaceAuthInteractor {
@@ -207,6 +206,12 @@
runFaceAuth(FaceAuthUiEvent.FACE_AUTH_ACCESSIBILITY_ACTION, false)
}
+ override fun onWalletLaunched() {
+ if (facePropertyRepository.sensorInfo.value?.strength == SensorStrength.STRONG) {
+ runFaceAuth(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_OCCLUDING_APP_REQUESTED, true)
+ }
+ }
+
override fun registerListener(listener: FaceAuthenticationListener) {
listeners.add(listener)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
new file mode 100644
index 0000000..41af9e8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.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.keyguard.ui.binder
+
+import android.view.View
+import android.view.ViewGroup
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.res.R
+import com.android.systemui.scrim.ScrimView
+import com.android.systemui.shade.NotificationShadeWindowView
+import com.android.systemui.statusbar.NotificationShadeWindowController
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class AlternateBouncerBinder
+@Inject
+constructor(
+ private val notificationShadeWindowView: NotificationShadeWindowView,
+ private val featureFlags: FeatureFlagsClassic,
+ private val alternateBouncerViewModel: AlternateBouncerViewModel,
+ @Application private val scope: CoroutineScope,
+ private val notificationShadeWindowController: NotificationShadeWindowController,
+) : CoreStartable {
+ override fun start() {
+ if (!featureFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) {
+ return
+ }
+
+ AlternateBouncerViewBinder.bind(
+ notificationShadeWindowView.requireViewById(R.id.alternate_bouncer),
+ alternateBouncerViewModel,
+ scope,
+ notificationShadeWindowController,
+ )
+ }
+}
+
+/**
+ * Binds the alternate bouncer view to its view-model.
+ *
+ * To use this properly, users should maintain a one-to-one relationship between the [View] and the
+ * view-binding, binding each view only once. It is okay and expected for the same instance of the
+ * view-model to be reused for multiple view/view-binder bindings.
+ */
+@ExperimentalCoroutinesApi
+object AlternateBouncerViewBinder {
+
+ /** Binds the view to the view-model, continuing to update the former based on the latter. */
+ @JvmStatic
+ fun bind(
+ view: ViewGroup,
+ viewModel: AlternateBouncerViewModel,
+ scope: CoroutineScope,
+ notificationShadeWindowController: NotificationShadeWindowController,
+ ) {
+ scope.launch {
+ // forcePluginOpen is necessary to show over occluded apps.
+ // This cannot be tied to the view's lifecycle because setting this allows the view
+ // to be started in the first place.
+ viewModel.forcePluginOpen.collect {
+ notificationShadeWindowController.setForcePluginOpen(it, this)
+ }
+ }
+
+ val scrim = view.requireViewById(R.id.alternate_bouncer_scrim) as ScrimView
+ view.repeatWhenAttached { alternateBouncerViewContainer ->
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ scrim.viewAlpha = 0f
+
+ launch {
+ viewModel.onClickListener.collect {
+ // TODO (b/287599719): Support swiping to dismiss altBouncer
+ alternateBouncerViewContainer.setOnClickListener(it)
+ }
+ }
+
+ launch {
+ viewModel.scrimAlpha.collect {
+ alternateBouncerViewContainer.visibility =
+ if (it < .1f) View.INVISIBLE else View.VISIBLE
+ scrim.viewAlpha = it
+ }
+ }
+
+ launch { viewModel.scrimColor.collect { scrim.tint = it } }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index abc30ef..c5a8375 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -47,6 +47,7 @@
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.VibratorHelper
+import com.android.systemui.util.doOnEnd
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -402,6 +403,9 @@
KeyguardBottomAreaVibrations.ShakeAnimationDuration.inWholeMilliseconds
shakeAnimator.interpolator =
CycleInterpolator(KeyguardBottomAreaVibrations.ShakeAnimationCycles)
+ shakeAnimator.doOnEnd {
+ view.translationX = 0f
+ }
shakeAnimator.start()
vibratorHelper?.vibrate(KeyguardBottomAreaVibrations.Shake)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
index f0d118c..99025ace 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
@@ -41,6 +41,7 @@
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.VibratorHelper
+import com.android.systemui.util.doOnEnd
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
@@ -240,6 +241,9 @@
KeyguardBottomAreaVibrations.ShakeAnimationDuration.inWholeMilliseconds
shakeAnimator.interpolator =
CycleInterpolator(KeyguardBottomAreaVibrations.ShakeAnimationCycles)
+ shakeAnimator.doOnEnd {
+ view.translationX = 0f
+ }
shakeAnimator.start()
vibratorHelper?.vibrate(KeyguardBottomAreaVibrations.Shake)
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 5ae2aba..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
@@ -27,6 +27,9 @@
import android.os.Bundle
import android.os.Handler
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
@@ -125,6 +128,8 @@
private val shouldHideClock: Boolean =
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 var host: SurfaceControlViewHost
@@ -164,7 +169,7 @@
host =
SurfaceControlViewHost(
context,
- displayManager.getDisplay(bundle.getInt(KEY_DISPLAY_ID)),
+ displayManager.getDisplay(DEFAULT_DISPLAY),
hostToken,
"KeyguardPreviewRenderer"
)
@@ -174,21 +179,29 @@
fun render() {
mainHandler.post {
- val rootView = FrameLayout(context)
+ val previewContext = display?.let { context.createDisplayContext(it) } ?: context
- setupKeyguardRootView(rootView)
+ val rootView = FrameLayout(previewContext)
+
+ setupKeyguardRootView(previewContext, rootView)
if (!featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
setUpBottomArea(rootView)
}
+ var displayInfo: DisplayInfo? = null
+ display?.let {
+ displayInfo = DisplayInfo()
+ it.getDisplayInfo(displayInfo)
+ }
rootView.measure(
View.MeasureSpec.makeMeasureSpec(
- windowManager.currentWindowMetrics.bounds.width(),
+ displayInfo?.logicalWidth ?: windowManager.currentWindowMetrics.bounds.width(),
View.MeasureSpec.EXACTLY
),
View.MeasureSpec.makeMeasureSpec(
- windowManager.currentWindowMetrics.bounds.height(),
+ displayInfo?.logicalHeight
+ ?: windowManager.currentWindowMetrics.bounds.height(),
View.MeasureSpec.EXACTLY
),
)
@@ -251,7 +264,7 @@
*
* The end padding is as follows: Below clock padding end
*/
- private fun setUpSmartspace(parentView: ViewGroup) {
+ private fun setUpSmartspace(previewContext: Context, parentView: ViewGroup) {
if (
!lockscreenSmartspaceController.isEnabled() ||
!lockscreenSmartspaceController.isDateWeatherDecoupled()
@@ -263,12 +276,12 @@
val topPadding: Int =
KeyguardPreviewSmartspaceViewModel.getLargeClockSmartspaceTopPadding(
- context.resources,
+ previewContext.resources,
)
val startPadding: Int =
- context.resources.getDimensionPixelSize(R.dimen.below_clock_padding_start)
+ previewContext.resources.getDimensionPixelSize(R.dimen.below_clock_padding_start)
val endPadding: Int =
- context.resources.getDimensionPixelSize(R.dimen.below_clock_padding_end)
+ previewContext.resources.getDimensionPixelSize(R.dimen.below_clock_padding_end)
smartSpaceView?.let {
it.setPaddingRelative(startPadding, topPadding, endPadding, 0)
@@ -308,8 +321,8 @@
}
@OptIn(ExperimentalCoroutinesApi::class)
- private fun setupKeyguardRootView(rootView: FrameLayout) {
- val keyguardRootView = KeyguardRootView(context, null).apply { removeAllViews() }
+ private fun setupKeyguardRootView(previewContext: Context, rootView: FrameLayout) {
+ val keyguardRootView = KeyguardRootView(previewContext, null).apply { removeAllViews() }
disposables.add(
KeyguardRootViewBinder.bind(
keyguardRootView,
@@ -333,10 +346,10 @@
if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
setupShortcuts(keyguardRootView)
}
- setUpUdfps(rootView)
+ setUpUdfps(previewContext, rootView)
if (!shouldHideClock) {
- setUpClock(rootView)
+ setUpClock(previewContext, rootView)
KeyguardPreviewClockViewBinder.bind(
largeClockHostView,
smallClockHostView,
@@ -344,7 +357,7 @@
)
}
- setUpSmartspace(rootView)
+ setUpSmartspace(previewContext, rootView)
smartSpaceView?.let {
KeyguardPreviewSmartspaceViewBinder.bind(it, smartspaceViewModel)
}
@@ -381,7 +394,7 @@
}
}
- private fun setUpUdfps(parentView: ViewGroup) {
+ private fun setUpUdfps(previewContext: Context, parentView: ViewGroup) {
val sensorBounds = udfpsOverlayInteractor.udfpsOverlayParams.value.sensorBounds
// If sensorBounds are default rect, then there is no UDFPS
@@ -399,7 +412,7 @@
sensorBounds.bottom
)
val finger =
- LayoutInflater.from(context)
+ LayoutInflater.from(previewContext)
.inflate(
R.layout.udfps_keyguard_preview,
parentView,
@@ -408,12 +421,12 @@
parentView.addView(finger, fingerprintLayoutParams)
}
- private fun setUpClock(parentView: ViewGroup) {
+ private fun setUpClock(previewContext: Context, parentView: ViewGroup) {
largeClockHostView =
if (featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
parentView.requireViewById<FrameLayout>(R.id.lockscreen_clock_view_large)
} else {
- val hostView = FrameLayout(context)
+ val hostView = FrameLayout(previewContext)
hostView.layoutParams =
FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
@@ -429,7 +442,7 @@
parentView.requireViewById<FrameLayout>(R.id.lockscreen_clock_view)
} else {
val resources = parentView.resources
- val hostView = FrameLayout(context)
+ val hostView = FrameLayout(previewContext)
val layoutParams =
FrameLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT,
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/view/layout/sections/DefaultStatusViewSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
index 1a9f021a..0b0f21d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
@@ -31,12 +31,12 @@
import androidx.constraintlayout.widget.ConstraintSet.TOP
import com.android.keyguard.KeyguardStatusView
import com.android.keyguard.dagger.KeyguardStatusViewComponent
-import com.android.systemui.res.R
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.KeyguardViewConfigurator
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.media.controls.ui.KeyguardMediaController
+import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
import com.android.systemui.shade.NotificationPanelViewController
import com.android.systemui.statusbar.policy.SplitShadeStateController
@@ -84,7 +84,8 @@
override fun bindData(constraintLayout: ConstraintLayout) {
if (featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
constraintLayout.findViewById<KeyguardStatusView?>(R.id.keyguard_status_view)?.let {
- val statusViewComponent = keyguardStatusViewComponentFactory.build(it)
+ val statusViewComponent =
+ keyguardStatusViewComponentFactory.build(it, context.display)
val controller = statusViewComponent.keyguardStatusViewController
controller.init()
keyguardMediaController.attachSplitShadeContainer(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
new file mode 100644
index 0000000..235a28d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
@@ -0,0 +1,103 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import android.graphics.Color
+import android.view.View
+import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor.Companion.TRANSITION_DURATION_MS
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
+import com.android.wm.shell.animation.Interpolators
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+
+@ExperimentalCoroutinesApi
+class AlternateBouncerViewModel
+@Inject
+constructor(
+ statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
+ transitionInteractor: KeyguardTransitionInteractor,
+ falsingManager: FalsingManager,
+) {
+ // When we're fully transitioned to the AlternateBouncer, the alpha of the scrim should be:
+ private val alternateBouncerScrimAlpha = .66f
+ private val toAlternateBouncerTransition =
+ KeyguardTransitionAnimationFlow(
+ transitionDuration = TRANSITION_DURATION_MS,
+ transitionFlow = transitionInteractor.anyStateToAlternateBouncerTransition,
+ )
+ .createFlow(
+ duration = TRANSITION_DURATION_MS,
+ onStep = { it },
+ onFinish = { 1f },
+ // Reset on cancel
+ onCancel = { 0f },
+ interpolator = Interpolators.FAST_OUT_SLOW_IN,
+ )
+ private val fromAlternateBouncerTransition =
+ KeyguardTransitionAnimationFlow(
+ transitionDuration = TRANSITION_DURATION_MS,
+ transitionFlow = transitionInteractor.transitionStepsFromState(ALTERNATE_BOUNCER),
+ )
+ .createFlow(
+ duration = TRANSITION_DURATION_MS,
+ onStep = { 1f - it },
+ // Reset on cancel
+ onCancel = { 0f },
+ interpolator = Interpolators.FAST_OUT_SLOW_IN,
+ )
+
+ /** Progress to a fully transitioned alternate bouncer. 1f represents fully transitioned. */
+ private val transitionToAlternateBouncerProgress =
+ merge(fromAlternateBouncerTransition, toAlternateBouncerTransition)
+
+ val forcePluginOpen: Flow<Boolean> =
+ transitionToAlternateBouncerProgress.map { it > 0f }.distinctUntilChanged()
+
+ /** An observable for the scrim alpha. */
+ val scrimAlpha = transitionToAlternateBouncerProgress.map { it * alternateBouncerScrimAlpha }
+
+ /** An observable for the scrim color. Change color for easier debugging. */
+ val scrimColor: Flow<Int> = flowOf(Color.BLACK)
+
+ private val clickListener =
+ View.OnClickListener {
+ if (!falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ statusBarKeyguardViewManager.showPrimaryBouncer(/* scrimmed */ true)
+ }
+ }
+
+ val onClickListener: Flow<View.OnClickListener?> =
+ transitionToAlternateBouncerProgress
+ .map {
+ if (it == 1f) {
+ clickListener
+ } else {
+ null
+ }
+ }
+ .distinctUntilChanged()
+}
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/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
index 54abc5b..0b1079f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
@@ -19,13 +19,13 @@
import android.content.Context
import androidx.annotation.ColorInt
import com.android.settingslib.Utils.getColorAttrDefaultColor
-import com.android.systemui.res.R
import com.android.systemui.keyguard.domain.interactor.BurnInOffsets
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.res.R
import com.android.wm.shell.animation.Interpolators
import javax.inject.Inject
import kotlin.math.roundToInt
@@ -62,7 +62,7 @@
private val toAlternateBouncer: Flow<TransitionViewModel> =
keyguardInteractor.statusBarState.flatMapLatest { statusBarState ->
- transitionInteractor.anyStateToAlternateBouncerTransition.map {
+ transitionInteractor.transitionStepsToState(KeyguardState.ALTERNATE_BOUNCER).map {
TransitionViewModel(
alpha = 1f,
scale =
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/MediaProjectionMetricsLogger.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLogger.kt
new file mode 100644
index 0000000..8634b09
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLogger.kt
@@ -0,0 +1,83 @@
+/*
+ * 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.mediaprojection
+
+import android.media.projection.IMediaProjectionManager
+import android.os.Process
+import android.os.RemoteException
+import android.util.Log
+import com.android.internal.util.FrameworkStatsLog
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/**
+ * Helper class for requesting that the server emit logs describing the MediaProjection setup
+ * experience.
+ */
+@SysUISingleton
+class MediaProjectionMetricsLogger
+@Inject
+constructor(private val service: IMediaProjectionManager) {
+ /**
+ * Request to log that the permission was requested.
+ *
+ * @param sessionCreationSource The entry point requesting permission to capture.
+ */
+ fun notifyPermissionProgress(state: Int, sessionCreationSource: Int) {
+ // TODO check that state & SessionCreationSource matches expected values
+ notifyToServer(state, sessionCreationSource)
+ }
+
+ /**
+ * Request to log that the permission request moved to the given state.
+ *
+ * Should not be used for the initialization state, since that
+ */
+ fun notifyPermissionProgress(state: Int) {
+ // TODO validate state is valid
+ notifyToServer(
+ state,
+ FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN)
+ }
+
+ /**
+ * Notifies system server that we are handling a particular state during the consent flow.
+ *
+ * Only used for emitting atoms.
+ *
+ * @param state The state that SystemUI is handling during the consent flow. Must be a valid
+ * state defined in the MediaProjectionState enum.
+ * @param sessionCreationSource Only set if the state is MEDIA_PROJECTION_STATE_INITIATED.
+ * Indicates the entry point for requesting the permission. Must be a valid state defined in
+ * the SessionCreationSource enum.
+ */
+ private fun notifyToServer(state: Int, sessionCreationSource: Int) {
+ Log.v(TAG, "FOO notifyToServer of state $state and source $sessionCreationSource")
+ try {
+ service.notifyPermissionRequestStateChange(
+ Process.myUid(), state, sessionCreationSource)
+ } catch (e: RemoteException) {
+ Log.e(
+ TAG,
+ "Error notifying server of permission flow state $state from source $sessionCreationSource",
+ e)
+ }
+ }
+
+ companion object {
+ const val TAG = "MediaProjectionMetricsLogger"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
index 72aea04..2217509 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
@@ -33,6 +33,8 @@
import com.android.systemui.mediaprojection.appselector.data.ShellRecentTaskListProvider
import com.android.systemui.mediaprojection.appselector.view.MediaProjectionRecentsViewController
import com.android.systemui.mediaprojection.appselector.view.TaskPreviewSizeProvider
+import com.android.systemui.mediaprojection.appselector.view.WindowMetricsProvider
+import com.android.systemui.mediaprojection.appselector.view.WindowMetricsProviderImpl
import com.android.systemui.mediaprojection.devicepolicy.MediaProjectionDevicePolicyModule
import com.android.systemui.mediaprojection.devicepolicy.PersonalProfile
import com.android.systemui.mediaprojection.permission.MediaProjectionPermissionActivity
@@ -106,6 +108,8 @@
impl: TaskPreviewSizeProvider
): DefaultLifecycleObserver
+ @Binds fun windowMetricsProvider(impl: WindowMetricsProviderImpl): WindowMetricsProvider
+
companion object {
@Provides
@MediaProjectionAppSelector
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/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt
index 864d35a..c829471 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt
@@ -19,8 +19,6 @@
import android.content.Context
import android.content.res.Configuration
import android.graphics.Rect
-import android.view.WindowInsets.Type
-import android.view.WindowManager
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorScope
@@ -36,7 +34,7 @@
@Inject
constructor(
private val context: Context,
- private val windowManager: WindowManager,
+ private val windowMetricsProvider: WindowMetricsProvider,
private val configurationController: ConfigurationController,
) : CallbackController<TaskPreviewSizeListener>, ConfigurationListener, DefaultLifecycleObserver {
@@ -62,17 +60,14 @@
}
private fun calculateSize(): Rect {
- val windowMetrics = windowManager.maximumWindowMetrics
- val maximumWindowHeight = windowMetrics.bounds.height()
- val width = windowMetrics.bounds.width()
+ val maxWindowBounds = windowMetricsProvider.maximumWindowBounds
+ val maximumWindowHeight = maxWindowBounds.height()
+ val width = maxWindowBounds.width()
var height = maximumWindowHeight
val isLargeScreen = isLargeScreen(context)
if (isLargeScreen) {
- val taskbarSize =
- windowManager.currentWindowMetrics.windowInsets
- .getInsets(Type.tappableElement())
- .bottom
+ val taskbarSize = windowMetricsProvider.currentWindowInsets.bottom
height -= taskbarSize
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/WindowMetricsProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/WindowMetricsProvider.kt
new file mode 100644
index 0000000..1932920
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/WindowMetricsProvider.kt
@@ -0,0 +1,30 @@
+package com.android.systemui.mediaprojection.appselector.view
+
+import android.graphics.Insets
+import android.graphics.Rect
+import android.view.WindowInsets
+import android.view.WindowManager
+import javax.inject.Inject
+
+/** Provides values related to window metrics. */
+interface WindowMetricsProvider {
+
+ val maximumWindowBounds: Rect
+
+ val currentWindowInsets: Insets
+}
+
+class WindowMetricsProviderImpl
+@Inject
+constructor(
+ private val windowManager: WindowManager,
+) : WindowMetricsProvider {
+ override val maximumWindowBounds: Rect
+ get() = windowManager.maximumWindowMetrics.bounds
+
+ override val currentWindowInsets: Insets
+ get() =
+ windowManager.currentWindowMetrics.windowInsets.getInsets(
+ WindowInsets.Type.tappableElement()
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
index 2b56d0c..d08d040 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -239,6 +239,8 @@
protected void onDestroy() {
super.onDestroy();
if (mDialog != null) {
+ mDialog.setOnDismissListener(null);
+ mDialog.setOnCancelListener(null);
mDialog.dismiss();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialog.kt
index 2f10ad3..b9bafd4 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialog.kt
@@ -89,15 +89,15 @@
}
return listOf(
ScreenShareOption(
- mode = ENTIRE_SCREEN,
- spinnerText = R.string.screen_share_permission_dialog_option_entire_screen,
- warningText = entireScreenWarningText
- ),
- ScreenShareOption(
mode = SINGLE_APP,
spinnerText = R.string.screen_share_permission_dialog_option_single_app,
warningText = singleAppWarningText,
spinnerDisabledText = singleAppDisabledText,
+ ),
+ ScreenShareOption(
+ mode = ENTIRE_SCREEN,
+ spinnerText = R.string.screen_share_permission_dialog_option_entire_screen,
+ warningText = entireScreenWarningText
)
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/ScreenShareOption.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/ScreenShareOption.kt
index 37e8d9f..9bd5783 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/ScreenShareOption.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/ScreenShareOption.kt
@@ -23,8 +23,8 @@
@IntDef(ENTIRE_SCREEN, SINGLE_APP)
annotation class ScreenShareMode
-const val ENTIRE_SCREEN = 0
-const val SINGLE_APP = 1
+const val SINGLE_APP = 0
+const val ENTIRE_SCREEN = 1
class ScreenShareOption(
@ScreenShareMode val mode: Int,
diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
index 07846b5..3cdcb2c 100644
--- a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
+++ b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
@@ -24,6 +24,8 @@
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.shared.system.QuickStepContract;
+import dalvik.annotation.optimization.NeverCompile;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -108,6 +110,7 @@
}
}
+ @NeverCompile
@Override
public void dump(PrintWriter pw, String[] args) {
pw.println("SysUiState state:");
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
index 564e984..2928cce 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
@@ -65,6 +65,8 @@
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.pip.Pip;
+import dalvik.annotation.optimization.NeverCompile;
+
import java.io.PrintWriter;
import java.util.Optional;
@@ -476,6 +478,7 @@
return mNavigationBars.get(mDisplayTracker.getDefaultDisplayId());
}
+ @NeverCompile
@Override
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("mIsLargeScreen=" + mIsLargeScreen);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 4d6d95a..bc4f7f25 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -584,7 +584,6 @@
if (!visible) {
mTransitionListener.onBackAltCleared();
}
- mRotationButtonController.getRotationButton().setCanShowRotationButton(!visible);
}
void setDisabledFlags(int disabledFlags, SysUiState sysUiState) {
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/QSFragmentLegacy.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java
index 8589ae9..68bf88b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java
@@ -72,7 +72,7 @@
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
- QSFragmentComponent qsFragmentComponent = mQsComponentFactory.create(this);
+ QSFragmentComponent qsFragmentComponent = mQsComponentFactory.create(getView());
mQsImpl = mQsImplProvider.get();
mQsImpl.onComponentCreated(qsFragmentComponent, savedInstanceState);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
index 2a36fdb..65a2c8c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
@@ -105,7 +105,7 @@
override fun removeCallback(callback: QSHost.Callback) {
if (useNewHost) {
- synchronized(callbacksMap) { callbacksMap.get(callback)?.cancel() }
+ synchronized(callbacksMap) { callbacksMap.remove(callback)?.cancel() }
} else {
qsTileHost.removeCallback(callback)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
index a32a024..4aad6a0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
@@ -72,6 +72,8 @@
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
import com.android.systemui.util.Utils;
+import dalvik.annotation.optimization.NeverCompile;
+
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.function.Consumer;
@@ -199,11 +201,13 @@
mListeningAndVisibilityLifecycleOwner = new ListeningAndVisibilityLifecycleOwner();
}
+ /**
+ * This method will set up all the necessary fields. Methods from the implemented interfaces
+ * should not be called before this method returns.
+ */
public void onComponentCreated(QSComponent qsComponent, @Nullable Bundle savedInstanceState) {
mRootView = qsComponent.getRootView();
- mCommandQueue.addCallback(this);
-
mQSPanelController = qsComponent.getQSPanelController();
mQuickQSPanelController = qsComponent.getQuickQSPanelController();
@@ -270,6 +274,9 @@
mQSPanelController.getMediaHost().getHostView().setAlpha(1.0f);
mQSAnimator.requestAnimatorUpdate();
});
+
+ // This will immediately call disable, so it needs to be added after setting up the fields.
+ mCommandQueue.addCallback(this);
}
private void bindFooterActionsView(View root) {
@@ -323,6 +330,8 @@
public void onDestroy() {
mCommandQueue.removeCallback(this);
mStatusBarStateController.removeCallback(this);
+ mQSPanelController.destroy();
+ mQuickQSPanelController.destroy();
if (mListening) {
setListening(false);
}
@@ -927,6 +936,7 @@
return mListeningAndVisibilityLifecycleOwner;
}
+ @NeverCompile
@Override
public void dump(PrintWriter pw, String[] args) {
IndentingPrintWriter indentingPw = new IndentingPrintWriter(pw, /* singleIndent= */ " ");
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 60c92c0..fc2f5f9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -158,6 +158,7 @@
protected void onInit() {
mView.initialize(mQSLogger);
mQSLogger.logAllTilesChangeListening(mView.isListening(), mView.getDumpableTag(), "");
+ mHost.addCallback(mQSHostCallback);
}
/**
@@ -172,6 +173,18 @@
}
@Override
+ public void destroy() {
+ super.destroy();
+ mHost.removeCallback(mQSHostCallback);
+
+ for (TileRecord record : mRecords) {
+ record.tile.removeCallback(record.callback);
+ mView.removeTile(record);
+ }
+ mRecords.clear();
+ }
+
+ @Override
protected void onViewAttached() {
mQsTileRevealController = createTileRevealController();
if (mQsTileRevealController != null) {
@@ -180,7 +193,6 @@
mMediaHost.addVisibilityChangeListener(mMediaHostVisibilityListener);
mView.addOnConfigurationChangedListener(mOnConfigurationChangedListener);
- mHost.addCallback(mQSHostCallback);
setTiles();
mLastOrientation = getResources().getConfiguration().orientation;
mQSLogger.logOnViewAttached(mLastOrientation, mView.getDumpableTag());
@@ -193,16 +205,11 @@
protected void onViewDetached() {
mQSLogger.logOnViewDetached(mLastOrientation, mView.getDumpableTag());
mView.removeOnConfigurationChangedListener(mOnConfigurationChangedListener);
- mHost.removeCallback(mQSHostCallback);
mView.getTileLayout().setListening(false, mUiEventLogger);
mMediaHost.removeVisibilityChangeListener(mMediaHostVisibilityListener);
- for (TileRecord record : mRecords) {
- record.tile.removeCallback(record.callback);
- }
- mRecords.clear();
mDumpManager.unregisterDumpable(mView.getDumpableTag());
}
@@ -222,15 +229,30 @@
if (!collapsedView && mQsTileRevealController != null) {
mQsTileRevealController.updateRevealedTiles(tiles);
}
-
- for (QSPanelControllerBase.TileRecord record : mRecords) {
- mView.removeTile(record);
- record.tile.removeCallback(record.callback);
+ boolean shouldChange = false;
+ if (tiles.size() == mRecords.size()) {
+ int i = 0;
+ for (QSTile tile : tiles) {
+ if (tile != mRecords.get(i).tile) {
+ shouldChange = true;
+ break;
+ }
+ i++;
+ }
+ } else {
+ shouldChange = true;
}
- mRecords.clear();
- mCachedSpecs = "";
- for (QSTile tile : tiles) {
- addTile(tile, collapsedView);
+
+ if (shouldChange) {
+ for (QSPanelControllerBase.TileRecord record : mRecords) {
+ mView.removeTile(record);
+ record.tile.removeCallback(record.callback);
+ }
+ mRecords.clear();
+ mCachedSpecs = "";
+ for (QSTile tile : tiles) {
+ addTile(tile, collapsedView);
+ }
}
}
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/SideLabelTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
index 11759f7..777faea 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
@@ -19,7 +19,7 @@
import android.content.Context
import android.util.AttributeSet
import com.android.systemui.flags.Flags
-import com.android.systemui.flags.ViewRefactorFlag
+import com.android.systemui.flags.RefactorFlag
import com.android.systemui.res.R
open class SideLabelTileLayout(
@@ -27,8 +27,8 @@
attrs: AttributeSet?
) : TileLayout(context, attrs) {
- private final val isSmallLandscapeLockscreenEnabled =
- ViewRefactorFlag(flag = Flags.LOCKSCREEN_ENABLE_LANDSCAPE).isEnabled
+ private val isSmallLandscapeLockscreenEnabled =
+ RefactorFlag.forView(Flags.LOCKSCREEN_ENABLE_LANDSCAPE).isEnabled
override fun updateResources(): Boolean {
return super.updateResources().also {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 9ba1645..9d4eba5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -15,13 +15,13 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.FontSizeUtils;
-import com.android.systemui.flags.ViewRefactorFlag;
-import com.android.systemui.res.R;
import com.android.systemui.flags.Flags;
+import com.android.systemui.flags.RefactorFlag;
import com.android.systemui.qs.QSPanel.QSTileLayout;
import com.android.systemui.qs.QSPanelControllerBase.TileRecord;
import com.android.systemui.qs.tileimpl.HeightOverrideable;
import com.android.systemui.qs.tileimpl.QSTileViewImplKt;
+import com.android.systemui.res.R;
import java.util.ArrayList;
@@ -55,7 +55,7 @@
protected int mLastTileBottom;
protected TextView mTempTextView;
private final Boolean mIsSmallLandscapeLockscreenEnabled =
- new ViewRefactorFlag(Flags.LOCKSCREEN_ENABLE_LANDSCAPE).isEnabled();
+ RefactorFlag.forView(Flags.LOCKSCREEN_ENABLE_LANDSCAPE).isEnabled();
public TileLayout(Context context) {
this(context, null);
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/dagger/QSFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
index 327e858..ce8db7898 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
@@ -16,6 +16,9 @@
package com.android.systemui.qs.dagger;
+import android.view.View;
+
+import com.android.systemui.dagger.qualifiers.RootView;
import com.android.systemui.qs.QSFragmentLegacy;
import dagger.BindsInstance;
@@ -31,6 +34,7 @@
/** Factory for building a {@link QSFragmentComponent}. */
@Subcomponent.Factory
interface Factory {
- QSFragmentComponent create(@BindsInstance QSFragmentLegacy qsFragment);
+ /** */
+ QSFragmentComponent create(@BindsInstance @RootView View view);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
index 0c9c24d..0e75b21 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
@@ -20,34 +20,17 @@
import static com.android.systemui.util.Utils.useQsMediaPlayer;
import android.content.Context;
-import android.view.View;
-import com.android.systemui.dagger.qualifiers.RootView;
-import com.android.systemui.plugins.qs.QS;
-import com.android.systemui.qs.QSFragmentLegacy;
-
-import javax.inject.Named;
-
-import dagger.Binds;
import dagger.Module;
import dagger.Provides;
+import javax.inject.Named;
+
/**
* Dagger Module for {@link QSFragmentComponent}.
*/
@Module(includes = {QSScopeModule.class})
public interface QSFragmentModule {
-
- @Provides
- @RootView
- static View provideRootView(QSFragmentLegacy qsFragment) {
- return qsFragment.getView();
- }
-
- /** */
- @Binds
- QS bindQS(QSFragmentLegacy qsFragment);
-
/** */
@Provides
@Named(QSScopeModule.QS_USING_MEDIA_PLAYER)
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/pipeline/domain/interactor/CurrentTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
index c5512c15..4bb8c6e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
@@ -272,7 +272,6 @@
// repository
launch { tileSpecRepository.setTiles(currentUser.value, resolvedSpecs) }
}
- Log.d("Fabian", "Finished resolving tiles")
}
}
}
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/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 5c6e902..a698208 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -38,7 +38,6 @@
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.res.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.DialogCuj;
import com.android.systemui.animation.DialogLaunchAnimator;
@@ -53,12 +52,13 @@
import com.android.systemui.qs.QsEventLogger;
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.connectivity.NetworkController;
import com.android.systemui.statusbar.connectivity.SignalCallback;
import com.android.systemui.statusbar.connectivity.WifiIndicators;
import com.android.systemui.statusbar.phone.SystemUIDialog;
-import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor;
-import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel;
+import com.android.systemui.statusbar.pipeline.shared.data.model.DefaultConnectionModel;
+import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.CastDevice;
import com.android.systemui.statusbar.policy.HotspotController;
@@ -85,10 +85,9 @@
private final NetworkController mNetworkController;
private final DialogLaunchAnimator mDialogLaunchAnimator;
private final Callback mCallback = new Callback();
- private final WifiInteractor mWifiInteractor;
private final TileJavaAdapter mJavaAdapter;
private final FeatureFlags mFeatureFlags;
- private boolean mWifiConnected;
+ private boolean mCastTransportAllowed;
private boolean mHotspotConnected;
@Inject
@@ -107,7 +106,7 @@
NetworkController networkController,
HotspotController hotspotController,
DialogLaunchAnimator dialogLaunchAnimator,
- WifiInteractor wifiInteractor,
+ ConnectivityRepository connectivityRepository,
TileJavaAdapter javaAdapter,
FeatureFlags featureFlags
) {
@@ -117,7 +116,6 @@
mKeyguard = keyguardStateController;
mNetworkController = networkController;
mDialogLaunchAnimator = dialogLaunchAnimator;
- mWifiInteractor = wifiInteractor;
mJavaAdapter = javaAdapter;
mFeatureFlags = featureFlags;
mController.observe(this, mCallback);
@@ -125,7 +123,11 @@
if (!mFeatureFlags.isEnabled(SIGNAL_CALLBACK_DEPRECATION)) {
mNetworkController.observe(this, mSignalCallback);
} else {
- mJavaAdapter.bind(this, mWifiInteractor.getWifiNetwork(), mNetworkModelConsumer);
+ mJavaAdapter.bind(
+ this,
+ connectivityRepository.getDefaultConnections(),
+ mNetworkModelConsumer
+ );
}
hotspotController.observe(this, mHotspotCallback);
}
@@ -282,7 +284,7 @@
}
state.icon = ResourceIcon.get(state.value ? R.drawable.ic_cast_connected
: R.drawable.ic_cast);
- if (canCastToWifi() || state.value) {
+ if (canCastToNetwork() || state.value) {
state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
if (!state.value) {
state.secondaryLabel = "";
@@ -291,7 +293,7 @@
state.forceExpandIcon = willPopDialog();
} else {
state.state = Tile.STATE_UNAVAILABLE;
- String noWifi = mContext.getString(R.string.quick_settings_cast_no_wifi);
+ String noWifi = mContext.getString(R.string.quick_settings_cast_no_network);
state.secondaryLabel = noWifi;
state.forceExpandIcon = false;
}
@@ -308,13 +310,13 @@
: mContext.getString(R.string.quick_settings_cast_device_default_name);
}
- private boolean canCastToWifi() {
- return mWifiConnected || mHotspotConnected;
+ private boolean canCastToNetwork() {
+ return mCastTransportAllowed || mHotspotConnected;
}
- private void setWifiConnected(boolean connected) {
- if (connected != mWifiConnected) {
- mWifiConnected = connected;
+ private void setCastTransportAllowed(boolean connected) {
+ if (connected != mCastTransportAllowed) {
+ mCastTransportAllowed = connected;
// Hotspot is not connected, so changes here should update
if (!mHotspotConnected) {
refreshState();
@@ -326,14 +328,17 @@
if (connected != mHotspotConnected) {
mHotspotConnected = connected;
// Wifi is not connected, so changes here should update
- if (!mWifiConnected) {
+ if (!mCastTransportAllowed) {
refreshState();
}
}
}
- private final Consumer<WifiNetworkModel> mNetworkModelConsumer = (model) -> {
- setWifiConnected(model instanceof WifiNetworkModel.Active);
+ private final Consumer<DefaultConnectionModel> mNetworkModelConsumer = (model) -> {
+ boolean isWifiDefault = model.getWifi().isDefault();
+ boolean isEthernetDefault = model.getEthernet().isDefault();
+ boolean hasCellularTransport = model.getMobile().isDefault();
+ setCastTransportAllowed((isWifiDefault || isEthernetDefault) && !hasCellularTransport);
};
private final SignalCallback mSignalCallback = new SignalCallback() {
@@ -342,7 +347,7 @@
// statusIcon.visible has the connected status information
boolean enabledAndConnected = indicators.enabled
&& (indicators.qsIcon != null && indicators.qsIcon.visible);
- setWifiConnected(enabledAndConnected);
+ setCastTransportAllowed(enabledAndConnected);
}
};
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/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 584456d..91b4d17 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -42,6 +42,7 @@
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
+import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -51,7 +52,6 @@
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.launch
-import javax.inject.Inject
/**
* Hooks up business logic that manipulates the state of the [SceneInteractor] for the system UI
@@ -142,7 +142,7 @@
// When the device becomes unlocked in Lockscreen, go to Gone if
// bypass is enabled.
renderedScenes.contains(SceneKey.Lockscreen) ->
- if (deviceEntryInteractor.isBypassEnabled()) {
+ if (deviceEntryInteractor.isBypassEnabled.value) {
SceneKey.Gone to
"device unlocked in Lockscreen scene with bypass"
} else {
@@ -179,36 +179,34 @@
}
applicationScope.launch {
- powerInteractor.isAsleep
- .collect { isAsleep ->
- if (isAsleep) {
- switchToScene(
- targetSceneKey = SceneKey.Lockscreen,
- loggingReason = "device is starting to sleep",
- )
- } else {
- val authMethod = authenticationInteractor.getAuthenticationMethod()
- val isUnlocked = deviceEntryInteractor.isUnlocked.value
- when {
- authMethod == AuthenticationMethodModel.None -> {
- switchToScene(
- targetSceneKey = SceneKey.Gone,
- loggingReason =
- "device is starting to wake up while auth method is" +
- " none",
- )
- }
- authMethod.isSecure && isUnlocked -> {
- switchToScene(
- targetSceneKey = SceneKey.Gone,
- loggingReason =
- "device is starting to wake up while unlocked with a" +
- " secure auth method",
- )
- }
+ powerInteractor.isAsleep.collect { isAsleep ->
+ if (isAsleep) {
+ switchToScene(
+ targetSceneKey = SceneKey.Lockscreen,
+ loggingReason = "device is starting to sleep",
+ )
+ } else {
+ val authMethod = authenticationInteractor.getAuthenticationMethod()
+ val isUnlocked = deviceEntryInteractor.isUnlocked.value
+ when {
+ authMethod == AuthenticationMethodModel.None -> {
+ switchToScene(
+ targetSceneKey = SceneKey.Gone,
+ loggingReason =
+ "device is starting to wake up while auth method is" + " none",
+ )
+ }
+ authMethod.isSecure && isUnlocked -> {
+ switchToScene(
+ targetSceneKey = SceneKey.Gone,
+ loggingReason =
+ "device is starting to wake up while unlocked with a" +
+ " secure auth method",
+ )
}
}
}
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt
index 4bc93a8..2e45353 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt
@@ -61,12 +61,17 @@
data class Swipe(
/** The direction of the swipe. */
val direction: Direction,
+ /**
+ * The edge from which the swipe originated or `null`, if the swipe didn't start close to an
+ * edge.
+ */
+ val fromEdge: Edge? = null,
/** The number of pointers that were used (for example, one or two fingers). */
val pointerCount: Int = 1,
) : UserAction
/** The user has hit the back button or performed the back navigation gesture. */
- object Back : UserAction
+ data object Back : UserAction
}
/** Enumerates all known "cardinal" directions for user actions. */
@@ -76,3 +81,11 @@
RIGHT,
DOWN,
}
+
+/** Enumerates all known edges from which a swipe can start. */
+enum class Edge {
+ LEFT,
+ TOP,
+ RIGHT,
+ BOTTOM,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
index a1d5d98..ade93b1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
@@ -186,14 +186,14 @@
private fun createOptionList(): List<ScreenShareOption> {
return listOf(
ScreenShareOption(
- ENTIRE_SCREEN,
- R.string.screen_share_permission_dialog_option_entire_screen,
- R.string.screenrecord_permission_dialog_warning_entire_screen
- ),
- ScreenShareOption(
SINGLE_APP,
R.string.screen_share_permission_dialog_option_single_app,
R.string.screenrecord_permission_dialog_warning_single_app
+ ),
+ ScreenShareOption(
+ ENTIRE_SCREEN,
+ R.string.screen_share_permission_dialog_option_entire_screen,
+ R.string.screenrecord_permission_dialog_warning_entire_screen
)
)
}
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/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 4b3bd0b..127a57e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -303,6 +303,13 @@
private String mPackageName = "";
private BroadcastReceiver mCopyBroadcastReceiver;
+ // When false, the screenshot is taken without showing the ui. Note that this only applies to
+ // external displays, as on the default one the UI should **always** be shown.
+ // This is needed in case of screenshot during display mirroring, as adding another window to
+ // the external display makes mirroring stop.
+ // When there is a way to distinguish between displays that are mirroring or extending, this
+ // can be removed and we can directly show the ui only in the extended case.
+ private final Boolean mShowUIOnExternalDisplay;
/** Tracks config changes that require re-creating UI */
private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
ActivityInfo.CONFIG_ORIENTATION
@@ -335,7 +342,8 @@
AssistContentRequester assistContentRequester,
MessageContainerController messageContainerController,
Provider<ScreenshotSoundController> screenshotSoundController,
- @Assisted int displayId
+ @Assisted int displayId,
+ @Assisted boolean showUIOnExternalDisplay
) {
mScreenshotSmartActions = screenshotSmartActions;
mNotificationsController = screenshotNotificationsController;
@@ -401,6 +409,7 @@
mContext.registerReceiver(mCopyBroadcastReceiver, new IntentFilter(
ClipboardOverlayController.COPY_OVERLAY_ACTION),
ClipboardOverlayController.SELF_PERMISSION, null, Context.RECEIVER_NOT_EXPORTED);
+ mShowUIOnExternalDisplay = showUIOnExternalDisplay;
}
void handleScreenshot(ScreenshotData screenshot, Consumer<Uri> finisher,
@@ -448,6 +457,23 @@
prepareViewForNewScreenshot(screenshot, oldPackageName);
+ if (mFlags.isEnabled(Flags.SCREENSHOT_METADATA) && screenshot.getTaskId() >= 0) {
+ mAssistContentRequester.requestAssistContent(screenshot.getTaskId(),
+ new AssistContentRequester.Callback() {
+ @Override
+ public void onAssistContentAvailable(AssistContent assistContent) {
+ screenshot.setContextUrl(assistContent.getWebUri());
+ }
+ });
+ }
+
+ if (!shouldShowUi()) {
+ saveScreenshotInWorkerThread(
+ screenshot.getUserHandle(), finisher, this::logSuccessOnActionsReady,
+ (ignored) -> {});
+ return;
+ }
+
saveScreenshotInWorkerThread(screenshot.getUserHandle(), finisher,
this::showUiOnActionsReady, this::showUiOnQuickShareActionReady);
@@ -482,22 +508,16 @@
screenshot.getUserHandle()));
mScreenshotView.setScreenshot(screenshot);
- if (mFlags.isEnabled(Flags.SCREENSHOT_METADATA) && screenshot.getTaskId() >= 0) {
- mAssistContentRequester.requestAssistContent(screenshot.getTaskId(),
- new AssistContentRequester.Callback() {
- @Override
- public void onAssistContentAvailable(AssistContent assistContent) {
- screenshot.setContextUrl(assistContent.getWebUri());
- }
- });
- }
-
// ignore system bar insets for the purpose of window layout
mWindow.getDecorView().setOnApplyWindowInsetsListener(
(v, insets) -> WindowInsets.CONSUMED);
mScreenshotHandler.cancelTimeout(); // restarted after animation
}
+ private boolean shouldShowUi() {
+ return mDisplayId == Display.DEFAULT_DISPLAY || mShowUIOnExternalDisplay;
+ }
+
void prepareViewForNewScreenshot(ScreenshotData screenshot, String oldPackageName) {
withWindowAttached(() -> {
if (mUserManager.isManagedProfile(screenshot.getUserHandle().getIdentifier())) {
@@ -1199,7 +1219,13 @@
/** Injectable factory to create screenshot controller instances for a specific display. */
@AssistedFactory
public interface Factory {
- /** Creates an instance of the controller for that specific displayId. */
- ScreenshotController create(int displayId);
+ /**
+ * Creates an instance of the controller for that specific displayId.
+ *
+ * @param displayId: display to capture
+ * @param showUIOnExternalDisplay: Whether the UI should be shown if this is an external
+ * display.
+ */
+ ScreenshotController create(int displayId, boolean showUIOnExternalDisplay);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
index 03c3f7a..abe40ff 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
@@ -135,7 +135,9 @@
}
private fun getScreenshotController(id: Int): ScreenshotController {
- return screenshotControllers.computeIfAbsent(id) { screenshotControllerFactory.create(id) }
+ return screenshotControllers.computeIfAbsent(id) {
+ screenshotControllerFactory.create(id, /* showUIOnExternalDisplay= */ false)
+ }
}
/** For java compatibility only. see [executeScreenshots] */
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 0be2265..75d52cb 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -132,7 +132,8 @@
if (mFeatureFlags.isEnabled(MULTI_DISPLAY_SCREENSHOT)) {
mScreenshot = null;
} else {
- mScreenshot = screenshotControllerFactory.create(Display.DEFAULT_DISPLAY);
+ mScreenshot = screenshotControllerFactory.create(
+ Display.DEFAULT_DISPLAY, /* showUIOnExternalDisplay= */ false);
}
}
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/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index 2f87301..393a698 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -132,6 +132,7 @@
setUserIdInternal(startingUser)
val filter = IntentFilter().apply {
+ addAction(Intent.ACTION_LOCALE_CHANGED)
addAction(Intent.ACTION_USER_INFO_CHANGED)
// These get called when a managed profile goes in or out of quiet mode.
addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
@@ -149,6 +150,7 @@
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
+ Intent.ACTION_LOCALE_CHANGED,
Intent.ACTION_USER_INFO_CHANGED,
Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java b/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java
index 9235fcc..c42fdf8 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/DebugDrawable.java
@@ -67,8 +67,6 @@
mDebugPaint.setStrokeWidth(2);
mDebugPaint.setStyle(Paint.Style.STROKE);
mDebugPaint.setTextSize(24);
- String headerDebugInfo = mNotificationPanelViewController.getHeaderDebugInfo();
- if (headerDebugInfo != null) canvas.drawText(headerDebugInfo, 50, 100, mDebugPaint);
drawDebugInfo(canvas, mNotificationPanelViewController.getMaxPanelHeight(),
Color.RED, "getMaxPanelHeight()");
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index d3d38e5c..51972c2 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -138,7 +138,6 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.shared.model.TransitionState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
-import com.android.systemui.power.shared.model.WakefulnessModel;
import com.android.systemui.keyguard.ui.binder.KeyguardLongPressViewBinder;
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingLockscreenHostedTransitionViewModel;
@@ -162,9 +161,10 @@
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
+import com.android.systemui.power.domain.interactor.PowerInteractor;
+import com.android.systemui.power.shared.model.WakefulnessModel;
import com.android.systemui.res.R;
import com.android.systemui.shade.data.repository.ShadeRepository;
-import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.shade.transition.ShadeTransitionController;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.CommandQueue;
@@ -200,7 +200,6 @@
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
import com.android.systemui.statusbar.phone.KeyguardBottomAreaView;
import com.android.systemui.statusbar.phone.KeyguardBottomAreaViewController;
@@ -217,6 +216,7 @@
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController;
@@ -230,6 +230,8 @@
import com.android.systemui.util.time.SystemClock;
import com.android.wm.shell.animation.FlingAnimationUtils;
+import dalvik.annotation.optimization.NeverCompile;
+
import kotlin.Unit;
import java.io.PrintWriter;
@@ -362,7 +364,7 @@
private boolean mIsLaunchAnimationRunning;
private float mOverExpansion;
private CentralSurfaces mCentralSurfaces;
- private HeadsUpManagerPhone mHeadsUpManager;
+ private HeadsUpManager mHeadsUpManager;
private float mExpandedHeight = 0;
/** The current squish amount for the predictive back animation */
private float mCurrentBackProgress = 0.0f;
@@ -412,9 +414,7 @@
private int mDisplayLeftInset = 0; // in pixels
@VisibleForTesting
- KeyguardClockPositionAlgorithm
- mClockPositionAlgorithm =
- new KeyguardClockPositionAlgorithm();
+ KeyguardClockPositionAlgorithm mClockPositionAlgorithm;
private final KeyguardClockPositionAlgorithm.Result
mClockPositionResult =
new KeyguardClockPositionAlgorithm.Result();
@@ -777,7 +777,8 @@
KeyguardViewConfigurator keyguardViewConfigurator,
KeyguardFaceAuthInteractor keyguardFaceAuthInteractor,
SplitShadeStateController splitShadeStateController,
- PowerInteractor powerInteractor) {
+ PowerInteractor powerInteractor,
+ KeyguardClockPositionAlgorithm keyguardClockPositionAlgorithm) {
keyguardStateController.addCallback(new KeyguardStateController.Callback() {
@Override
public void onKeyguardFadingAwayChanged() {
@@ -805,6 +806,7 @@
mKeyguardInteractor = keyguardInteractor;
mPowerInteractor = powerInteractor;
mKeyguardViewConfigurator = keyguardViewConfigurator;
+ mClockPositionAlgorithm = keyguardClockPositionAlgorithm;
mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
@@ -1275,7 +1277,8 @@
KeyguardStatusView keyguardStatusView = mView.getRootView().findViewById(
R.id.keyguard_status_view);
KeyguardStatusViewComponent statusViewComponent =
- mKeyguardStatusViewComponentFactory.build(keyguardStatusView);
+ mKeyguardStatusViewComponentFactory.build(keyguardStatusView,
+ mView.getContext().getDisplay());
mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController();
mKeyguardStatusViewController.init();
}
@@ -3025,7 +3028,7 @@
return headsUpVisible || isExpanded() || mBouncerShowing;
}
- private void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
+ private void setHeadsUpManager(HeadsUpManager headsUpManager) {
mHeadsUpManager = headsUpManager;
mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
mHeadsUpTouchHelper = new HeadsUpTouchHelper(
@@ -3378,6 +3381,7 @@
mBlockingExpansionForCurrentTouch = isTracking();
}
+ @NeverCompile
@Override
public void dump(PrintWriter pw, String[] args) {
pw.println(TAG + ":");
@@ -3507,7 +3511,7 @@
GestureRecorder recorder,
Runnable hideExpandedRunnable,
NotificationShelfController notificationShelfController,
- HeadsUpManagerPhone headsUpManager) {
+ HeadsUpManager headsUpManager) {
setHeadsUpManager(headsUpManager);
// TODO(b/254859580): this can be injected.
mCentralSurfaces = centralSurfaces;
@@ -3556,10 +3560,6 @@
mView.getViewTreeObserver().removeOnGlobalLayoutListener(listener);
}
- String getHeaderDebugInfo() {
- return "USER " + mHeadsUpManager.getUser();
- }
-
@Override
public void onThemeChanged() {
mConfigurationListener.onThemeChanged();
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 6cf4ff5..d05dfe2 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -21,7 +21,6 @@
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
import android.app.StatusBarManager;
-import android.os.PowerManager;
import android.util.Log;
import android.view.GestureDetector;
import android.view.InputDevice;
@@ -36,10 +35,11 @@
import com.android.keyguard.LockIconViewController;
import com.android.keyguard.dagger.KeyguardBouncerComponent;
import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.back.domain.interactor.BackActionInteractor;
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor;
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.bouncer.ui.binder.KeyguardBouncerViewBinder;
import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel;
import com.android.systemui.classifier.FalsingCollector;
@@ -48,7 +48,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
-import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor;
+import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.shared.model.TransitionState;
@@ -56,6 +56,7 @@
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
import com.android.systemui.log.BouncerLogger;
import com.android.systemui.power.domain.interactor.PowerInteractor;
+import com.android.systemui.res.R;
import com.android.systemui.shared.animation.DisableSubpixelTextTransitionListener;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
@@ -105,7 +106,9 @@
private final NotificationInsetsController mNotificationInsetsController;
private final boolean mIsTrackpadCommonEnabled;
private final FeatureFlags mFeatureFlags;
- private final KeyEventInteractor mKeyEventInteractor;
+ private final SysUIKeyEventHandler mSysUIKeyEventHandler;
+ private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
+ private final AlternateBouncerInteractor mAlternateBouncerInteractor;
private GestureDetector mPulsingWakeupGestureHandler;
private GestureDetector mDreamingWakeupGestureHandler;
private View mBrightnessMirror;
@@ -182,7 +185,9 @@
SystemClock clock,
BouncerMessageInteractor bouncerMessageInteractor,
BouncerLogger bouncerLogger,
- KeyEventInteractor keyEventInteractor) {
+ SysUIKeyEventHandler sysUIKeyEventHandler,
+ PrimaryBouncerInteractor primaryBouncerInteractor,
+ AlternateBouncerInteractor alternateBouncerInteractor) {
mLockscreenShadeTransitionController = transitionController;
mFalsingCollector = falsingCollector;
mStatusBarStateController = statusBarStateController;
@@ -209,7 +214,9 @@
mNotificationInsetsController = notificationInsetsController;
mIsTrackpadCommonEnabled = featureFlags.isEnabled(TRACKPAD_GESTURE_COMMON);
mFeatureFlags = featureFlags;
- mKeyEventInteractor = keyEventInteractor;
+ mSysUIKeyEventHandler = sysUIKeyEventHandler;
+ mPrimaryBouncerInteractor = primaryBouncerInteractor;
+ mAlternateBouncerInteractor = alternateBouncerInteractor;
// This view is not part of the newly inflated expanded status bar.
mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container);
@@ -351,16 +358,6 @@
if (mStatusBarStateController.isDozing()) {
mDozeScrimController.extendPulse();
}
- mLockIconViewController.onTouchEvent(
- ev,
- /* onGestureDetectedRunnable */
- () -> {
- mService.userActivity();
- mPowerInteractor.wakeUpIfDozing(
- "LOCK_ICON_TOUCH",
- PowerManager.WAKE_REASON_GESTURE);
- }
- );
// In case we start outside of the view bounds (below the status bar), we need to
// dispatch the touch manually as the view system can't accommodate for touches
@@ -415,8 +412,18 @@
private boolean shouldInterceptTouchEventInternal(MotionEvent ev) {
mLastInterceptWasDragDownHelper = false;
- if (mStatusBarStateController.isDozing() && !mDozeServiceHost.isPulsing()
- && !mDockManager.isDocked()) {
+ // When the device starts dozing, there's a delay before the device's display state
+ // changes from ON => DOZE to allow for the light reveal animation to run at
+ // a higher refresh rate and to delay visual changes (ie: display blink) when
+ // changing the display state. We'll call this specific state the
+ // "aodDefermentState". In this state we:
+ // - don't want touches to get sent to underlying views, except the lock icon
+ // - handle the tap to wake gesture via the PulsingGestureListener
+ if (mStatusBarStateController.isDozing()
+ && !mDozeServiceHost.isPulsing()
+ && !mDockManager.isDocked()
+ && !mLockIconViewController.willHandleTouchWhileDozing(ev)
+ ) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
mShadeLogger.d("NSWVC: capture all touch events in always-on");
}
@@ -432,16 +439,15 @@
return true;
}
- if (mLockIconViewController.onInterceptTouchEvent(ev)) {
- // immediately return true; don't send the touch to the drag down helper
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- mShadeLogger.d("NSWVC: don't send touch to drag down helper");
- }
- return true;
+ boolean bouncerShowing;
+ if (mFeatureFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) {
+ bouncerShowing = mPrimaryBouncerInteractor.isBouncerShowing()
+ || mAlternateBouncerInteractor.isVisibleState();
+ } else {
+ bouncerShowing = mService.isBouncerShowing();
}
-
if (mNotificationPanelViewController.isFullyExpanded()
- && !mService.isBouncerShowing()
+ && !bouncerShowing
&& !mStatusBarStateController.isDozing()) {
if (mDragDownHelper.isDragDownEnabled()) {
// This handles drag down over lockscreen
@@ -523,17 +529,17 @@
@Override
public boolean interceptMediaKey(KeyEvent event) {
- return mKeyEventInteractor.interceptMediaKey(event);
+ return mSysUIKeyEventHandler.interceptMediaKey(event);
}
@Override
public boolean dispatchKeyEventPreIme(KeyEvent event) {
- return mKeyEventInteractor.dispatchKeyEventPreIme(event);
+ return mSysUIKeyEventHandler.dispatchKeyEventPreIme(event);
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
- return mKeyEventInteractor.dispatchKeyEvent(event);
+ return mSysUIKeyEventHandler.dispatchKeyEvent(event);
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index 9b74ac4..3bbb2cf 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -105,6 +105,8 @@
import com.android.systemui.util.LargeScreenUtils;
import com.android.systemui.util.kotlin.JavaAdapter;
+import dalvik.annotation.optimization.NeverCompile;
+
import dagger.Lazy;
import java.io.PrintWriter;
@@ -2015,6 +2017,7 @@
(int) ((y - getInitialTouchY()) / displayDensity), (int) (vel / displayDensity));
}
+ @NeverCompile
@Override
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.println(TAG + ":");
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
index 447a15d..2c4b0b9 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
@@ -178,9 +178,6 @@
/** Listens for shade visibility changes. */
interface ShadeVisibilityListener {
- /** Called when the visibility of the shade changes. */
- void visibilityChanged(boolean visible);
-
/** Called when shade expanded and visible state changed. */
void expandedVisibleChanged(boolean expandedVisible);
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
index 367449b..fdc7eec 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
@@ -24,6 +24,7 @@
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
+import com.android.systemui.DejankUtils;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
@@ -75,6 +76,7 @@
private final ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>();
private boolean mExpandedVisible;
+ private boolean mLockscreenOrShadeVisible;
private NotificationPresenter mPresenter;
private NotificationShadeWindowViewController mNotificationShadeWindowViewController;
@@ -399,8 +401,19 @@
}
private void notifyVisibilityChanged(boolean visible) {
- mShadeVisibilityListener.visibilityChanged(visible);
mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(visible);
+ if (mLockscreenOrShadeVisible != visible) {
+ mLockscreenOrShadeVisible = visible;
+ if (visible) {
+ // It would be best if this could be done as a side effect of listening to the
+ // [WindowRootViewVisibilityInteractor.isLockscreenOrShadeVisible] flow inside
+ // NotificationShadeWindowViewController. However, there's no guarantee that the
+ // flow will emit in the same frame as when the visibility changed, and we want the
+ // DejankUtils to be notified immediately, so we do it immediately here.
+ DejankUtils.notifyRendererOfExpensiveFrame(
+ getNotificationShadeWindowView(), "onShadeVisibilityChanged");
+ }
+ }
}
private void notifyExpandedVisibleChanged(boolean expandedVisible) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
index 0554c58..1a28ddc 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
@@ -72,10 +72,6 @@
listener.onShadeExpansionFullyChanged(qsExpanded)
}
- fun removeFullExpansionListener(listener: ShadeFullExpansionListener) {
- fullExpansionListeners.remove(listener)
- }
-
fun addQsExpansionListener(listener: ShadeQsExpansionListener) {
qsExpansionListeners.add(listener)
listener.onQsExpansionChanged(qsExpanded)
@@ -99,11 +95,6 @@
stateListeners.add(listener)
}
- /** Removes a state listener. */
- fun removeStateListener(listener: ShadeStateListener) {
- stateListeners.remove(listener)
- }
-
override fun addShadeStateEventsListener(listener: ShadeStateEventsListener) {
shadeStateEventsListeners.addIfAbsent(listener)
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
index 25bd8e7..cb95b25 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
@@ -38,7 +38,6 @@
import com.android.app.animation.Interpolators
import com.android.settingslib.Utils
import com.android.systemui.Dumpable
-import com.android.systemui.res.R
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
@@ -49,6 +48,7 @@
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.ChipVisibilityListener
import com.android.systemui.qs.HeaderPrivacyIconsController
+import com.android.systemui.res.R
import com.android.systemui.shade.ShadeHeaderController.Companion.HEADER_TRANSITION_ID
import com.android.systemui.shade.ShadeHeaderController.Companion.LARGE_SCREEN_HEADER_CONSTRAINT
import com.android.systemui.shade.ShadeHeaderController.Companion.LARGE_SCREEN_HEADER_TRANSITION_ID
@@ -304,7 +304,8 @@
iconManager = tintedIconManagerFactory.create(iconContainer, StatusBarLocation.QS)
iconManager.setTint(
- Utils.getColorAttrDefaultColor(header.context, android.R.attr.textColorPrimary)
+ Utils.getColorAttrDefaultColor(header.context, android.R.attr.textColorPrimary),
+ Utils.getColorAttrDefaultColor(header.context, android.R.attr.textColorPrimaryInverse),
)
carrierIconSlots =
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt
index 6ee6cbf..4e23e7d 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt
@@ -20,7 +20,7 @@
import com.android.systemui.statusbar.NotificationShelfController
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.phone.CentralSurfaces
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
+import com.android.systemui.statusbar.policy.HeadsUpManager
/**
* Allows CentralSurfacesImpl to interact with the shade. Only CentralSurfacesImpl should reference
@@ -34,7 +34,7 @@
recorder: GestureRecorder,
hideExpandedRunnable: Runnable,
notificationShelfController: NotificationShelfController,
- headsUpManager: HeadsUpManagerPhone
+ headsUpManager: HeadsUpManager
)
/** Cancels any pending collapses. */
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 ac8333a..99370e9 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
@@ -19,7 +19,11 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.DozeStateModel
+import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlags
import com.android.systemui.scene.shared.model.ObservableTransitionState
@@ -27,9 +31,10 @@
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
+import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository
-import com.android.systemui.statusbar.policy.DeviceProvisionedController
-import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.statusbar.policy.data.repository.DeviceProvisioningRepository
+import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.kotlin.pairwise
import javax.inject.Inject
import javax.inject.Provider
@@ -56,14 +61,17 @@
@Inject
constructor(
@Application scope: CoroutineScope,
+ deviceProvisioningRepository: DeviceProvisioningRepository,
disableFlagsRepository: DisableFlagsRepository,
+ dozeParams: DozeParameters,
sceneContainerFlags: SceneContainerFlags,
// TODO(b/300258424) convert to direct reference instead of provider
sceneInteractorProvider: Provider<SceneInteractor>,
keyguardRepository: KeyguardRepository,
+ keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ powerInteractor: PowerInteractor,
userSetupRepository: UserSetupRepository,
- deviceProvisionedController: DeviceProvisionedController,
- userInteractor: UserInteractor,
+ userRepository: UserRepository,
sharedNotificationContainerInteractor: SharedNotificationContainerInteractor,
repository: ShadeRepository,
) {
@@ -148,12 +156,13 @@
*
* TODO(b/300258424) remove all but the first sentence of this comment
*/
- val isAnyExpanded: Flow<Boolean> =
+ val isAnyExpanded: StateFlow<Boolean> =
if (sceneContainerFlags.isEnabled()) {
- anyExpansion.map { it > 0f }.distinctUntilChanged()
- } else {
- repository.legacyExpandedOrAwaitingInputTransfer
- }
+ anyExpansion.map { it > 0f }.distinctUntilChanged()
+ } else {
+ repository.legacyExpandedOrAwaitingInputTransfer
+ }
+ .stateIn(scope, SharingStarted.Eagerly, false)
/**
* Whether the user is expanding or collapsing the shade with user input. This will be true even
@@ -187,6 +196,26 @@
combine(isUserInteractingWithShade, isUserInteractingWithShade) { shade, qs -> shade || qs }
.distinctUntilChanged()
+ /** Are touches allowed on the notification panel? */
+ val isShadeTouchable: Flow<Boolean> =
+ combine(
+ powerInteractor.isAsleep,
+ keyguardTransitionInteractor.isInTransitionToStateWhere { it == KeyguardState.AOD },
+ keyguardRepository.dozeTransitionModel.map { it.to == DozeStateModel.DOZE_PULSING },
+ deviceProvisioningRepository.isFactoryResetProtectionActive,
+ ) { isAsleep, goingToSleep, isPulsing, isFrpActive ->
+ when {
+ // Touches are disabled when Factory Reset Protection is active
+ isFrpActive -> false
+ // If the device is going to sleep, only accept touches if we're still
+ // animating
+ goingToSleep -> dozeParams.shouldControlScreenOff()
+ // If the device is asleep, only accept touches if there's a pulse
+ isAsleep -> isPulsing
+ else -> true
+ }
+ }
+
/** Emits true if the shade can be expanded from QQS to QS and false otherwise. */
val isExpandToQsEnabled: Flow<Boolean> =
combine(
@@ -194,11 +223,12 @@
isShadeEnabled,
keyguardRepository.isDozing,
userSetupRepository.isUserSetupFlow,
- ) { disableFlags, isShadeEnabled, isDozing, isUserSetup ->
- deviceProvisionedController.isDeviceProvisioned &&
+ deviceProvisioningRepository.isDeviceProvisioned,
+ ) { disableFlags, isShadeEnabled, isDozing, isUserSetup, isDeviceProvisioned ->
+ isDeviceProvisioned &&
// Disallow QS during setup if it's a simple user switcher. (The user intends to
// use the lock screen user switcher, QS is not needed.)
- (isUserSetup || !userInteractor.isSimpleUserSwitcher) &&
+ (isUserSetup || !userRepository.isSimpleUserSwitcher()) &&
isShadeEnabled &&
disableFlags.isQuickSettingsEnabled() &&
!isDozing
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
index d24f9d8..77b0958 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
@@ -18,6 +18,8 @@
import android.view.View;
+import androidx.annotation.Nullable;
+
import com.android.app.animation.Interpolators;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
@@ -113,7 +115,16 @@
fadeIn(view, ANIMATION_DURATION_LENGTH, 0);
}
+ public static void fadeIn(final View view, Runnable endRunnable) {
+ fadeIn(view, ANIMATION_DURATION_LENGTH, /* delay= */ 0, endRunnable);
+ }
+
public static void fadeIn(final View view, long duration, int delay) {
+ fadeIn(view, duration, delay, /* endRunnable= */ null);
+ }
+
+ public static void fadeIn(final View view, long duration, int delay,
+ @Nullable Runnable endRunnable) {
view.animate().cancel();
if (view.getVisibility() == View.INVISIBLE) {
view.setAlpha(0.0f);
@@ -124,7 +135,7 @@
.setDuration(duration)
.setStartDelay(delay)
.setInterpolator(Interpolators.ALPHA_IN)
- .withEndAction(null);
+ .withEndAction(endRunnable);
if (view.hasOverlappingRendering() && view.getLayerType() != View.LAYER_TYPE_HARDWARE) {
view.animate().withLayer();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 2147510..f32f1c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -16,9 +16,16 @@
package com.android.systemui.statusbar;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
+import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS;
+import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS;
+import static android.os.UserHandle.USER_NULL;
+import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS;
+import static android.provider.Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS;
import static com.android.systemui.DejankUtils.whitelistIpcs;
+import android.annotation.SuppressLint;
+import android.annotation.UserIdInt;
import android.app.ActivityOptions;
import android.app.KeyguardManager;
import android.app.Notification;
@@ -30,8 +37,10 @@
import android.content.IntentSender;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
+import android.net.Uri;
import android.os.Handler;
import android.os.HandlerExecutor;
+import android.os.Looper;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
@@ -41,14 +50,18 @@
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
+import androidx.annotation.WorkerThread;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.Dumpable;
import com.android.systemui.broadcast.BroadcastDispatcher;
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.flags.FeatureFlagsClassic;
+import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.recents.OverviewProxyService;
@@ -63,7 +76,9 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -84,6 +99,11 @@
private final SecureSettings mSecureSettings;
private final Object mLock = new Object();
+ private static final Uri SHOW_LOCKSCREEN =
+ Settings.Secure.getUriFor(LOCK_SCREEN_SHOW_NOTIFICATIONS);
+ private static final Uri SHOW_PRIVATE_LOCKSCREEN =
+ Settings.Secure.getUriFor(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+
private final Lazy<NotificationVisibilityProvider> mVisibilityProviderLazy;
private final Lazy<CommonNotifCollection> mCommonNotifCollectionLazy;
private final DevicePolicyManager mDevicePolicyManager;
@@ -91,6 +111,23 @@
private final SparseBooleanArray mUsersWithSeparateWorkChallenge = new SparseBooleanArray();
private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
private final SparseBooleanArray mUsersAllowingNotifications = new SparseBooleanArray();
+
+ // The variables between mUsersDpcAllowingNotifications and
+ // mUsersUsersAllowingPrivateNotifications (inclusive) are written on a background thread
+ // and read on the main thread. Because the pipeline needs these values, adding locks would
+ // introduce too much jank. This means that some pipeline runs could get incorrect values, that
+ // would be fixed on the next pipeline run. We think this will be rare since a pipeline run
+ // would have to overlap with a DPM sync or a user changing a value in Settings, and we run the
+ // pipeline frequently enough that it should be corrected by the next time it matters for the
+ // user.
+ private final SparseBooleanArray mUsersDpcAllowingNotifications = new SparseBooleanArray();
+ private final SparseBooleanArray mUsersUsersAllowingNotifications = new SparseBooleanArray();
+ private boolean mKeyguardAllowingNotifications = true;
+ private final SparseBooleanArray mUsersDpcAllowingPrivateNotifications
+ = new SparseBooleanArray();
+ private final SparseBooleanArray mUsersUsersAllowingPrivateNotifications
+ = new SparseBooleanArray();
+
private final SparseBooleanArray mUsersInLockdownLatestResult = new SparseBooleanArray();
private final SparseBooleanArray mShouldHideNotifsLatestResult = new SparseBooleanArray();
private final UserManager mUserManager;
@@ -99,24 +136,39 @@
private final BroadcastDispatcher mBroadcastDispatcher;
private final NotificationClickNotifier mClickNotifier;
private final Lazy<OverviewProxyService> mOverviewProxyServiceLazy;
+ private final FeatureFlagsClassic mFeatureFlags;
private boolean mShowLockscreenNotifications;
private LockPatternUtils mLockPatternUtils;
protected KeyguardManager mKeyguardManager;
private int mState = StatusBarState.SHADE;
private final ListenerSet<NotificationStateChangedListener> mNotifStateChangedListeners =
new ListenerSet<>();
+ private final Collection<Uri> mLockScreenUris = new ArrayList<>();
+
protected final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
- if (ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action) &&
- isCurrentProfile(getSendingUserId())) {
- mUsersAllowingPrivateNotifications.clear();
- updateLockscreenNotificationSetting();
- // TODO(b/231976036): Consolidate pipeline invalidations related to this event
- // notifyNotificationStateChanged();
+ if (ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action)) {
+ if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
+ boolean changed = updateDpcSettings(getSendingUserId());
+ if (mCurrentUserId == getSendingUserId()) {
+ changed |= updateLockscreenNotificationSetting();
+ }
+ if (changed) {
+ notifyNotificationStateChanged();
+ }
+ } else {
+ if (isCurrentProfile(getSendingUserId())) {
+ mUsersAllowingPrivateNotifications.clear();
+ updateLockscreenNotificationSetting();
+ // TODO(b/231976036): Consolidate pipeline invalidations related to this
+ // event
+ // notifyNotificationStateChanged();
+ }
+ }
}
}
};
@@ -136,6 +188,14 @@
updateCurrentProfilesCache();
break;
case Intent.ACTION_USER_ADDED:
+ updateCurrentProfilesCache();
+ if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
+ mBackgroundHandler.post(() -> {
+ initValuesForUser(userId);
+ });
+ }
+ break;
case Intent.ACTION_MANAGED_PROFILE_AVAILABLE:
case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE:
updateCurrentProfilesCache();
@@ -193,6 +253,8 @@
protected final Context mContext;
private final Handler mMainHandler;
+ private final Handler mBackgroundHandler;
+ private final Executor mBackgroundExecutor;
protected final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<>();
protected final SparseArray<UserInfo> mCurrentManagedProfiles = new SparseArray<>();
@@ -214,13 +276,18 @@
KeyguardManager keyguardManager,
StatusBarStateController statusBarStateController,
@Main Handler mainHandler,
+ @Background Handler backgroundHandler,
+ @Background Executor backgroundExecutor,
DeviceProvisionedController deviceProvisionedController,
KeyguardStateController keyguardStateController,
SecureSettings secureSettings,
DumpManager dumpManager,
- LockPatternUtils lockPatternUtils) {
+ LockPatternUtils lockPatternUtils,
+ FeatureFlagsClassic featureFlags) {
mContext = context;
mMainHandler = mainHandler;
+ mBackgroundHandler = backgroundHandler;
+ mBackgroundExecutor = backgroundExecutor;
mDevicePolicyManager = devicePolicyManager;
mUserManager = userManager;
mUserTracker = userTracker;
@@ -236,6 +303,10 @@
mDeviceProvisionedController = deviceProvisionedController;
mSecureSettings = secureSettings;
mKeyguardStateController = keyguardStateController;
+ mFeatureFlags = featureFlags;
+
+ mLockScreenUris.add(SHOW_LOCKSCREEN);
+ mLockScreenUris.add(SHOW_PRIVATE_LOCKSCREEN);
dumpManager.registerDumpable(this);
}
@@ -243,16 +314,53 @@
public void setUpWithPresenter(NotificationPresenter presenter) {
mPresenter = presenter;
- mLockscreenSettingsObserver = new ContentObserver(mMainHandler) {
+ mLockscreenSettingsObserver = new ContentObserver(
+ mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)
+ ? mBackgroundHandler
+ : mMainHandler) {
+
@Override
- public void onChange(boolean selfChange) {
- // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS or
- // LOCK_SCREEN_SHOW_NOTIFICATIONS, so we just dump our cache ...
- mUsersAllowingPrivateNotifications.clear();
- mUsersAllowingNotifications.clear();
- // ... and refresh all the notifications
- updateLockscreenNotificationSetting();
- notifyNotificationStateChanged();
+ public void onChange(boolean selfChange, Collection<Uri> uris, int flags) {
+ if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
+ @SuppressLint("MissingPermission")
+ List<UserInfo> users = mUserManager.getUsers();
+ for (int i = users.size() - 1; i >= 0; i--) {
+ onChange(selfChange, uris, flags,users.get(i).getUserHandle());
+ }
+ } else {
+ // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS or
+ // LOCK_SCREEN_SHOW_NOTIFICATIONS, so we just dump our cache ...
+ mUsersAllowingPrivateNotifications.clear();
+ mUsersAllowingNotifications.clear();
+ // ... and refresh all the notifications
+ updateLockscreenNotificationSetting();
+ notifyNotificationStateChanged();
+ }
+ }
+
+ // Note: even though this is an override, this method is not called by the OS
+ // since we're not in system_server. We are using it internally for cases when
+ // we have a single user id available (e.g. from USER_ADDED).
+ @Override
+ public void onChange(boolean selfChange, Collection<Uri> uris,
+ int flags, UserHandle user) {
+ if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
+ boolean changed = false;
+ for (Uri uri: uris) {
+ if (SHOW_LOCKSCREEN.equals(uri)) {
+ changed |= updateUserShowSettings(user.getIdentifier());
+ } else if (SHOW_PRIVATE_LOCKSCREEN.equals(uri)) {
+ changed |= updateUserShowPrivateSettings(user.getIdentifier());
+ }
+ }
+
+ if (mCurrentUserId == user.getIdentifier()) {
+ changed |= updateLockscreenNotificationSetting();
+ }
+ if (changed) {
+ notifyNotificationStateChanged();
+ }
+ }
}
};
@@ -268,23 +376,26 @@
};
mContext.getContentResolver().registerContentObserver(
- mSecureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
+ SHOW_LOCKSCREEN, false,
mLockscreenSettingsObserver,
UserHandle.USER_ALL);
mContext.getContentResolver().registerContentObserver(
- mSecureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
+ SHOW_PRIVATE_LOCKSCREEN,
true,
mLockscreenSettingsObserver,
UserHandle.USER_ALL);
- mContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
- mSettingsObserver);
+ if (!mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
+ mSettingsObserver);
+ }
mBroadcastDispatcher.registerReceiver(mAllUsersReceiver,
new IntentFilter(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
- null /* handler */, UserHandle.ALL);
+ mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)
+ ? mBackgroundExecutor : null, UserHandle.ALL);
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_ADDED);
@@ -305,7 +416,23 @@
mCurrentUserId = mUserTracker.getUserId(); // in case we reg'd receiver too late
updateCurrentProfilesCache();
- mSettingsObserver.onChange(false); // set up
+ if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
+ // Set up
+ mBackgroundHandler.post(() -> {
+ @SuppressLint("MissingPermission") List<UserInfo> users = mUserManager.getUsers();
+ for (int i = users.size() - 1; i >= 0; i--) {
+ initValuesForUser(users.get(i).id);
+ }
+ });
+ } else {
+ mSettingsObserver.onChange(false); // set up
+ }
+ }
+
+ private void initValuesForUser(@UserIdInt int userId) {
+ mLockscreenSettingsObserver.onChange(
+ false, mLockScreenUris, 0, UserHandle.of(userId));
+ updateDpcSettings(userId);
}
public boolean shouldShowLockscreenNotifications() {
@@ -322,17 +449,75 @@
mShowLockscreenNotifications = show;
}
- protected void updateLockscreenNotificationSetting() {
- final boolean show = mSecureSettings.getIntForUser(
- Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
- 1,
- mCurrentUserId) != 0;
- final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(
- null /* admin */, mCurrentUserId);
- final boolean allowedByDpm = (dpmFlags
- & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0;
+ protected boolean updateLockscreenNotificationSetting() {
+ boolean show;
+ boolean allowedByDpm;
+ if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
+ show = mUsersUsersAllowingNotifications.get(mCurrentUserId)
+ && mKeyguardAllowingNotifications;
+ // If DPC never notified us about a user, that means they have no policy for the user,
+ // and they allow the behavior
+ allowedByDpm = mUsersDpcAllowingNotifications.get(mCurrentUserId, true);
+ } else {
+ show = mSecureSettings.getIntForUser(
+ LOCK_SCREEN_SHOW_NOTIFICATIONS,
+ 1,
+ mCurrentUserId) != 0;
+ final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(
+ null /* admin */, mCurrentUserId);
+ allowedByDpm = (dpmFlags
+ & KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0;
+ }
+
+ final boolean oldValue = mShowLockscreenNotifications;
setShowLockscreenNotifications(show && allowedByDpm);
+
+ return oldValue != mShowLockscreenNotifications;
+ }
+
+ @WorkerThread
+ protected boolean updateDpcSettings(int userId) {
+ boolean originalAllowLockscreen = mUsersDpcAllowingNotifications.get(userId);
+ boolean originalAllowPrivate = mUsersDpcAllowingPrivateNotifications.get(userId);
+ final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(
+ null /* admin */, userId);
+ final boolean allowedLockscreen = (dpmFlags & KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0;
+ final boolean allowedPrivate = (dpmFlags & KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS) == 0;
+ mUsersDpcAllowingNotifications.put(userId, allowedLockscreen);
+ mUsersDpcAllowingPrivateNotifications.put(userId, allowedPrivate);
+ return (originalAllowLockscreen != allowedLockscreen)
+ || (originalAllowPrivate != allowedPrivate);
+ }
+
+ @WorkerThread
+ private boolean updateUserShowSettings(int userId) {
+ boolean originalAllowLockscreen = mUsersUsersAllowingNotifications.get(userId);
+ boolean newAllowLockscreen = mSecureSettings.getIntForUser(
+ LOCK_SCREEN_SHOW_NOTIFICATIONS,
+ 1,
+ userId) != 0;
+ mUsersUsersAllowingNotifications.put(userId, newAllowLockscreen);
+ boolean keyguardChanged = updateGlobalKeyguardSettings();
+ return (newAllowLockscreen != originalAllowLockscreen) || keyguardChanged;
+ }
+
+ @WorkerThread
+ private boolean updateUserShowPrivateSettings(int userId) {
+ boolean originalValue = mUsersUsersAllowingPrivateNotifications.get(userId);
+ boolean newValue = mSecureSettings.getIntForUser(
+ LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+ 0,
+ userId) != 0;
+ mUsersUsersAllowingPrivateNotifications.put(userId, newValue);
+ return (newValue != originalValue);
+ }
+
+ @WorkerThread
+ private boolean updateGlobalKeyguardSettings() {
+ final boolean oldValue = mKeyguardAllowingNotifications;
+ mKeyguardAllowingNotifications = mKeyguardManager.getPrivateNotificationsAllowed();
+ return oldValue != mKeyguardAllowingNotifications;
}
/**
@@ -340,21 +525,41 @@
* when the lockscreen is in "public" (secure & locked) mode?
*/
public boolean userAllowsPrivateNotificationsInPublic(int userHandle) {
- if (userHandle == UserHandle.USER_ALL) {
- return true;
- }
+ if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
+ if (userHandle == UserHandle.USER_ALL) {
+ userHandle = mCurrentUserId;
+ }
+ if (mUsersUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
+ // TODO(b/301955929): STOP_SHIP (stop flag flip): remove this read and use a safe
+ // default value before moving to 'released'
+ Log.wtf(TAG, "Asking for redact notifs setting too early", new Throwable());
+ updateUserShowPrivateSettings(userHandle);
+ }
+ if (mUsersDpcAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
+ // TODO(b/301955929): STOP_SHIP (stop flag flip): remove this read and use a safe
+ // default value before moving to 'released'
+ Log.wtf(TAG, "Asking for redact notifs dpm override too early", new Throwable());
+ updateDpcSettings(userHandle);
+ }
+ return mUsersUsersAllowingPrivateNotifications.get(userHandle)
+ && mUsersDpcAllowingPrivateNotifications.get(userHandle);
+ } else {
+ if (userHandle == UserHandle.USER_ALL) {
+ return true;
+ }
- if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
- final boolean allowedByUser = 0 != mSecureSettings.getIntForUser(
- Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle);
- final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle,
- DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
- final boolean allowed = allowedByUser && allowedByDpm;
- mUsersAllowingPrivateNotifications.append(userHandle, allowed);
- return allowed;
- }
+ if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
+ final boolean allowedByUser = 0 != mSecureSettings.getIntForUser(
+ LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle);
+ final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle,
+ KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
+ final boolean allowed = allowedByUser && allowedByDpm;
+ mUsersAllowingPrivateNotifications.append(userHandle, allowed);
+ return allowed;
+ }
- return mUsersAllowingPrivateNotifications.get(userHandle);
+ return mUsersAllowingPrivateNotifications.get(userHandle);
+ }
}
/**
@@ -406,21 +611,44 @@
* "public" (secure & locked) mode?
*/
public boolean userAllowsNotificationsInPublic(int userHandle) {
- if (isCurrentProfile(userHandle) && userHandle != mCurrentUserId) {
- return true;
- }
+ if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
+ // Unlike 'show private', settings does not show a copy of this setting for each
+ // profile, so it inherits from the parent user.
+ if (userHandle == UserHandle.USER_ALL || mCurrentManagedProfiles.contains(userHandle)) {
+ userHandle = mCurrentUserId;
+ }
+ if (mUsersUsersAllowingNotifications.indexOfKey(userHandle) < 0) {
+ // TODO(b/301955929): STOP_SHIP (stop flag flip): remove this read and use a safe
+ // default value before moving to 'released'
+ Log.wtf(TAG, "Asking for show notifs setting too early", new Throwable());
+ updateUserShowSettings(userHandle);
+ }
+ if (mUsersDpcAllowingNotifications.indexOfKey(userHandle) < 0) {
+ // TODO(b/301955929): STOP_SHIP (stop flag flip): remove this read and use a safe
+ // default value before moving to 'released'
+ Log.wtf(TAG, "Asking for show notifs dpm override too early", new Throwable());
+ updateDpcSettings(userHandle);
+ }
+ return mUsersUsersAllowingNotifications.get(userHandle)
+ && mUsersDpcAllowingNotifications.get(userHandle)
+ && mKeyguardAllowingNotifications;
+ } else {
+ if (isCurrentProfile(userHandle) && userHandle != mCurrentUserId) {
+ return true;
+ }
- if (mUsersAllowingNotifications.indexOfKey(userHandle) < 0) {
- final boolean allowedByUser = 0 != mSecureSettings.getIntForUser(
- Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, userHandle);
- final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle,
- DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
- final boolean allowedBySystem = mKeyguardManager.getPrivateNotificationsAllowed();
- final boolean allowed = allowedByUser && allowedByDpm && allowedBySystem;
- mUsersAllowingNotifications.append(userHandle, allowed);
- return allowed;
+ if (mUsersAllowingNotifications.indexOfKey(userHandle) < 0) {
+ final boolean allowedByUser = 0 != mSecureSettings.getIntForUser(
+ LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, userHandle);
+ final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle,
+ KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
+ final boolean allowedBySystem = mKeyguardManager.getPrivateNotificationsAllowed();
+ final boolean allowed = allowedByUser && allowedByDpm && allowedBySystem;
+ mUsersAllowingNotifications.append(userHandle, allowed);
+ return allowed;
+ }
+ return mUsersAllowingNotifications.get(userHandle);
}
- return mUsersAllowingNotifications.get(userHandle);
}
/** @return true if the entry needs redaction when on the lockscreen. */
@@ -451,9 +679,15 @@
return true;
}
NotificationEntry entry = mCommonNotifCollectionLazy.get().getEntry(key);
- return entry != null
- && entry.getRanking().getLockscreenVisibilityOverride()
- == Notification.VISIBILITY_PRIVATE;
+ if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
+ return entry != null
+ && entry.getRanking().getChannel().getLockscreenVisibility()
+ == Notification.VISIBILITY_PRIVATE;
+ } else {
+ return entry != null
+ && entry.getRanking().getLockscreenVisibilityOverride()
+ == Notification.VISIBILITY_PRIVATE;
+ }
}
private void updateCurrentProfilesCache() {
@@ -491,20 +725,6 @@
}
/**
- * If any managed/work profiles are in public mode.
- */
- public boolean isAnyManagedProfilePublicMode() {
- synchronized (mLock) {
- for (int i = mCurrentManagedProfiles.size() - 1; i >= 0; i--) {
- if (isLockscreenPublicMode(mCurrentManagedProfiles.valueAt(i).id)) {
- return true;
- }
- }
- }
- return false;
- }
-
- /**
* Returns the current user id. This can change if the user is switched.
*/
public int getCurrentUserId() {
@@ -581,8 +801,16 @@
}
private void notifyNotificationStateChanged() {
- for (NotificationStateChangedListener listener : mNotifStateChangedListeners) {
- listener.onNotificationStateChanged();
+ if (!Looper.getMainLooper().isCurrentThread()) {
+ mMainHandler.post(() -> {
+ for (NotificationStateChangedListener listener : mNotifStateChangedListeners) {
+ listener.onNotificationStateChanged();
+ }
+ });
+ } else {
+ for (NotificationStateChangedListener listener : mNotifStateChangedListeners) {
+ listener.onNotificationStateChanged();
+ }
}
}
@@ -620,5 +848,15 @@
pw.println(mUsersInLockdownLatestResult);
pw.print(" mShouldHideNotifsLatestResult=");
pw.println(mShouldHideNotifsLatestResult);
+ pw.print(" mUsersDpcAllowingNotifications=");
+ pw.println(mUsersDpcAllowingNotifications);
+ pw.print(" mUsersUsersAllowingNotifications=");
+ pw.println(mUsersUsersAllowingNotifications);
+ pw.print(" mKeyguardAllowingNotifications=");
+ pw.println(mKeyguardAllowingNotifications);
+ pw.print(" mUsersDpcAllowingPrivateNotifications=");
+ pw.println(mUsersDpcAllowingPrivateNotifications);
+ pw.print(" mUsersUsersAllowingPrivateNotifications=");
+ pw.println(mUsersUsersAllowingPrivateNotifications);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 09ad55e..f8c049e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -37,11 +37,11 @@
import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.SystemBarUtils;
-import com.android.systemui.res.R;
import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.flags.Flags;
-import com.android.systemui.flags.ViewRefactorFlag;
+import com.android.systemui.flags.RefactorFlag;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
+import com.android.systemui.res.R;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.SourceType;
@@ -96,10 +96,10 @@
private float mCornerAnimationDistance;
private NotificationShelfController mController;
private float mActualWidth = -1;
- private final ViewRefactorFlag mSensitiveRevealAnim =
- new ViewRefactorFlag(Flags.SENSITIVE_REVEAL_ANIM);
- private final ViewRefactorFlag mShelfRefactor =
- new ViewRefactorFlag(Flags.NOTIFICATION_SHELF_REFACTOR);
+ private final RefactorFlag mSensitiveRevealAnim =
+ RefactorFlag.forView(Flags.SENSITIVE_REVEAL_ANIM);
+ private final RefactorFlag mShelfRefactor =
+ RefactorFlag.forView(Flags.NOTIFICATION_SHELF_REFACTOR);
private boolean mCanModifyColorOfNotifications;
private boolean mCanInteract;
private NotificationStackScrollLayout mHostLayout;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index 3640ae0..4ea7026 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -31,22 +31,22 @@
import com.android.app.animation.Interpolators
import com.android.systemui.Dumpable
import com.android.systemui.Gefingerpoken
-import com.android.systemui.res.R
import com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.res.R
import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.HeadsUpManager
import java.io.PrintWriter
import javax.inject.Inject
import kotlin.math.max
@@ -63,7 +63,7 @@
context: Context,
private val wakeUpCoordinator: NotificationWakeUpCoordinator,
private val bypassController: KeyguardBypassController,
- private val headsUpManager: HeadsUpManagerPhone,
+ private val headsUpManager: HeadsUpManager,
private val roundnessManager: NotificationRoundnessManager,
configurationController: ConfigurationController,
private val statusBarStateController: StatusBarStateController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index f616b91..3a4ad0e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -51,6 +51,7 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.Interpolator;
+import androidx.annotation.Nullable;
import androidx.core.graphics.ColorUtils;
import com.android.app.animation.Interpolators;
@@ -959,12 +960,17 @@
}
public void setDozing(boolean dozing, boolean fade, long delay) {
+ setDozing(dozing, fade, delay, /* onChildCompleted= */ null);
+ }
+
+ public void setDozing(boolean dozing, boolean fade, long delay,
+ @Nullable Runnable endRunnable) {
mDozer.setDozing(f -> {
mDozeAmount = f;
updateDecorColor();
updateIconColor();
updateAllowAnimation();
- }, dozing, fade, delay, this);
+ }, dozing, fade, delay, this, endRunnable);
}
private void updateAllowAnimation() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 37a4ef1..ef45d2b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -18,6 +18,7 @@
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_FROM_AOD;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_TO_AOD;
+import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -43,15 +44,17 @@
import com.android.keyguard.KeyguardClockSwitch;
import com.android.systemui.DejankUtils;
import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.res.R;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.policy.CallbackController;
import com.android.systemui.util.Compile;
+import dagger.Lazy;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Comparator;
@@ -95,6 +98,7 @@
private final ArrayList<RankedListener> mListeners = new ArrayList<>();
private final UiEventLogger mUiEventLogger;
private final InteractionJankMonitor mInteractionJankMonitor;
+ private final Lazy<ShadeInteractor> mShadeInteractorLazy;
private int mState;
private int mLastState;
private int mUpcomingState;
@@ -158,15 +162,13 @@
UiEventLogger uiEventLogger,
DumpManager dumpManager,
InteractionJankMonitor interactionJankMonitor,
- ShadeExpansionStateManager shadeExpansionStateManager
- ) {
+ Lazy<ShadeInteractor> shadeInteractorLazy) {
mUiEventLogger = uiEventLogger;
mInteractionJankMonitor = interactionJankMonitor;
+ mShadeInteractorLazy = shadeInteractorLazy;
for (int i = 0; i < HISTORY_SIZE; i++) {
mHistoricalRecords[i] = new HistoricalState();
}
- shadeExpansionStateManager.addFullExpansionListener(this::onShadeExpansionFullyChanged);
-
dumpManager.registerDumpable(this);
}
@@ -336,6 +338,8 @@
&& (view != null && view.isAttachedToWindow())) {
mView = view;
mClockSwitchView = view.findViewById(R.id.keyguard_clock_container);
+ collectFlow(mView, mShadeInteractorLazy.get().isAnyExpanded(),
+ this::onShadeOrQsExpanded);
}
mDozeAmountTarget = dozeAmount;
if (animated) {
@@ -345,7 +349,7 @@
}
}
- private void onShadeExpansionFullyChanged(Boolean isExpanded) {
+ private void onShadeOrQsExpanded(Boolean isExpanded) {
if (mIsExpanded != isExpanded) {
mIsExpanded = isExpanded;
String tag = getClass().getSimpleName() + "#setIsExpanded";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java
index 1196211..595ab70 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java
@@ -21,6 +21,21 @@
public interface StatusIconDisplayable extends DarkReceiver {
String getSlot();
void setStaticDrawableColor(int color);
+
+ /**
+ * For a layer drawable, or one that has a background, {@code tintColor} should be used as the
+ * background tint for the container, while {@code contrastColor} can be used as the foreground
+ * drawable's tint so that it is visible on the background. Essentially, tintColor should apply
+ * to the portion of the icon that borders the underlying window content (status bar's
+ * background), and the contrastColor only need be used to distinguish from the tintColor.
+ *
+ * Defaults to calling {@link #setStaticDrawableColor(int)} with only the tint color, so modern
+ * callers can just call this method and still get the default behavior.
+ */
+ default void setStaticDrawableColor(int tintColor, int contrastColor) {
+ setStaticDrawableColor(tintColor);
+ }
+
void setDecorColor(int color);
/** Sets the visible state that this displayable should be. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
index 9db61c6..fc84973 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
@@ -83,6 +83,8 @@
import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.util.CarrierConfigTracker;
+import dalvik.annotation.optimization.NeverCompile;
+
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -1154,6 +1156,7 @@
}
/** */
+ @NeverCompile
public void dump(PrintWriter pw, String[] args) {
pw.println("NetworkController state:");
pw.println(" mUserSetup=" + mUserSetup);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java
index 167efc7..dc0eb7b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java
@@ -24,6 +24,8 @@
import android.view.View;
import android.widget.ImageView;
+import androidx.annotation.Nullable;
+
import com.android.app.animation.Interpolators;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
@@ -81,6 +83,11 @@
public void setDozing(Consumer<Float> listener, boolean dozing,
boolean animate, long delay, View view) {
+ setDozing(listener, dozing, animate, delay, view, /* endRunnable= */ null);
+ }
+
+ public void setDozing(Consumer<Float> listener, boolean dozing,
+ boolean animate, long delay, View view, @Nullable Runnable endRunnable) {
if (animate) {
startIntensityAnimation(a -> listener.accept((Float) a.getAnimatedValue()), dozing,
delay,
@@ -89,6 +96,9 @@
@Override
public void onAnimationEnd(Animator animation) {
view.setTag(DOZE_ANIMATOR_TAG, null);
+ if (endRunnable != null) {
+ endRunnable.run();
+ }
}
@Override
@@ -102,6 +112,9 @@
animator.cancel();
}
listener.accept(dozing ? 1f : 0f);
+ if (endRunnable != null) {
+ endRunnable.run();
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
index c62546f6..756151b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
@@ -24,7 +24,7 @@
import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
+import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.statusbar.policy.HeadsUpUtil
import kotlin.math.ceil
import kotlin.math.max
@@ -35,7 +35,7 @@
class NotificationLaunchAnimatorControllerProvider(
private val notificationExpansionRepository: NotificationExpansionRepository,
private val notificationListContainer: NotificationListContainer,
- private val headsUpManager: HeadsUpManagerPhone,
+ private val headsUpManager: HeadsUpManager,
private val jankMonitor: InteractionJankMonitor
) {
@JvmOverloads
@@ -62,7 +62,7 @@
class NotificationLaunchAnimatorController(
private val notificationExpansionRepository: NotificationExpansionRepository,
private val notificationListContainer: NotificationListContainer,
- private val headsUpManager: HeadsUpManagerPhone,
+ private val headsUpManager: HeadsUpManager,
private val notification: ExpandableNotificationRow,
private val jankMonitor: InteractionJankMonitor,
private val onFinishAnimationCallback: Runnable?
@@ -152,16 +152,17 @@
}
}
- private val headsUpNotificationRow: ExpandableNotificationRow? get() {
- val summaryEntry = notificationEntry.parent?.summary
+ private val headsUpNotificationRow: ExpandableNotificationRow?
+ get() {
+ val summaryEntry = notificationEntry.parent?.summary
- return when {
- headsUpManager.isAlerting(notificationKey) -> notification
- summaryEntry == null -> null
- headsUpManager.isAlerting(summaryEntry.key) -> summaryEntry.row
- else -> null
+ return when {
+ headsUpManager.isAlerting(notificationKey) -> notification
+ summaryEntry == null -> null
+ headsUpManager.isAlerting(summaryEntry.key) -> summaryEntry.row
+ else -> null
+ }
}
- }
private fun removeHun(animate: Boolean) {
val row = headsUpNotificationRow ?: return
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
index 328a741..3e9c6fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
@@ -3,10 +3,10 @@
import android.util.FloatProperty
import android.view.View
import androidx.annotation.FloatRange
-import com.android.systemui.res.R
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.flags.ViewRefactorFlag
+import com.android.systemui.flags.RefactorFlag
+import com.android.systemui.res.R
import com.android.systemui.statusbar.notification.stack.AnimationProperties
import com.android.systemui.statusbar.notification.stack.StackStateAnimator
import kotlin.math.abs
@@ -323,7 +323,7 @@
internal var maxRadius = maxRadius
private set
- internal val newHeadsUpAnim = ViewRefactorFlag(featureFlags, Flags.IMPROVED_HUN_ANIMATIONS)
+ internal val newHeadsUpAnim = RefactorFlag.forView(Flags.IMPROVED_HUN_ANIMATIONS, featureFlags)
/** Animatable for top roundness */
private val topAnimatable = topAnimatable(roundable)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
index 07eb8a00..2d83970 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
@@ -37,8 +37,8 @@
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider
-import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderImpl
import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider
+import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationListInteractor
import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.statusbar.policy.headsUpEvents
import com.android.systemui.util.asIndenting
@@ -85,7 +85,7 @@
@Application private val scope: CoroutineScope,
private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider,
private val secureSettings: SecureSettings,
- private val seenNotifsProvider: SeenNotificationsProviderImpl,
+ private val notificationListInteractor: NotificationListInteractor,
private val statusBarStateController: StatusBarStateController,
) : Coordinator, Dumpable {
@@ -351,7 +351,7 @@
override fun onCleanup() {
logger.logProviderHasFilteredOutSeenNotifs(hasFilteredAnyNotifs)
- seenNotifsProvider.hasFilteredOutSeenNotifications = hasFilteredAnyNotifs
+ notificationListInteractor.setHasFilteredOutSeenNotifications(hasFilteredAnyNotifs)
hasFilteredAnyNotifs = false
}
}
@@ -388,8 +388,8 @@
override fun dump(pw: PrintWriter, args: Array<out String>) =
with(pw.asIndenting()) {
println(
- "seenNotifsProvider.hasFilteredOutSeenNotifications=" +
- seenNotifsProvider.hasFilteredOutSeenNotifications
+ "notificationListInteractor.hasFilteredOutSeenNotifications.value=" +
+ notificationListInteractor.hasFilteredOutSeenNotifications.value
)
println("unseen notifications:")
indentIfPossible {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
index 657c394d..c0f674846 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
@@ -28,6 +28,7 @@
import com.android.systemui.statusbar.notification.row.NotificationGutsManager
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.Compile
+import com.android.systemui.util.traceSection
import javax.inject.Inject
/**
@@ -122,8 +123,10 @@
private fun updateNotificationsOnUiModeChanged() {
log { "ViewConfigCoordinator.updateNotificationsOnUiModeChanged()" }
- mPipeline?.allNotifs?.forEach { entry ->
- entry.row?.onUiModeChanged()
+ traceSection("updateNotifOnUiModeChanged") {
+ mPipeline?.allNotifs?.forEach { entry ->
+ entry.row?.onUiModeChanged()
+ }
}
}
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/collection/provider/SeenNotificationsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SeenNotificationsProvider.kt
deleted file mode 100644
index cff47e2..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SeenNotificationsProvider.kt
+++ /dev/null
@@ -1,41 +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.statusbar.notification.collection.provider
-
-import com.android.systemui.dagger.SysUISingleton
-import dagger.Binds
-import dagger.Module
-import javax.inject.Inject
-
-/** Keeps track of whether "seen" notification content has been filtered out of the shade. */
-interface SeenNotificationsProvider {
- /** Are any already-seen notifications currently filtered out of the shade? */
- val hasFilteredOutSeenNotifications: Boolean
-}
-
-@Module
-interface SeenNotificationsProviderModule {
- @Binds
- fun bindSeenNotificationsProvider(
- impl: SeenNotificationsProviderImpl
- ): SeenNotificationsProvider
-}
-
-@SysUISingleton
-class SeenNotificationsProviderImpl @Inject constructor() : SeenNotificationsProvider {
- override var hasFilteredOutSeenNotifications: Boolean = false
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
index 59fc387..1a88815 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
@@ -231,18 +231,24 @@
fun getChildCount(): Int = controller.getChildCount()
fun addChildAt(child: ShadeNode, index: Int) {
- controller.addChildAt(child.controller, index)
- child.controller.onViewAdded()
+ traceSection("ShadeNode#addChildAt") {
+ controller.addChildAt(child.controller, index)
+ child.controller.onViewAdded()
+ }
}
fun moveChildTo(child: ShadeNode, index: Int) {
- controller.moveChildTo(child.controller, index)
- child.controller.onViewMoved()
+ traceSection("ShadeNode#moveChildTo") {
+ controller.moveChildTo(child.controller, index)
+ child.controller.onViewMoved()
+ }
}
fun removeChild(child: ShadeNode, isTransfer: Boolean) {
- controller.removeChild(child.controller, isTransfer)
- child.controller.onViewRemoved()
+ traceSection("ShadeNode#removeChild") {
+ controller.removeChild(child.controller, isTransfer)
+ child.controller.onViewRemoved()
+ }
}
fun offerToKeepInParentForAnimation(): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 8ee0de6..8561869 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -20,10 +20,10 @@
import com.android.internal.jank.InteractionJankMonitor;
import com.android.systemui.CoreStartable;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
import com.android.systemui.shade.ShadeEventsModule;
import com.android.systemui.statusbar.NotificationListener;
@@ -45,7 +45,6 @@
import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider;
import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProviderImpl;
import com.android.systemui.statusbar.notification.collection.provider.NotificationVisibilityProviderImpl;
-import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderModule;
import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManagerImpl;
@@ -54,6 +53,7 @@
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager;
import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
+import com.android.systemui.statusbar.notification.data.NotificationDataLayerModule;
import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository;
import com.android.systemui.statusbar.notification.icon.ConversationIconManager;
import com.android.systemui.statusbar.notification.icon.IconManager;
@@ -74,9 +74,9 @@
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModelModule;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.kotlin.JavaAdapter;
import dagger.Binds;
@@ -95,8 +95,8 @@
@Module(includes = {
CoordinatorsModule.class,
KeyguardNotificationVisibilityProviderModule.class,
- SeenNotificationsProviderModule.class,
ShadeEventsModule.class,
+ NotificationDataLayerModule.class,
NotifPipelineChoreographerModule.class,
NotificationSectionHeadersModule.class,
NotificationListViewModelModule.class,
@@ -206,7 +206,7 @@
static NotificationLaunchAnimatorControllerProvider provideNotifLaunchAnimControllerProvider(
NotificationExpansionRepository notificationExpansionRepository,
NotificationListContainer notificationListContainer,
- HeadsUpManagerPhone headsUpManager,
+ HeadsUpManager headsUpManager,
InteractionJankMonitor jankMonitor) {
return new NotificationLaunchAnimatorControllerProvider(
notificationExpansionRepository,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt
new file mode 100644
index 0000000..5435fb5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.data
+
+import com.android.systemui.statusbar.notification.data.repository.NotificationsKeyguardStateRepositoryModule
+import dagger.Module
+
+@Module(includes = [NotificationsKeyguardStateRepositoryModule::class])
+interface NotificationDataLayerModule
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepository.kt
new file mode 100644
index 0000000..cf03d1c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepository.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.data.repository
+
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+
+/** View-states pertaining to notifications on the keyguard. */
+interface NotificationsKeyguardViewStateRepository {
+ /** Are notifications fully hidden from view? */
+ val areNotificationsFullyHidden: Flow<Boolean>
+
+ /** Is a pulse expansion occurring? */
+ val isPulseExpanding: Flow<Boolean>
+}
+
+@Module
+interface NotificationsKeyguardStateRepositoryModule {
+ @Binds
+ fun bindImpl(
+ impl: NotificationsKeyguardViewStateRepositoryImpl
+ ): NotificationsKeyguardViewStateRepository
+}
+
+@SysUISingleton
+class NotificationsKeyguardViewStateRepositoryImpl
+@Inject
+constructor(
+ wakeUpCoordinator: NotificationWakeUpCoordinator,
+) : NotificationsKeyguardViewStateRepository {
+ override val areNotificationsFullyHidden: Flow<Boolean> = conflatedCallbackFlow {
+ val listener =
+ object : NotificationWakeUpCoordinator.WakeUpListener {
+ override fun onFullyHiddenChanged(isFullyHidden: Boolean) {
+ trySend(isFullyHidden)
+ }
+ }
+ trySend(wakeUpCoordinator.notificationsFullyHidden)
+ wakeUpCoordinator.addListener(listener)
+ awaitClose { wakeUpCoordinator.removeListener(listener) }
+ }
+
+ override val isPulseExpanding: Flow<Boolean> = conflatedCallbackFlow {
+ val listener =
+ object : NotificationWakeUpCoordinator.WakeUpListener {
+ override fun onPulseExpansionChanged(expandingChanged: Boolean) {
+ trySend(expandingChanged)
+ }
+ }
+ trySend(wakeUpCoordinator.isPulseExpanding())
+ wakeUpCoordinator.addListener(listener)
+ awaitClose { wakeUpCoordinator.removeListener(listener) }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt
new file mode 100644
index 0000000..87b8e55
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt
@@ -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 com.android.systemui.statusbar.notification.domain.interactor
+
+import com.android.systemui.statusbar.notification.data.repository.NotificationsKeyguardViewStateRepository
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+/** Domain logic pertaining to notifications on the keyguard. */
+class NotificationsKeyguardInteractor
+@Inject
+constructor(
+ repository: NotificationsKeyguardViewStateRepository,
+) {
+ /** Is a pulse expansion occurring? */
+ val isPulseExpanding: Flow<Boolean> = repository.isPulseExpanding
+
+ /** Are notifications fully hidden from view? */
+ val areNotificationsFullyHidden: Flow<Boolean> = repository.areNotificationsFullyHidden
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
similarity index 91%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
index 26db5f2..e74b3fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
@@ -11,10 +11,10 @@
* 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
+ * limitations under the License.
*/
-package com.android.systemui.statusbar.notification.row;
+package com.android.systemui.statusbar.notification.footer.ui.view;
import static android.graphics.PorterDuff.Mode.SRC_ATOP;
@@ -35,6 +35,8 @@
import com.android.settingslib.Utils;
import com.android.systemui.res.R;
+import com.android.systemui.statusbar.notification.row.FooterViewButton;
+import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.ViewState;
import com.android.systemui.util.DumpUtilsKt;
@@ -93,6 +95,7 @@
updateColors();
}
+ /** Show a message instead of the footer buttons. */
public void setFooterLabelVisible(boolean isVisible) {
if (isVisible) {
mManageButton.setVisibility(View.GONE);
@@ -105,14 +108,22 @@
}
}
+ /** Set onClickListener for the manage/history button. */
public void setManageButtonClickListener(OnClickListener listener) {
mManageButton.setOnClickListener(listener);
}
+ /** Set onClickListener for the clear all (end) button. */
public void setClearAllButtonClickListener(OnClickListener listener) {
mClearAllButton.setOnClickListener(listener);
}
+ /**
+ * Whether the touch is outside the Clear all button.
+ *
+ * TODO(b/293167744): This is an artifact from the time when we could press underneath the
+ * shade to dismiss it. Check if it's safe to remove.
+ */
public boolean isOnEmptySpace(float touchX, float touchY) {
return touchX < mContent.getX()
|| touchX > mContent.getX() + mContent.getWidth()
@@ -120,6 +131,7 @@
|| touchY > mContent.getY() + mContent.getHeight();
}
+ /** Show "History" instead of "Manage" on the start button. */
public void showHistory(boolean showHistory) {
if (mShowHistory == showHistory) {
return;
@@ -141,6 +153,7 @@
.setCompoundDrawablesRelative(mSeenNotifsFilteredIcon, null, null, null);
}
+ /** Whether the start button shows "History" (true) or "Manage" (false). */
public boolean isHistoryShown() {
return mShowHistory;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt
index 5cc5e75..50efbb5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt
@@ -26,26 +26,21 @@
import androidx.annotation.ColorInt
import androidx.annotation.VisibleForTesting
import androidx.collection.ArrayMap
-import com.android.app.animation.Interpolators
import com.android.internal.statusbar.StatusBarIcon
import com.android.internal.util.ContrastColorUtil
import com.android.settingslib.Utils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.demomode.DemoMode
import com.android.systemui.demomode.DemoModeController
-import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
-import com.android.systemui.flags.Flags.NEW_AOD_TRANSITION
-import com.android.systemui.flags.ViewRefactorFlag
+import com.android.systemui.flags.RefactorFlag
import com.android.systemui.plugins.DarkIconDispatcher
-import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
-import com.android.systemui.statusbar.CrossFadeHelper
import com.android.systemui.statusbar.NotificationListener
import com.android.systemui.statusbar.NotificationMediaManager
import com.android.systemui.statusbar.NotificationShelfController
import com.android.systemui.statusbar.StatusBarIconView
-import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.notification.NotificationUtils
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
import com.android.systemui.statusbar.notification.collection.ListEntry
@@ -60,6 +55,7 @@
import com.android.systemui.statusbar.phone.NotificationIconAreaController
import com.android.systemui.statusbar.phone.NotificationIconContainer
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
+import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.wm.shell.bubbles.Bubbles
import java.util.Optional
@@ -79,9 +75,9 @@
@Inject
constructor(
private val context: Context,
- private val statusBarStateController: StatusBarStateController,
private val wakeUpCoordinator: NotificationWakeUpCoordinator,
private val bypassController: KeyguardBypassController,
+ private val configurationController: ConfigurationController,
private val mediaManager: NotificationMediaManager,
notificationListener: NotificationListener,
private val dozeParameters: DozeParameters,
@@ -89,7 +85,7 @@
private val bubblesOptional: Optional<Bubbles>,
demoModeController: DemoModeController,
darkIconDispatcher: DarkIconDispatcher,
- private val featureFlags: FeatureFlags,
+ private val featureFlags: FeatureFlagsClassic,
private val statusBarWindowController: StatusBarWindowController,
private val screenOffAnimationController: ScreenOffAnimationController,
private val shelfIconsViewModel: NotificationIconContainerShelfViewModel,
@@ -98,14 +94,12 @@
) :
NotificationIconAreaController,
DarkIconDispatcher.DarkReceiver,
- StatusBarStateController.StateListener,
NotificationWakeUpCoordinator.WakeUpListener,
DemoMode {
private val contrastColorUtil: ContrastColorUtil = ContrastColorUtil.getInstance(context)
private val updateStatusBarIcons = Runnable { updateStatusBarIcons() }
- private val shelfRefactor = ViewRefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR)
- private val statusViewMigrated = featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)
+ private val shelfRefactor = RefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR)
private val tintAreas = ArrayList<Rect>()
private var iconSize = 0
@@ -118,9 +112,7 @@
private var aodIcons: NotificationIconContainer? = null
private var aodBindJob: DisposableHandle? = null
private var aodIconAppearTranslation = 0
- private var animationsEnabled = false
private var aodIconTint = 0
- private var aodIconsVisible = false
private var showLowPriority = true
@VisibleForTesting
@@ -133,7 +125,6 @@
}
init {
- statusBarStateController.addCallback(this)
wakeUpCoordinator.addListener(this)
demoModeController.addCallback(this)
notificationListener.addNotificationSettingsListener(settingsListener)
@@ -157,9 +148,15 @@
}
this.aodIcons = aodIcons
this.aodIcons!!.setOnLockScreen(true)
- aodBindJob = NotificationIconContainerViewBinder.bind(aodIcons, aodIconsViewModel)
- updateAodIconsVisibility(animate = false, forceUpdate = changed)
- updateAnimations()
+ aodBindJob =
+ NotificationIconContainerViewBinder.bind(
+ aodIcons,
+ aodIconsViewModel,
+ configurationController,
+ dozeParameters,
+ featureFlags,
+ screenOffAnimationController,
+ )
if (changed) {
updateAodNotificationIcons()
}
@@ -171,7 +168,14 @@
override fun setShelfIcons(icons: NotificationIconContainer) {
if (shelfRefactor.expectEnabled()) {
- NotificationIconContainerViewBinder.bind(icons, shelfIconsViewModel)
+ NotificationIconContainerViewBinder.bind(
+ icons,
+ shelfIconsViewModel,
+ configurationController,
+ dozeParameters,
+ featureFlags,
+ screenOffAnimationController,
+ )
shelfIcons = icons
}
}
@@ -244,23 +248,7 @@
notificationIcons!!.setIsolatedIconLocation(iconDrawingRect, requireStateUpdate)
}
- override fun onDozingChanged(isDozing: Boolean) {
- if (aodIcons == null) {
- return
- }
- val animate = (dozeParameters.alwaysOn && !dozeParameters.displayNeedsBlanking)
- aodIcons!!.setDozing(isDozing, animate, 0)
- }
-
- override fun setAnimationsEnabled(enabled: Boolean) {
- animationsEnabled = enabled
- updateAnimations()
- }
-
- override fun onStateChanged(newState: Int) {
- updateAodIconsVisibility(animate = false, forceUpdate = false)
- updateAnimations()
- }
+ override fun setAnimationsEnabled(enabled: Boolean) = unsupported
override fun onThemeChanged() {
reloadAodColor()
@@ -271,53 +259,11 @@
return if (aodIcons == null) 0 else aodIcons!!.height
}
- @VisibleForTesting
- fun appearAodIcons() {
- if (aodIcons == null) {
- return
- }
- if (screenOffAnimationController.shouldAnimateAodIcons()) {
- if (!statusViewMigrated) {
- aodIcons!!.translationY = -aodIconAppearTranslation.toFloat()
- }
- aodIcons!!.alpha = 0f
- animateInAodIconTranslation()
- aodIcons!!
- .animate()
- .alpha(1f)
- .setInterpolator(Interpolators.LINEAR)
- .setDuration(AOD_ICONS_APPEAR_DURATION)
- .start()
- } else {
- aodIcons!!.alpha = 1.0f
- if (!statusViewMigrated) {
- aodIcons!!.translationY = 0f
- }
- }
- }
-
override fun onFullyHiddenChanged(isFullyHidden: Boolean) {
- var animate = true
- if (!bypassController.bypassEnabled) {
- animate = dozeParameters.alwaysOn && !dozeParameters.displayNeedsBlanking
- if (!featureFlags.isEnabled(NEW_AOD_TRANSITION)) {
- // We only want the appear animations to happen when the notifications get fully
- // hidden,
- // since otherwise the unhide animation overlaps
- animate = animate and isFullyHidden
- }
- }
- updateAodIconsVisibility(animate, false /* force */)
updateAodNotificationIcons()
updateAodIconColors()
}
- override fun onPulseExpansionChanged(expandingChanged: Boolean) {
- if (expandingChanged) {
- updateAodIconsVisibility(animate = true, forceUpdate = false)
- }
- }
-
override fun demoCommands(): List<String> {
val commands = ArrayList<String>()
commands.add(DemoMode.COMMAND_NOTIFICATIONS)
@@ -348,7 +294,14 @@
val layoutInflater = LayoutInflater.from(context)
notificationIconArea = inflateIconArea(layoutInflater)
notificationIcons = notificationIconArea?.findViewById(R.id.notificationIcons)
- NotificationIconContainerViewBinder.bind(notificationIcons!!, statusBarIconsViewModel)
+ NotificationIconContainerViewBinder.bind(
+ notificationIcons!!,
+ statusBarIconsViewModel,
+ configurationController,
+ dozeParameters,
+ featureFlags,
+ screenOffAnimationController,
+ )
}
private fun updateIconLayoutParams(context: Context) {
@@ -598,25 +551,6 @@
v.setDecorColor(tint)
}
- private fun updateAnimations() {
- val inShade = statusBarStateController.state == StatusBarState.SHADE
- if (aodIcons != null) {
- aodIcons!!.setAnimationsEnabled(animationsEnabled && !inShade)
- }
- notificationIcons!!.setAnimationsEnabled(animationsEnabled && inShade)
- }
-
- private fun animateInAodIconTranslation() {
- if (!statusViewMigrated) {
- aodIcons!!
- .animate()
- .setInterpolator(Interpolators.DECELERATE_QUINT)
- .translationY(0f)
- .setDuration(AOD_ICONS_APPEAR_DURATION)
- .start()
- }
- }
-
private fun reloadAodColor() {
aodIconTint =
Utils.getColorAttrDefaultColor(
@@ -639,70 +573,13 @@
}
}
- private fun updateAodIconsVisibility(animate: Boolean, forceUpdate: Boolean) {
- if (aodIcons == null) {
- return
- }
- var visible = (bypassController.bypassEnabled || wakeUpCoordinator.notificationsFullyHidden)
-
- // Hide the AOD icons if we're not in the KEYGUARD state unless the screen off animation is
- // playing, in which case we want them to be visible since we're animating in the AOD UI and
- // will be switching to KEYGUARD shortly.
- if (
- statusBarStateController.state != StatusBarState.KEYGUARD &&
- !screenOffAnimationController.shouldShowAodIconsWhenShade()
- ) {
- visible = false
- }
- if (visible && wakeUpCoordinator.isPulseExpanding() && !bypassController.bypassEnabled) {
- visible = false
- }
- if (aodIconsVisible != visible || forceUpdate) {
- aodIconsVisible = visible
- aodIcons!!.animate().cancel()
- if (animate) {
- if (featureFlags.isEnabled(NEW_AOD_TRANSITION)) {
- // Let's make sure the icon are translated to 0, since we cancelled it above
- animateInAodIconTranslation()
- if (aodIconsVisible) {
- CrossFadeHelper.fadeIn(aodIcons)
- } else {
- CrossFadeHelper.fadeOut(aodIcons)
- }
- } else {
- val wasFullyInvisible = aodIcons!!.visibility != View.VISIBLE
- if (aodIconsVisible) {
- if (wasFullyInvisible) {
- // No fading here, let's just appear the icons instead!
- aodIcons!!.visibility = View.VISIBLE
- aodIcons!!.alpha = 1.0f
- appearAodIcons()
- } else {
- // Let's make sure the icon are translated to 0, since we cancelled it
- // above
- animateInAodIconTranslation()
- // We were fading out, let's fade in instead
- CrossFadeHelper.fadeIn(aodIcons)
- }
- } else {
- // Let's make sure the icon are translated to 0, since we cancelled it above
- animateInAodIconTranslation()
- CrossFadeHelper.fadeOut(aodIcons)
- }
- }
- } else {
- aodIcons!!.alpha = 1.0f
- if (!statusViewMigrated) {
- aodIcons!!.translationY = 0f
- }
- aodIcons!!.visibility = if (visible) View.VISIBLE else View.INVISIBLE
- }
- }
- }
-
companion object {
- private const val AOD_ICONS_APPEAR_DURATION: Long = 200
-
@ColorInt private val DEFAULT_AOD_ICON_COLOR = -0x1
+
+ val unsupported: Nothing
+ get() =
+ error(
+ "Code path not supported when NOTIFICATION_ICON_CONTAINER_REFACTOR is disabled"
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
index 8293bb3..0d2f00a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
@@ -15,19 +15,172 @@
*/
package com.android.systemui.statusbar.notification.icon.ui.viewbinder
+import android.content.res.Resources
+import android.view.View
+import androidx.annotation.DimenRes
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
+import com.android.app.animation.Interpolators
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.CrossFadeHelper
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel
+import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.NotificationIconContainer
+import com.android.systemui.statusbar.phone.ScreenOffAnimationController
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.onDensityOrFontScaleChanged
+import com.android.systemui.util.kotlin.stateFlow
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.launch
/** Binds a [NotificationIconContainer] to its [view model][NotificationIconContainerViewModel]. */
object NotificationIconContainerViewBinder {
fun bind(
view: NotificationIconContainer,
viewModel: NotificationIconContainerViewModel,
+ configurationController: ConfigurationController,
+ dozeParameters: DozeParameters,
+ featureFlags: FeatureFlagsClassic,
+ screenOffAnimationController: ScreenOffAnimationController,
): DisposableHandle {
- return view.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.CREATED) {} }
+ return view.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ launch { viewModel.animationsEnabled.collect(view::setAnimationsEnabled) }
+ launch {
+ viewModel.isDozing.collect { (isDozing, animate) ->
+ val animateIfNotBlanking = animate && !dozeParameters.displayNeedsBlanking
+ view.setDozing(isDozing, animateIfNotBlanking, /* delay= */ 0) {
+ viewModel.completeDozeAnimation()
+ }
+ }
+ }
+ // TODO(278765923): this should live where AOD is bound, not inside of the NIC
+ // view-binder
+ launch {
+ val iconAppearTranslation =
+ view.resources.getConfigAwareDimensionPixelSize(
+ this,
+ configurationController,
+ R.dimen.shelf_appear_translation,
+ )
+ bindVisibility(
+ viewModel,
+ view,
+ featureFlags,
+ screenOffAnimationController,
+ iconAppearTranslation,
+ ) {
+ viewModel.completeVisibilityAnimation()
+ }
+ }
+ }
+ }
}
+ private suspend fun bindVisibility(
+ viewModel: NotificationIconContainerViewModel,
+ view: NotificationIconContainer,
+ featureFlags: FeatureFlagsClassic,
+ screenOffAnimationController: ScreenOffAnimationController,
+ iconAppearTranslation: StateFlow<Int>,
+ onAnimationEnd: () -> Unit,
+ ) {
+ val statusViewMigrated = featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)
+ viewModel.isVisible.collect { (isVisible, animate) ->
+ view.animate().cancel()
+ when {
+ !animate -> {
+ view.alpha = 1f
+ if (!statusViewMigrated) {
+ view.translationY = 0f
+ }
+ view.visibility = if (isVisible) View.VISIBLE else View.INVISIBLE
+ }
+ featureFlags.isEnabled(Flags.NEW_AOD_TRANSITION) -> {
+ animateInIconTranslation(view, statusViewMigrated)
+ if (isVisible) {
+ CrossFadeHelper.fadeIn(view, onAnimationEnd)
+ } else {
+ CrossFadeHelper.fadeOut(view, onAnimationEnd)
+ }
+ }
+ !isVisible -> {
+ // Let's make sure the icon are translated to 0, since we cancelled it above
+ animateInIconTranslation(view, statusViewMigrated)
+ CrossFadeHelper.fadeOut(view, onAnimationEnd)
+ }
+ view.visibility != View.VISIBLE -> {
+ // No fading here, let's just appear the icons instead!
+ view.visibility = View.VISIBLE
+ view.alpha = 1f
+ appearIcons(
+ view,
+ animate = screenOffAnimationController.shouldAnimateAodIcons(),
+ iconAppearTranslation.value,
+ statusViewMigrated,
+ )
+ onAnimationEnd()
+ }
+ else -> {
+ // Let's make sure the icons are translated to 0, since we cancelled it above
+ animateInIconTranslation(view, statusViewMigrated)
+ // We were fading out, let's fade in instead
+ CrossFadeHelper.fadeIn(view, onAnimationEnd)
+ }
+ }
+ }
+ }
+
+ private fun appearIcons(
+ view: View,
+ animate: Boolean,
+ iconAppearTranslation: Int,
+ statusViewMigrated: Boolean,
+ ) {
+ if (animate) {
+ if (!statusViewMigrated) {
+ view.translationY = -iconAppearTranslation.toFloat()
+ }
+ view.alpha = 0f
+ animateInIconTranslation(view, statusViewMigrated)
+ view
+ .animate()
+ .alpha(1f)
+ .setInterpolator(Interpolators.LINEAR)
+ .setDuration(AOD_ICONS_APPEAR_DURATION)
+ .start()
+ } else {
+ view.alpha = 1.0f
+ if (!statusViewMigrated) {
+ view.translationY = 0f
+ }
+ }
+ }
+
+ private fun animateInIconTranslation(view: View, statusViewMigrated: Boolean) {
+ if (!statusViewMigrated) {
+ view
+ .animate()
+ .setInterpolator(Interpolators.DECELERATE_QUINT)
+ .translationY(0f)
+ .setDuration(AOD_ICONS_APPEAR_DURATION)
+ .start()
+ }
+ }
+
+ private const val AOD_ICONS_APPEAR_DURATION: Long = 200
}
+
+fun Resources.getConfigAwareDimensionPixelSize(
+ scope: CoroutineScope,
+ configurationController: ConfigurationController,
+ @DimenRes id: Int,
+): StateFlow<Int> =
+ scope.stateFlow(
+ changedSignals = configurationController.onDensityOrFontScaleChanged,
+ getValue = { getDimensionPixelSize(id) }
+ )
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt
index f68b0ef..3289a3c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt
@@ -15,8 +15,146 @@
*/
package com.android.systemui.statusbar.notification.icon.ui.viewmodel
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
+import com.android.systemui.statusbar.phone.DozeParameters
+import com.android.systemui.statusbar.phone.ScreenOffAnimationController
+import com.android.systemui.util.kotlin.pairwise
+import com.android.systemui.util.kotlin.sample
+import com.android.systemui.util.ui.AnimatableEvent
+import com.android.systemui.util.ui.AnimatedValue
+import com.android.systemui.util.ui.toAnimatedValueFlow
import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
/** View-model for the row of notification icons displayed on the always-on display. */
-class NotificationIconContainerAlwaysOnDisplayViewModel @Inject constructor() :
- NotificationIconContainerViewModel
+@SysUISingleton
+class NotificationIconContainerAlwaysOnDisplayViewModel
+@Inject
+constructor(
+ private val deviceEntryInteractor: DeviceEntryInteractor,
+ private val dozeParameters: DozeParameters,
+ private val featureFlags: FeatureFlagsClassic,
+ keyguardInteractor: KeyguardInteractor,
+ keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ private val notificationsKeyguardInteractor: NotificationsKeyguardInteractor,
+ screenOffAnimationController: ScreenOffAnimationController,
+ shadeInteractor: ShadeInteractor,
+) : NotificationIconContainerViewModel {
+
+ private val onDozeAnimationComplete = MutableSharedFlow<Unit>(extraBufferCapacity = 1)
+ private val onVisAnimationComplete = MutableSharedFlow<Unit>(extraBufferCapacity = 1)
+
+ override val animationsEnabled: Flow<Boolean> =
+ combine(
+ shadeInteractor.isShadeTouchable,
+ keyguardInteractor.isKeyguardVisible,
+ ) { panelTouchesEnabled, isKeyguardVisible ->
+ panelTouchesEnabled && isKeyguardVisible
+ }
+
+ override val isDozing: Flow<AnimatedValue<Boolean>> =
+ keyguardTransitionInteractor.startedKeyguardTransitionStep
+ // Determine if we're dozing based on the most recent transition
+ .map { step: TransitionStep ->
+ val isDozing = step.to == KeyguardState.AOD || step.to == KeyguardState.DOZING
+ isDozing to step
+ }
+ // Only emit changes based on whether we've started or stopped dozing
+ .distinctUntilChanged { (wasDozing, _), (isDozing, _) -> wasDozing != isDozing }
+ // Determine whether we need to animate
+ .map { (isDozing, step) ->
+ val animate = step.to == KeyguardState.AOD || step.from == KeyguardState.AOD
+ AnimatableEvent(isDozing, animate)
+ }
+ .distinctUntilChanged()
+ .toAnimatedValueFlow(completionEvents = onDozeAnimationComplete)
+
+ override val isVisible: Flow<AnimatedValue<Boolean>> =
+ combine(
+ keyguardTransitionInteractor.finishedKeyguardState.map { it != KeyguardState.GONE },
+ deviceEntryInteractor.isBypassEnabled,
+ areNotifsFullyHiddenAnimated(),
+ isPulseExpandingAnimated(),
+ ) {
+ onKeyguard: Boolean,
+ bypassEnabled: Boolean,
+ (notifsFullyHidden: Boolean, isAnimatingHide: Boolean),
+ (pulseExpanding: Boolean, isAnimatingPulse: Boolean),
+ ->
+ val isAnimating = isAnimatingHide || isAnimatingPulse
+ when {
+ // Hide the AOD icons if we're not in the KEYGUARD state unless the screen off
+ // animation is playing, in which case we want them to be visible if we're
+ // animating in the AOD UI and will be switching to KEYGUARD shortly.
+ !onKeyguard && !screenOffAnimationController.shouldShowAodIconsWhenShade() ->
+ AnimatedValue(false, isAnimating = false)
+ // If we're bypassing, then we're visible
+ bypassEnabled -> AnimatedValue(true, isAnimating)
+ // If we are pulsing (and not bypassing), then we are hidden
+ pulseExpanding -> AnimatedValue(false, isAnimating)
+ // If notifs are fully gone, then we're visible
+ notifsFullyHidden -> AnimatedValue(true, isAnimating)
+ // Otherwise, we're hidden
+ else -> AnimatedValue(false, isAnimating)
+ }
+ }
+ .distinctUntilChanged()
+
+ override fun completeDozeAnimation() {
+ onDozeAnimationComplete.tryEmit(Unit)
+ }
+
+ override fun completeVisibilityAnimation() {
+ onVisAnimationComplete.tryEmit(Unit)
+ }
+
+ /** Is there an expanded pulse, are we animating in response? */
+ private fun isPulseExpandingAnimated(): Flow<AnimatedValue<Boolean>> {
+ return notificationsKeyguardInteractor.isPulseExpanding
+ .pairwise(initialValue = null)
+ // If pulsing changes, start animating, unless it's the first emission
+ .map { (prev, expanding) ->
+ AnimatableEvent(expanding!!, startAnimating = prev != null)
+ }
+ .toAnimatedValueFlow(completionEvents = onVisAnimationComplete)
+ }
+
+ /** Are notifications completely hidden from view, are we animating in response? */
+ private fun areNotifsFullyHiddenAnimated(): Flow<AnimatedValue<Boolean>> {
+ return notificationsKeyguardInteractor.areNotificationsFullyHidden
+ .pairwise(initialValue = null)
+ .sample(deviceEntryInteractor.isBypassEnabled) { (prev, fullyHidden), bypassEnabled ->
+ val animate =
+ when {
+ // Don't animate for the first value
+ prev == null -> false
+ // Always animate if bypass is enabled.
+ bypassEnabled -> true
+ // If we're not bypassing and we're not going to AOD, then we're not
+ // animating.
+ !dozeParameters.alwaysOn -> false
+ // Don't animate when going to AOD if the display needs blanking.
+ dozeParameters.displayNeedsBlanking -> false
+ // We only want the appear animations to happen when the notifications
+ // get fully hidden, since otherwise the un-hide animation overlaps.
+ featureFlags.isEnabled(Flags.NEW_AOD_TRANSITION) -> true
+ else -> fullyHidden!!
+ }
+ AnimatableEvent(fullyHidden!!, animate)
+ }
+ .toAnimatedValueFlow(completionEvents = onVisAnimationComplete)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt
index 933c76f..c44a2b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt
@@ -15,8 +15,18 @@
*/
package com.android.systemui.statusbar.notification.icon.ui.viewmodel
+import com.android.systemui.util.ui.AnimatedValue
import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flowOf
/** View-model for the overflow row of notification icons displayed in the notification shade. */
class NotificationIconContainerShelfViewModel @Inject constructor() :
- NotificationIconContainerViewModel
+ NotificationIconContainerViewModel {
+ override val animationsEnabled: Flow<Boolean> = flowOf(true)
+ override val isDozing: Flow<AnimatedValue<Boolean>> = emptyFlow()
+ override val isVisible: Flow<AnimatedValue<Boolean>> = emptyFlow()
+ override fun completeDozeAnimation() {}
+ override fun completeVisibilityAnimation() {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
index 2217646..035687a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
@@ -15,8 +15,31 @@
*/
package com.android.systemui.statusbar.notification.icon.ui.viewmodel
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.util.ui.AnimatedValue
import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.emptyFlow
/** View-model for the row of notification icons displayed in the status bar, */
-class NotificationIconContainerStatusBarViewModel @Inject constructor() :
- NotificationIconContainerViewModel
+class NotificationIconContainerStatusBarViewModel
+@Inject
+constructor(
+ keyguardInteractor: KeyguardInteractor,
+ shadeInteractor: ShadeInteractor,
+) : NotificationIconContainerViewModel {
+ override val animationsEnabled: Flow<Boolean> =
+ combine(
+ shadeInteractor.isShadeTouchable,
+ keyguardInteractor.isKeyguardShowing,
+ ) { panelTouchesEnabled, isKeyguardShowing ->
+ panelTouchesEnabled && !isKeyguardShowing
+ }
+
+ override val isDozing: Flow<AnimatedValue<Boolean>> = emptyFlow()
+ override val isVisible: Flow<AnimatedValue<Boolean>> = emptyFlow()
+ override fun completeDozeAnimation() {}
+ override fun completeVisibilityAnimation() {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerViewModel.kt
index 892b2be..65eb220 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerViewModel.kt
@@ -15,8 +15,32 @@
*/
package com.android.systemui.statusbar.notification.icon.ui.viewmodel
+import com.android.systemui.util.ui.AnimatedValue
+import kotlinx.coroutines.flow.Flow
+
/**
* View-model for the row of notification icons displayed in the NotificationShelf, StatusBar, and
* AOD.
*/
-interface NotificationIconContainerViewModel
+interface NotificationIconContainerViewModel {
+ /** Are changes to the icon container animated? */
+ val animationsEnabled: Flow<Boolean>
+
+ /** Should icons be rendered in "dozing" mode? */
+ val isDozing: Flow<AnimatedValue<Boolean>>
+
+ /** Is the icon container visible? */
+ val isVisible: Flow<AnimatedValue<Boolean>>
+
+ /**
+ * Signal completion of the [isDozing] animation; if [isDozing]'s [AnimatedValue.isAnimating]
+ * property was `true`, calling this method will update it to `false.
+ */
+ fun completeDozeAnimation()
+
+ /**
+ * Signal completion of the [isVisible] animation; if [isVisible]'s [AnimatedValue.isAnimating]
+ * property was `true`, calling this method will update it to `false.
+ */
+ fun completeVisibilityAnimation()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
index b2c32cd..db7f46e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
@@ -1,6 +1,5 @@
package com.android.systemui.statusbar.notification.interruption
-import android.app.Notification
import android.app.Notification.VISIBILITY_SECRET
import android.content.Context
import android.database.ContentObserver
@@ -14,6 +13,8 @@
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.NotificationLockscreenUserManager
@@ -78,7 +79,8 @@
private val statusBarStateController: SysuiStatusBarStateController,
private val userTracker: UserTracker,
private val secureSettings: SecureSettings,
- private val globalSettings: GlobalSettings
+ private val globalSettings: GlobalSettings,
+ private val featureFlags: FeatureFlagsClassic
) : CoreStartable, KeyguardNotificationVisibilityProvider {
private val showSilentNotifsUri =
secureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS)
@@ -201,7 +203,7 @@
// device isn't public, no need to check public-related settings, so allow
!lockscreenUserManager.isLockscreenPublicMode(user) -> false
// entry is meant to be secret on the lockscreen, disallow
- entry.ranking.lockscreenVisibilityOverride == Notification.VISIBILITY_SECRET -> true
+ isRankingVisibilitySecret(entry) -> true
// disallow if user disallows notifications in public
else -> !lockscreenUserManager.userAllowsNotificationsInPublic(user)
}
@@ -215,6 +217,17 @@
}
}
+ private fun isRankingVisibilitySecret(entry: NotificationEntry): Boolean {
+ return if (featureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
+ // ranking.lockscreenVisibilityOverride contains possibly out of date DPC and Setting
+ // info, and NotificationLockscreenUserManagerImpl is already listening for updates
+ // to those
+ entry.ranking.channel.lockscreenVisibility == VISIBILITY_SECRET
+ } else {
+ entry.ranking.lockscreenVisibilityOverride == VISIBILITY_SECRET
+ }
+ }
+
override fun dump(pw: PrintWriter, args: Array<out String>) = pw.asIndenting().run {
println("isLockedOrLocking=$isLockedOrLocking")
withIncreasedIndent {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
index 6ec9dbe..b0155f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
@@ -180,4 +180,9 @@
* Add a component that can suppress visual interruptions.
*/
void addSuppressor(NotificationInterruptSuppressor suppressor);
+
+ /**
+ * Remove a component that can suppress visual interruptions.
+ */
+ void removeSuppressor(NotificationInterruptSuppressor suppressor);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index 3819843..778a0a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -175,6 +175,11 @@
}
@Override
+ public void removeSuppressor(NotificationInterruptSuppressor suppressor) {
+ mSuppressors.remove(suppressor);
+ }
+
+ @Override
public boolean shouldBubbleUp(NotificationEntry entry) {
final StatusBarNotification sbn = entry.getSbn();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapper.kt
index ebdeded..d7f0baf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapper.kt
@@ -58,6 +58,10 @@
wrapped.addSuppressor(suppressor)
}
+ override fun removeLegacySuppressor(suppressor: NotificationInterruptSuppressor) {
+ wrapped.removeSuppressor(suppressor)
+ }
+
override fun makeUnloggedHeadsUpDecision(entry: NotificationEntry): Decision =
wrapped.checkHeadsUp(entry, /* log= */ false).let { DecisionImpl.of(it) }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt
index 454ba02..920bbe9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt
@@ -60,6 +60,13 @@
fun addLegacySuppressor(suppressor: NotificationInterruptSuppressor)
/**
+ * Removes a [component][suppressor] that can suppress visual interruptions.
+ *
+ * @param[suppressor] the suppressor to remove
+ */
+ fun removeLegacySuppressor(suppressor: NotificationInterruptSuppressor)
+
+ /**
* Decides whether a [notification][entry] should display as heads-up or not, but does not log
* that decision.
*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryDumper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryDumper.kt
index 197ae1a..dc9028d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryDumper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryDumper.kt
@@ -25,6 +25,7 @@
import com.android.systemui.dump.DumpsysTableLogger
import com.android.systemui.dump.Row
import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import dalvik.annotation.optimization.NeverCompile
import java.io.PrintWriter
import javax.inject.Inject
@@ -39,6 +40,7 @@
Log.i("NotificationMemory", "Registered dumpable.")
}
+ @NeverCompile
override fun dump(pw: PrintWriter, args: Array<out String>) {
val memoryUse =
NotificationMemoryMeter.notificationMemoryUse(notificationPipeline.allNotifs)
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 c61258b..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,15 +351,20 @@
@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;
if (mDrawingAppearAnimation) {
startAppearAnimation(false /* isAppearing */, translationDirection,
- delay, duration, onFinishedRunnable, animationListener);
- } else if (onFinishedRunnable != null) {
- onFinishedRunnable.run();
+ delay, duration, onStartedRunnable, onFinishedRunnable, animationListener);
+ } else {
+ if (onStartedRunnable != null) {
+ onStartedRunnable.run();
+ }
+ if (onFinishedRunnable != null) {
+ onFinishedRunnable.run();
+ }
}
return 0;
}
@@ -371,12 +376,12 @@
mIsHeadsUpAnimation = isHeadsUpAppear;
if (mDrawingAppearAnimation) {
startAppearAnimation(true /* isAppearing */, isHeadsUpAppear ? 0.0f : -1.0f, delay,
- duration, null, null);
+ 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();
@@ -440,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 061132f..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
@@ -27,7 +27,6 @@
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.app.Notification;
-import android.app.NotificationChannel;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -40,8 +39,6 @@
import android.os.Build;
import android.os.Bundle;
import android.os.Trace;
-import android.service.notification.StatusBarNotification;
-import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.IndentingPrintWriter;
@@ -73,15 +70,15 @@
import com.android.internal.util.ContrastColorUtil;
import com.android.internal.widget.CachingIconView;
import com.android.internal.widget.CallLayout;
-import com.android.systemui.res.R;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
-import com.android.systemui.flags.ViewRefactorFlag;
+import com.android.systemui.flags.RefactorFlag;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.StatusBarIconView;
@@ -274,8 +271,8 @@
private OnExpandClickListener mOnExpandClickListener;
private View.OnClickListener mOnFeedbackClickListener;
private Path mExpandingClipPath;
- private final ViewRefactorFlag mInlineReplyAnimation =
- new ViewRefactorFlag(Flags.NOTIFICATION_INLINE_REPLY_ANIMATION);
+ private final RefactorFlag mInlineReplyAnimation =
+ RefactorFlag.forView(Flags.NOTIFICATION_INLINE_REPLY_ANIMATION);
// Listener will be called when receiving a long click event.
// Use #setLongPressPosition to optionally assign positional data with the long press.
@@ -2930,6 +2927,7 @@
long delay,
float translationDirection,
boolean isHeadsUpAnimation,
+ Runnable onStartedRunnable,
Runnable onFinishedRunnable,
AnimatorListenerAdapter animationListener) {
if (mMenuRow != null && mMenuRow.isMenuVisible()) {
@@ -2937,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();
@@ -2948,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/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
index 2599231..2a3e69b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
@@ -28,9 +28,9 @@
import android.view.View;
import android.view.ViewOutlineProvider;
-import com.android.systemui.res.R;
import com.android.systemui.flags.Flags;
-import com.android.systemui.flags.ViewRefactorFlag;
+import com.android.systemui.flags.RefactorFlag;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.RoundableState;
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
import com.android.systemui.util.DumpUtilsKt;
@@ -49,8 +49,8 @@
private float mOutlineAlpha = -1f;
private boolean mAlwaysRoundBothCorners;
private Path mTmpPath = new Path();
- protected final ViewRefactorFlag mImprovedHunAnimation =
- new ViewRefactorFlag(Flags.IMPROVED_HUN_ANIMATIONS);
+ protected final RefactorFlag mImprovedHunAnimation =
+ RefactorFlag.forView(Flags.IMPROVED_HUN_ANIMATIONS);
/**
* {@code false} if the children views of the {@link ExpandableOutlineView} are translated when
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/FeedbackInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java
index 42c80ed..40897da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java
@@ -42,9 +42,8 @@
import android.widget.TextView;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.Dependency;
-import com.android.systemui.res.R;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.util.Compile;
@@ -76,7 +75,9 @@
final StatusBarNotification sbn,
final NotificationEntry entry,
final ExpandableNotificationRow row,
- final AssistantFeedbackController controller) {
+ final AssistantFeedbackController controller,
+ final IStatusBarService statusBarService,
+ final NotificationGutsManager notificationGutsManager) {
mPkg = sbn.getPackageName();
mPm = pm;
mEntry = entry;
@@ -84,8 +85,8 @@
mRanking = entry.getRanking();
mFeedbackController = controller;
mAppName = mPkg;
- mStatusBarService = Dependency.get(IStatusBarService.class);
- mNotificationGutsManager = Dependency.get(NotificationGutsManager.class);
+ mStatusBarService = statusBarService;
+ mNotificationGutsManager = notificationGutsManager;
bindHeader();
bindPrompt();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 6d65660..9e9116b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -44,9 +44,9 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.statusbar.IStatusBarService;
import com.android.settingslib.notification.ConversationIconFactory;
import com.android.systemui.CoreStartable;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
@@ -54,6 +54,7 @@
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.shade.ShadeController;
@@ -69,8 +70,8 @@
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.CentralSurfaces;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.wmshell.BubblesManager;
@@ -99,6 +100,7 @@
// Dependencies:
private final NotificationLockscreenUserManager mLockscreenUserManager;
private final StatusBarStateController mStatusBarStateController;
+ private final IStatusBarService mStatusBarService;
private final DeviceProvisionedController mDeviceProvisionedController;
private final AssistantFeedbackController mAssistantFeedbackController;
@@ -127,7 +129,7 @@
private final ShadeController mShadeController;
private final WindowRootViewVisibilityInteractor mWindowRootViewVisibilityInteractor;
private NotifGutsViewListener mGutsListener;
- private final HeadsUpManagerPhone mHeadsUpManagerPhone;
+ private final HeadsUpManager mHeadsUpManager;
private final ActivityStarter mActivityStarter;
@Inject
@@ -152,9 +154,10 @@
WindowRootViewVisibilityInteractor windowRootViewVisibilityInteractor,
NotificationLockscreenUserManager notificationLockscreenUserManager,
StatusBarStateController statusBarStateController,
+ IStatusBarService statusBarService,
DeviceProvisionedController deviceProvisionedController,
MetricsLogger metricsLogger,
- HeadsUpManagerPhone headsUpManagerPhone,
+ HeadsUpManager headsUpManager,
ActivityStarter activityStarter) {
mContext = context;
mMainHandler = mainHandler;
@@ -177,9 +180,10 @@
mWindowRootViewVisibilityInteractor = windowRootViewVisibilityInteractor;
mLockscreenUserManager = notificationLockscreenUserManager;
mStatusBarStateController = statusBarStateController;
+ mStatusBarService = statusBarService;
mDeviceProvisionedController = deviceProvisionedController;
mMetricsLogger = metricsLogger;
- mHeadsUpManagerPhone = headsUpManagerPhone;
+ mHeadsUpManager = headsUpManager;
mActivityStarter = activityStarter;
}
@@ -296,7 +300,7 @@
if (mGutsListener != null) {
mGutsListener.onGutsClose(entry);
}
- mHeadsUpManagerPhone.setGutsShown(row.getEntry(), false);
+ mHeadsUpManager.setGutsShown(row.getEntry(), false);
});
View gutsView = item.getGutsView();
@@ -358,7 +362,8 @@
PackageManager pmUser = CentralSurfaces.getPackageManagerForUser(mContext,
userHandle.getIdentifier());
- feedbackInfo.bindGuts(pmUser, sbn, row.getEntry(), row, mAssistantFeedbackController);
+ feedbackInfo.bindGuts(pmUser, sbn, row.getEntry(), row, mAssistantFeedbackController,
+ mStatusBarService, this);
}
/**
@@ -676,7 +681,7 @@
row.closeRemoteInput();
mListContainer.onHeightChanged(row, true /* needsAnimation */);
mGutsMenuItem = menuItem;
- mHeadsUpManagerPhone.setGutsShown(row.getEntry(), true);
+ mHeadsUpManager.setGutsShown(row.getEntry(), true);
}
};
guts.post(mOpenRunnable);
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 0c686be..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
@@ -167,7 +167,7 @@
}
@VisibleForTesting
- boolean isSecondaryVisible() {
+ public boolean isSecondaryVisible() {
return mIsSecondaryVisible;
}
@@ -179,7 +179,8 @@
return mIsVisible;
}
- void setDuration(int duration) {
+ @VisibleForTesting
+ public void setDuration(int duration) {
mDuration = duration;
}
@@ -236,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 78d7558..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;
@@ -86,12 +87,12 @@
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.ExpandHelper;
-import com.android.systemui.res.R;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
-import com.android.systemui.flags.ViewRefactorFlag;
+import com.android.systemui.flags.RefactorFlag;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
+import com.android.systemui.res.R;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.TouchLogger;
import com.android.systemui.statusbar.CommandQueue;
@@ -106,12 +107,12 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
+import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
-import com.android.systemui.statusbar.notification.row.FooterView;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
@@ -200,8 +201,8 @@
private Set<Integer> mDebugTextUsedYPositions;
private final boolean mDebugRemoveAnimation;
private final boolean mSensitiveRevealAnimEndabled;
- private final ViewRefactorFlag mAnimatedInsets;
- private final ViewRefactorFlag mShelfRefactor;
+ private final RefactorFlag mAnimatedInsets;
+ private final RefactorFlag mShelfRefactor;
private final boolean mNewAodTransition;
@@ -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 ViewRefactorFlag(featureFlags, Flags.ANIMATED_NOTIFICATION_SHADE_INSETS);
- mShelfRefactor = new ViewRefactorFlag(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/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 66b2555..5020780 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -64,7 +64,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
-import com.android.systemui.flags.ViewRefactorFlag;
+import com.android.systemui.flags.RefactorFlag;
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository;
import com.android.systemui.keyguard.shared.model.KeyguardState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
@@ -99,7 +99,6 @@
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider;
-import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProvider;
import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.NotifStackController;
@@ -115,10 +114,10 @@
import com.android.systemui.statusbar.notification.row.NotificationGuts;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationSnooze;
+import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationListInteractor;
import com.android.systemui.statusbar.notification.stack.ui.viewbinder.NotificationListViewBinder;
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
@@ -127,6 +126,7 @@
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.policy.SplitShadeStateController;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -156,7 +156,7 @@
private final NotificationGutsManager mNotificationGutsManager;
private final NotificationsController mNotificationsController;
private final NotificationVisibilityProvider mVisibilityProvider;
- private final HeadsUpManagerPhone mHeadsUpManager;
+ private final HeadsUpManager mHeadsUpManager;
private final NotificationRoundnessManager mNotificationRoundnessManager;
private final TunerService mTunerService;
private final DeviceProvisionedController mDeviceProvisionedController;
@@ -194,7 +194,7 @@
private final GroupExpansionManager mGroupExpansionManager;
private final NotifPipelineFlags mNotifPipelineFlags;
- private final SeenNotificationsProvider mSeenNotificationsProvider;
+ private final NotificationListInteractor mNotificationListInteractor;
private final KeyguardTransitionRepository mKeyguardTransitionRepo;
private NotificationStackScrollLayout mView;
@@ -207,7 +207,7 @@
private boolean mIsInTransitionToAod = false;
private final FeatureFlags mFeatureFlags;
- private final ViewRefactorFlag mShelfRefactor;
+ private final RefactorFlag mShelfRefactor;
private final NotificationTargetsHelper mNotificationTargetsHelper;
private final SecureSettings mSecureSettings;
private final NotificationDismissibilityProvider mDismissibilityProvider;
@@ -631,7 +631,7 @@
NotificationGutsManager notificationGutsManager,
NotificationsController notificationsController,
NotificationVisibilityProvider visibilityProvider,
- HeadsUpManagerPhone headsUpManager,
+ HeadsUpManager headsUpManager,
NotificationRoundnessManager notificationRoundnessManager,
TunerService tunerService,
DeviceProvisionedController deviceProvisionedController,
@@ -662,7 +662,7 @@
UiEventLogger uiEventLogger,
NotificationRemoteInputManager remoteInputManager,
VisibilityLocationProviderDelegator visibilityLocationProviderDelegator,
- SeenNotificationsProvider seenNotificationsProvider,
+ NotificationListInteractor notificationListInteractor,
ShadeController shadeController,
InteractionJankMonitor jankMonitor,
StackStateLogger stackLogger,
@@ -715,11 +715,11 @@
mUiEventLogger = uiEventLogger;
mRemoteInputManager = remoteInputManager;
mVisibilityLocationProviderDelegator = visibilityLocationProviderDelegator;
- mSeenNotificationsProvider = seenNotificationsProvider;
+ mNotificationListInteractor = notificationListInteractor;
mShadeController = shadeController;
mNotifIconAreaController = notifIconAreaController;
mFeatureFlags = featureFlags;
- mShelfRefactor = new ViewRefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR);
+ mShelfRefactor = new RefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR);
mNotificationTargetsHelper = notificationTargetsHelper;
mSecureSettings = secureSettings;
mDismissibilityProvider = dismissibilityProvider;
@@ -2006,7 +2006,7 @@
public void setNotifStats(@NonNull NotifStats notifStats) {
mNotifStats = notifStats;
mView.setHasFilteredOutSeenNotifications(
- mSeenNotificationsProvider.getHasFilteredOutSeenNotifications());
+ mNotificationListInteractor.getHasFilteredOutSeenNotifications().getValue());
updateFooter();
updateShowEmptyShadeView();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 8ca1852..80f98a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -27,16 +27,16 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.SystemBarUtils;
import com.android.keyguard.BouncerPanelExpansionCalculator;
-import com.android.systemui.res.R;
import com.android.systemui.animation.ShadeInterpolation;
+import com.android.systemui.res.R;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.SourceType;
+import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
-import com.android.systemui.statusbar.notification.row.FooterView;
import java.util.ArrayList;
import java.util.List;
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/notification/stack/data/repository/NotificationListRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationListRepository.kt
new file mode 100644
index 0000000..f6ed8c8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationListRepository.kt
@@ -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.statusbar.notification.stack.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/** Repository for information about the current notification list. */
+@SysUISingleton
+class NotificationListRepository @Inject constructor() {
+ private val _hasFilteredOutSeenNotifications = MutableStateFlow(false)
+ val hasFilteredOutSeenNotifications: StateFlow<Boolean> =
+ _hasFilteredOutSeenNotifications.asStateFlow()
+
+ fun setHasFilteredOutSeenNotifications(value: Boolean) {
+ _hasFilteredOutSeenNotifications.value = value
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationListInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationListInteractor.kt
new file mode 100644
index 0000000..3fd68a8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationListInteractor.kt
@@ -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 com.android.systemui.statusbar.notification.stack.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.stack.data.repository.NotificationListRepository
+import javax.inject.Inject
+import kotlinx.coroutines.flow.StateFlow
+
+/** Interactor for business logic associated with the notification stack. */
+@SysUISingleton
+class NotificationListInteractor
+@Inject
+constructor(
+ private val notificationListRepository: NotificationListRepository,
+) {
+ /** Are any already-seen notifications currently filtered out of the shade? */
+ val hasFilteredOutSeenNotifications: StateFlow<Boolean>
+ get() = notificationListRepository.hasFilteredOutSeenNotifications
+
+ fun setHasFilteredOutSeenNotifications(value: Boolean) {
+ notificationListRepository.setHasFilteredOutSeenNotifications(value)
+ }
+}
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 3e2f10d..9fb6c1b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -220,6 +220,7 @@
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
import com.android.systemui.statusbar.policy.ExtensionController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
@@ -236,6 +237,10 @@
import com.android.wm.shell.startingsurface.SplashscreenContentDrawer;
import com.android.wm.shell.startingsurface.StartingSurface;
+import dagger.Lazy;
+
+import dalvik.annotation.optimization.NeverCompile;
+
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;
@@ -247,8 +252,6 @@
import javax.inject.Named;
import javax.inject.Provider;
-import dagger.Lazy;
-
/**
* A class handling initialization and coordination between some of the key central surfaces in
* System UI: The notification shade, the keyguard (lockscreen), and the status bar.
@@ -309,8 +312,8 @@
};
void onStatusBarWindowStateChanged(@WindowVisibleState int state) {
- updateBubblesVisibility();
mStatusBarWindowState = state;
+ updateBubblesVisibility();
}
@Override
@@ -417,7 +420,7 @@
private final NotificationWakeUpCoordinator mWakeUpCoordinator;
private final KeyguardBypassController mKeyguardBypassController;
private final KeyguardStateController mKeyguardStateController;
- private final HeadsUpManagerPhone mHeadsUpManager;
+ private final HeadsUpManager mHeadsUpManager;
private final StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
private final FalsingCollector mFalsingCollector;
private final FalsingManager mFalsingManager;
@@ -611,7 +614,7 @@
NotificationWakeUpCoordinator notificationWakeUpCoordinator,
KeyguardBypassController keyguardBypassController,
KeyguardStateController keyguardStateController,
- HeadsUpManagerPhone headsUpManagerPhone,
+ HeadsUpManager headsUpManager,
DynamicPrivacyController dynamicPrivacyController,
FalsingManager falsingManager,
FalsingCollector falsingCollector,
@@ -718,7 +721,7 @@
mWakeUpCoordinator = notificationWakeUpCoordinator;
mKeyguardBypassController = keyguardBypassController;
mKeyguardStateController = keyguardStateController;
- mHeadsUpManager = headsUpManagerPhone;
+ mHeadsUpManager = headsUpManager;
mBackActionInteractor = backActionInteractor;
mKeyguardIndicationController = keyguardIndicationController;
mStatusBarTouchableRegionManager = statusBarTouchableRegionManager;
@@ -1085,14 +1088,10 @@
* @deprecated use {@link
* WindowRootViewVisibilityInteractor.isLockscreenOrShadeVisible} instead.
*/ @VisibleForTesting
+ @Deprecated
void initShadeVisibilityListener() {
mShadeController.setVisibilityListener(new ShadeController.ShadeVisibilityListener() {
@Override
- public void visibilityChanged(boolean visible) {
- onShadeVisibilityChanged(visible);
- }
-
- @Override
public void expandedVisibleChanged(boolean expandedVisible) {
if (expandedVisible) {
setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
@@ -1195,7 +1194,7 @@
});
mStatusBarInitializer.initializeStatusBar();
- mStatusBarTouchableRegionManager.setup(this, getNotificationShadeWindowView());
+ mStatusBarTouchableRegionManager.setup(getNotificationShadeWindowView());
createNavigationBar(result);
@@ -1727,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(
@@ -1767,6 +1767,7 @@
}
}
+ @NeverCompile
@Override
public void dump(PrintWriter pwOriginal, String[] args) {
IndentingPrintWriter pw = DumpUtilsKt.asIndenting(pwOriginal);
@@ -2603,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) {
@@ -2688,7 +2692,9 @@
!mDozeServiceHost.isPulsing(), mDeviceProvisionedController.isFrpActive());
mShadeSurface.setTouchAndAnimationDisabled(disabled);
- mNotificationIconAreaController.setAnimationsEnabled(!disabled);
+ if (!mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+ mNotificationIconAreaController.setAnimationsEnabled(!disabled);
+ }
}
final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
@@ -2844,13 +2850,16 @@
mScrimController.setExpansionAffectsAlpha(!unlocking);
if (mAlternateBouncerInteractor.isVisibleState()) {
- if ((!mKeyguardStateController.isOccluded() || mShadeSurface.isPanelExpanded())
- && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
- || mTransitionToFullShadeProgress > 0f)) {
- mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED_SHADE);
- } else {
- mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED);
+ if (!mFeatureFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) {
+ if ((!mKeyguardStateController.isOccluded() || mShadeSurface.isPanelExpanded())
+ && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
+ || mTransitionToFullShadeProgress > 0f)) {
+ mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED_SHADE);
+ } else {
+ mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED);
+ }
}
+
// This will cancel the keyguardFadingAway animation if it is running. We need to do
// this as otherwise it can remain pending and leave keyguard in a weird state.
mUnlockScrimCallback.onCancelled();
@@ -2913,8 +2922,6 @@
protected boolean mDeviceInteractive;
- protected boolean mVisible;
-
protected DevicePolicyManager mDevicePolicyManager;
private final PowerManager mPowerManager;
protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -3032,16 +3039,6 @@
afterKeyguardGone);
}
- private void onShadeVisibilityChanged(boolean visible) {
- if (mVisible != visible) {
- mVisible = visible;
- if (visible) {
- DejankUtils.notifyRendererOfExpensiveFrame(
- getNotificationShadeWindowView(), "onShadeVisibilityChanged");
- }
- }
- }
-
private void clearNotificationEffects() {
try {
mBarService.clearNotificationEffects();
@@ -3163,7 +3160,6 @@
// TODO: Bring these out of CentralSurfaces.
mUserInfoControllerImpl.onDensityOrFontScaleChanged();
mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext);
- mHeadsUpManager.onDensityOrFontScaleChanged();
}
@Override
@@ -3200,7 +3196,8 @@
// down on the lockscreen), clear notification LED, vibration,
// ringing.
// Other transitions are covered in WindowRootViewVisibilityInteractor.
- if (mVisible && (newState == StatusBarState.SHADE_LOCKED
+ if (mWindowRootViewVisibilityInteractor.isLockscreenOrShadeVisible().getValue()
+ && (newState == StatusBarState.SHADE_LOCKED
|| mStatusBarStateController.goingToFullShade())) {
clearNotificationEffects();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java
index 1576aa2..6f992ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java
@@ -47,6 +47,11 @@
private final ArrayMap<Object, DarkReceiver> mReceivers = new ArrayMap<>();
private int mIconTint = DEFAULT_ICON_TINT;
+ private int mContrastTint = DEFAULT_INVERSE_ICON_TINT;
+
+ private int mDarkModeContrastColor = DEFAULT_ICON_TINT;
+ private int mLightModeContrastColor = DEFAULT_INVERSE_ICON_TINT;
+
private float mDarkIntensity;
private int mDarkModeIconColorSingleTone;
private int mLightModeIconColorSingleTone;
@@ -83,6 +88,7 @@
public void addDarkReceiver(DarkReceiver receiver) {
mReceivers.put(receiver, receiver);
receiver.onDarkChanged(mTintAreas, mDarkIntensity, mIconTint);
+ receiver.onDarkChangedWithContrast(mTintAreas, mIconTint, mContrastTint);
}
public void addDarkReceiver(ImageView imageView) {
@@ -90,6 +96,7 @@
ColorStateList.valueOf(getTint(mTintAreas, imageView, mIconTint)));
mReceivers.put(imageView, receiver);
receiver.onDarkChanged(mTintAreas, mDarkIntensity, mIconTint);
+ receiver.onDarkChangedWithContrast(mTintAreas, mIconTint, mContrastTint);
}
public void removeDarkReceiver(DarkReceiver object) {
@@ -102,6 +109,7 @@
public void applyDark(DarkReceiver object) {
mReceivers.get(object).onDarkChanged(mTintAreas, mDarkIntensity, mIconTint);
+ mReceivers.get(object).onDarkChangedWithContrast(mTintAreas, mIconTint, mContrastTint);
}
/**
@@ -125,8 +133,13 @@
@Override
public void applyDarkIntensity(float darkIntensity) {
mDarkIntensity = darkIntensity;
- mIconTint = (int) ArgbEvaluator.getInstance().evaluate(darkIntensity,
+ ArgbEvaluator evaluator = ArgbEvaluator.getInstance();
+
+ mIconTint = (int) evaluator.evaluate(darkIntensity,
mLightModeIconColorSingleTone, mDarkModeIconColorSingleTone);
+ mContrastTint = (int) evaluator
+ .evaluate(darkIntensity, mLightModeContrastColor, mDarkModeContrastColor);
+
applyIconTint();
}
@@ -139,6 +152,7 @@
mDarkChangeFlow.setValue(new DarkChange(mTintAreas, mDarkIntensity, mIconTint));
for (int i = 0; i < mReceivers.size(); i++) {
mReceivers.valueAt(i).onDarkChanged(mTintAreas, mDarkIntensity, mIconTint);
+ mReceivers.valueAt(i).onDarkChangedWithContrast(mTintAreas, mIconTint, mContrastTint);
}
}
@@ -146,6 +160,16 @@
public void dump(PrintWriter pw, String[] args) {
pw.println("DarkIconDispatcher: ");
pw.println(" mIconTint: 0x" + Integer.toHexString(mIconTint));
+ pw.println(" mContrastTint: 0x" + Integer.toHexString(mContrastTint));
+
+ pw.println(" mDarkModeIconColorSingleTone: 0x"
+ + Integer.toHexString(mDarkModeIconColorSingleTone));
+ pw.println(" mLightModeIconColorSingleTone: 0x"
+ + Integer.toHexString(mLightModeIconColorSingleTone));
+
+ pw.println(" mDarkModeContrastColor: 0x" + Integer.toHexString(mDarkModeContrastColor));
+ pw.println(" mLightModeContrastColor: 0x" + Integer.toHexString(mLightModeContrastColor));
+
pw.println(" mDarkIntensity: " + mDarkIntensity + "f");
pw.println(" mTintAreas: " + mTintAreas);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
index de9854a..5deb08a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
@@ -28,10 +28,10 @@
import android.widget.LinearLayout;
import com.android.internal.statusbar.StatusBarIcon;
-import com.android.systemui.res.R;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.StatusIconDisplayable;
import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger;
@@ -54,6 +54,7 @@
private ModernStatusBarWifiView mModernWifiView;
private boolean mDemoMode;
private int mColor;
+ private int mContrastColor;
private final MobileIconsViewModel mMobileIconsViewModel;
private final StatusBarLocation mLocation;
@@ -68,6 +69,7 @@
mStatusIcons = statusIcons;
mIconSize = iconSize;
mColor = DarkIconDispatcher.DEFAULT_ICON_TINT;
+ mContrastColor = DarkIconDispatcher.DEFAULT_INVERSE_ICON_TINT;
mMobileIconsViewModel = mobileIconsViewModel;
mLocation = location;
@@ -89,15 +91,17 @@
((ViewGroup) getParent()).removeView(this);
}
- public void setColor(int color) {
+ /** Set the tint colors */
+ public void setColor(int color, int contrastColor) {
mColor = color;
+ mContrastColor = contrastColor;
updateColors();
}
private void updateColors() {
for (int i = 0; i < getChildCount(); i++) {
StatusIconDisplayable child = (StatusIconDisplayable) getChildAt(i);
- child.setStaticDrawableColor(mColor);
+ child.setStaticDrawableColor(mColor, mContrastColor);
child.setDecorColor(mColor);
}
}
@@ -223,7 +227,7 @@
StatusBarIconView v = new StatusBarIconView(getContext(), slot, null, false);
v.setTag(slot);
v.set(icon);
- v.setStaticDrawableColor(mColor);
+ v.setStaticDrawableColor(mColor, mContrastColor);
v.setDecorColor(mColor);
addView(v, 0, createLayoutParams());
}
@@ -269,7 +273,7 @@
}
mModernWifiView = view;
- mModernWifiView.setStaticDrawableColor(mColor);
+ mModernWifiView.setStaticDrawableColor(mColor, mContrastColor);
addView(view, viewIndex, createLayoutParams());
}
@@ -305,14 +309,20 @@
}
@Override
- public void onDarkChanged(ArrayList<Rect> areas, float darkIntensity, int tint) {
- setColor(DarkIconDispatcher.getTint(areas, mStatusIcons, tint));
+ public void onDarkChangedWithContrast(ArrayList<Rect> areas, int tint, int contrastTint) {
+ setColor(tint, contrastTint);
if (mModernWifiView != null) {
- mModernWifiView.onDarkChanged(areas, darkIntensity, tint);
+ mModernWifiView.onDarkChangedWithContrast(areas, tint, contrastTint);
}
+
for (ModernStatusBarMobileView view : mModernMobileViews) {
- view.onDarkChanged(areas, darkIntensity, tint);
+ view.onDarkChangedWithContrast(areas, tint, contrastTint);
}
}
+
+ @Override
+ public void onDarkChanged(ArrayList<Rect> areas, float darkIntensity, int tint) {
+ // not needed
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index 4849f64..d3d11ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -49,6 +49,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.util.Assert;
@@ -80,7 +81,7 @@
private final WakefulnessLifecycle mWakefulnessLifecycle;
private final SysuiStatusBarStateController mStatusBarStateController;
private final DeviceProvisionedController mDeviceProvisionedController;
- private final HeadsUpManagerPhone mHeadsUpManagerPhone;
+ private final HeadsUpManager mHeadsUpManager;
private final BatteryController mBatteryController;
private final ScrimController mScrimController;
private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
@@ -106,7 +107,7 @@
WakefulnessLifecycle wakefulnessLifecycle,
SysuiStatusBarStateController statusBarStateController,
DeviceProvisionedController deviceProvisionedController,
- HeadsUpManagerPhone headsUpManagerPhone, BatteryController batteryController,
+ HeadsUpManager headsUpManager, BatteryController batteryController,
ScrimController scrimController,
Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
Lazy<AssistManager> assistManagerLazy,
@@ -123,7 +124,7 @@
mWakefulnessLifecycle = wakefulnessLifecycle;
mStatusBarStateController = statusBarStateController;
mDeviceProvisionedController = deviceProvisionedController;
- mHeadsUpManagerPhone = headsUpManagerPhone;
+ mHeadsUpManager = headsUpManager;
mBatteryController = batteryController;
mScrimController = scrimController;
mBiometricUnlockControllerLazy = biometricUnlockControllerLazy;
@@ -135,7 +136,7 @@
mNotificationWakeUpCoordinator = notificationWakeUpCoordinator;
mAuthController = authController;
mNotificationIconAreaController = notificationIconAreaController;
- mHeadsUpManagerPhone.addListener(mOnHeadsUpChangedListener);
+ mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
mDozeInteractor = dozeInteractor;
}
@@ -337,8 +338,8 @@
if (reason == DozeLog.PULSE_REASON_SENSOR_WAKE_REACH) {
mScrimController.setWakeLockScreenSensorActive(true);
}
- if (mDozeScrimController.isPulsing() && mHeadsUpManagerPhone.hasNotifications()) {
- mHeadsUpManagerPhone.extendHeadsUp();
+ if (mDozeScrimController.isPulsing() && mHeadsUpManager.hasNotifications()) {
+ mHeadsUpManager.extendHeadsUp();
} else {
mDozeScrimController.extendPulse();
}
@@ -493,7 +494,7 @@
mDozeScrimController.cancelPendingPulseTimeout();
}
}
- if (!isHeadsUp && !mHeadsUpManagerPhone.hasNotifications()) {
+ if (!isHeadsUp && !mHeadsUpManager.hasNotifications()) {
// There are no longer any notifications to show. We should end the
// pulse now.
stopPulsing();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 270c40e..c493eeda 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -26,9 +26,9 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.ViewClippingUtil;
-import com.android.systemui.res.R;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
import com.android.systemui.shade.ShadeHeadsUpTracker;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.statusbar.CommandQueue;
@@ -43,6 +43,7 @@
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentScope;
import com.android.systemui.statusbar.policy.Clock;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.util.ViewController;
@@ -70,7 +71,7 @@
private static final SourceType HEADS_UP = SourceType.from("HeadsUp");
private static final SourceType PULSING = SourceType.from("Pulsing");
private final NotificationIconAreaController mNotificationIconAreaController;
- private final HeadsUpManagerPhone mHeadsUpManager;
+ private final HeadsUpManager mHeadsUpManager;
private final NotificationStackScrollLayoutController mStackScrollerController;
private final DarkIconDispatcher mDarkIconDispatcher;
@@ -108,7 +109,7 @@
@Inject
public HeadsUpAppearanceController(
NotificationIconAreaController notificationIconAreaController,
- HeadsUpManagerPhone headsUpManager,
+ HeadsUpManager headsUpManager,
StatusBarStateController stateController,
PhoneStatusBarTransitions phoneStatusBarTransitions,
KeyguardBypassController bypassController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index a5ea142..407148f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -29,12 +29,12 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.policy.SystemBarUtils;
-import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
+import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.res.R;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.provider.OnReorderingAllowedListener;
@@ -42,10 +42,13 @@
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+import com.android.systemui.statusbar.policy.AnimationStateHandler;
+import com.android.systemui.statusbar.policy.BaseHeadsUpManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+import com.android.systemui.statusbar.policy.OnHeadsUpPhoneListenerChange;
+import com.android.systemui.util.kotlin.JavaAdapter;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -53,11 +56,11 @@
import java.util.List;
import java.util.Stack;
-/**
- * A implementation of HeadsUpManager for phone and car.
- */
-public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
- OnHeadsUpChangedListener {
+import javax.inject.Inject;
+
+/** A implementation of HeadsUpManager for phone. */
+@SysUISingleton
+public class HeadsUpManagerPhone extends BaseHeadsUpManager implements OnHeadsUpChangedListener {
private static final String TAG = "HeadsUpManagerPhone";
@VisibleForTesting
@@ -102,8 +105,9 @@
///////////////////////////////////////////////////////////////////////////////////////////////
// Constructor:
-
- public HeadsUpManagerPhone(@NonNull final Context context,
+ @Inject
+ public HeadsUpManagerPhone(
+ @NonNull final Context context,
HeadsUpManagerLogger logger,
StatusBarStateController statusBarStateController,
KeyguardBypassController bypassController,
@@ -113,7 +117,8 @@
@Main Handler handler,
AccessibilityManagerWrapper accessibilityManagerWrapper,
UiEventLogger uiEventLogger,
- ShadeExpansionStateManager shadeExpansionStateManager) {
+ JavaAdapter javaAdapter,
+ ShadeInteractor shadeInteractor) {
super(context, logger, handler, accessibilityManagerWrapper, uiEventLogger);
Resources resources = mContext.getResources();
mExtensionTime = resources.getInteger(R.integer.ambient_notification_extension_time);
@@ -134,8 +139,7 @@
updateResources();
}
});
-
- shadeExpansionStateManager.addFullExpansionListener(this::onShadeExpansionFullyChanged);
+ javaAdapter.alwaysCollectFlow(shadeInteractor.isAnyExpanded(), this::onShadeOrQsExpanded);
}
public void setAnimationStateHandler(AnimationStateHandler handler) {
@@ -154,7 +158,8 @@
/**
* Add a listener to receive callbacks onHeadsUpGoingAway
*/
- void addHeadsUpPhoneListener(OnHeadsUpPhoneListenerChange listener) {
+ @Override
+ public void addHeadsUpPhoneListener(OnHeadsUpPhoneListenerChange listener) {
mHeadsUpPhoneListeners.add(listener);
}
@@ -162,7 +167,8 @@
* Gets the touchable region needed for heads up notifications. Returns null if no touchable
* region is required (ie: no heads up notification currently exists).
*/
- @Nullable Region getTouchableRegion() {
+ @Override
+ public @Nullable Region getTouchableRegion() {
NotificationEntry topEntry = getTopEntry();
// This call could be made in an inconsistent state while the pinnedMode hasn't been
@@ -197,8 +203,9 @@
* @param key the key of the touched notification
* @return whether the touch is invalid and should be discarded
*/
- boolean shouldSwallowClick(@NonNull String key) {
- HeadsUpManager.HeadsUpEntry entry = getHeadsUpEntry(key);
+ @Override
+ public boolean shouldSwallowClick(@NonNull String key) {
+ BaseHeadsUpManager.HeadsUpEntry entry = getHeadsUpEntry(key);
return entry != null && mClock.currentTimeMillis() < entry.mPostTime;
}
@@ -225,7 +232,7 @@
mTrackingHeadsUp = trackingHeadsUp;
}
- private void onShadeExpansionFullyChanged(Boolean isExpanded) {
+ private void onShadeOrQsExpanded(Boolean isExpanded) {
if (isExpanded != mIsExpanded) {
mIsExpanded = isExpanded;
if (isExpanded) {
@@ -238,7 +245,8 @@
* Set that we are exiting the headsUp pinned mode, but some notifications might still be
* animating out. This is used to keep the touchable regions in a reasonable state.
*/
- void setHeadsUpGoingAway(boolean headsUpGoingAway) {
+ @Override
+ public void setHeadsUpGoingAway(boolean headsUpGoingAway) {
if (headsUpGoingAway != mHeadsUpGoingAway) {
mHeadsUpGoingAway = headsUpGoingAway;
for (OnHeadsUpPhoneListenerChange listener : mHeadsUpPhoneListeners) {
@@ -247,7 +255,8 @@
}
}
- boolean isHeadsUpGoingAway() {
+ @Override
+ public boolean isHeadsUpGoingAway() {
return mHeadsUpGoingAway;
}
@@ -260,8 +269,8 @@
public void setRemoteInputActive(
@NonNull NotificationEntry entry, boolean remoteInputActive) {
HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(entry.getKey());
- if (headsUpEntry != null && headsUpEntry.remoteInputActive != remoteInputActive) {
- headsUpEntry.remoteInputActive = remoteInputActive;
+ if (headsUpEntry != null && headsUpEntry.mRemoteInputActive != remoteInputActive) {
+ headsUpEntry.mRemoteInputActive = remoteInputActive;
if (remoteInputActive) {
headsUpEntry.removeAutoRemovalCallbacks("setRemoteInputActive(true)");
} else {
@@ -313,6 +322,7 @@
mSwipedOutKeys.add(key);
}
+ @Override
public boolean removeNotification(@NonNull String key, boolean releaseImmediately,
boolean animate) {
if (animate) {
@@ -411,7 +421,7 @@
///////////////////////////////////////////////////////////////////////////////////////////////
// HeadsUpEntryPhone:
- protected class HeadsUpEntryPhone extends HeadsUpManager.HeadsUpEntry {
+ protected class HeadsUpEntryPhone extends BaseHeadsUpManager.HeadsUpEntry {
private boolean mGutsShownPinned;
@@ -459,11 +469,11 @@
@Override
public void setExpanded(boolean expanded) {
- if (this.expanded == expanded) {
+ if (this.mExpanded == expanded) {
return;
}
- this.expanded = expanded;
+ this.mExpanded = expanded;
if (expanded) {
removeAutoRemovalCallbacks("setExpanded(true)");
} else {
@@ -504,21 +514,6 @@
}
}
- public interface AnimationStateHandler {
- void setHeadsUpGoingAwayAnimationsAllowed(boolean allowed);
- }
-
- /**
- * Listener to register for HeadsUpNotification Phone changes.
- */
- public interface OnHeadsUpPhoneListenerChange {
- /**
- * Called when a heads up notification is 'going away' or no longer 'going away'.
- * See {@link HeadsUpManagerPhone#setHeadsUpGoingAway}.
- */
- void onHeadsUpGoingAwayStateChanged(boolean headsUpGoingAway);
- }
-
private final StateListener mStatusBarStateListener = new StateListener() {
@Override
public void onStateChanged(int newState) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpModule.kt
new file mode 100644
index 0000000..0d0f2cd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpModule.kt
@@ -0,0 +1,11 @@
+package com.android.systemui.statusbar.phone
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.policy.HeadsUpManager
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface HeadsUpModule {
+ @Binds @SysUISingleton fun bindsHeadsUpManager(hum: HeadsUpManagerPhone): HeadsUpManager
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index dcbaac2..198272e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -26,13 +26,14 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
/**
* A helper class to handle touches on the heads-up views.
*/
public class HeadsUpTouchHelper implements Gefingerpoken {
- private final HeadsUpManagerPhone mHeadsUpManager;
+ private final HeadsUpManager mHeadsUpManager;
private final IStatusBarService mStatusBarService;
private final Callback mCallback;
private int mTrackingPointer;
@@ -45,7 +46,7 @@
private final HeadsUpNotificationViewController mPanel;
private ExpandableNotificationRow mPickedChild;
- public HeadsUpTouchHelper(HeadsUpManagerPhone headsUpManager,
+ public HeadsUpTouchHelper(HeadsUpManager headsUpManager,
IStatusBarService statusBarService,
Callback callback,
HeadsUpNotificationViewController notificationPanelView) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index fb5a530..0a03af7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -26,14 +26,20 @@
import com.android.app.animation.Interpolators;
import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.keyguard.KeyguardStatusView;
+import com.android.systemui.log.LogBuffer;
+import com.android.systemui.log.core.Logger;
+import com.android.systemui.log.dagger.KeyguardClockLog;
import com.android.systemui.res.R;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcherListView;
+import javax.inject.Inject;
+
/**
* Utility class to calculate the clock position and top padding of notifications on Keyguard.
*/
public class KeyguardClockPositionAlgorithm {
+ private static final String TAG = "KeyguardClockPositionAlgorithm";
/**
* Margin between the bottom of the status view and the notification shade.
@@ -147,6 +153,13 @@
*/
private boolean mIsClockTopAligned;
+ private Logger mLogger;
+
+ @Inject
+ public KeyguardClockPositionAlgorithm(@KeyguardClockLog LogBuffer logBuffer) {
+ mLogger = new Logger(logBuffer, TAG);
+ }
+
/**
* Refreshes the dimension values.
*/
@@ -306,6 +319,20 @@
+ fullyDarkBurnInOffset
+ shift;
mCurrentBurnInOffsetY = MathUtils.lerp(0, fullyDarkBurnInOffset, darkAmount);
+ final String inputs = "panelExpansion: " + panelExpansion + " darkAmount: " + darkAmount;
+ final String outputs = "clockY: " + clockY
+ + " burnInPreventionOffsetY: " + burnInPreventionOffsetY
+ + " fullyDarkBurnInOffset: " + fullyDarkBurnInOffset
+ + " shift: " + shift
+ + " mOverStretchAmount: " + mOverStretchAmount
+ + " mCurrentBurnInOffsetY: " + mCurrentBurnInOffsetY;
+ mLogger.i(msg -> {
+ return msg.getStr1() + " -> " + msg.getStr2();
+ }, msg -> {
+ msg.setStr1(inputs);
+ msg.setStr2(outputs);
+ return kotlin.Unit.INSTANCE;
+ });
return (int) (MathUtils.lerp(clockY, clockYDark, darkAmount) + mOverStretchAmount);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 58126ae..8a64a50 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -436,10 +436,14 @@
private void updateIconsAndTextColors(StatusBarIconController.TintedIconManager iconManager) {
@ColorInt int textColor = Utils.getColorAttrDefaultColor(mContext,
R.attr.wallpaperTextColor);
+ float luminance = Color.luminance(textColor);
@ColorInt int iconColor = Utils.getColorStateListDefaultColor(mContext,
- Color.luminance(textColor) < 0.5
+ luminance < 0.5
? com.android.settingslib.R.color.dark_mode_icon_color_single_tone
: com.android.settingslib.R.color.light_mode_icon_color_single_tone);
+ @ColorInt int contrastColor = luminance < 0.5
+ ? DarkIconDispatcherImpl.DEFAULT_ICON_TINT
+ : DarkIconDispatcherImpl.DEFAULT_INVERSE_ICON_TINT;
float intensity = textColor == Color.WHITE ? 0 : 1;
mCarrierLabel.setTextColor(iconColor);
@@ -451,7 +455,7 @@
}
if (iconManager != null) {
- iconManager.setTint(iconColor);
+ iconManager.setTint(iconColor, contrastColor);
}
mDarkChange.setValue(new DarkChange(mEmptyTintRect, intensity, iconColor));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java
index 1b9e5b3..5553270 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java
@@ -36,16 +36,16 @@
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.ContrastColorUtil;
import com.android.settingslib.Utils;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
-import com.android.systemui.flags.ViewRefactorFlag;
+import com.android.systemui.flags.RefactorFlag;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -109,7 +109,7 @@
private final ArrayList<Rect> mTintAreas = new ArrayList<>();
private final Context mContext;
- private final ViewRefactorFlag mShelfRefactor;
+ private final RefactorFlag mShelfRefactor;
private final boolean mNewAodTransition;
@@ -150,7 +150,7 @@
mContrastColorUtil = ContrastColorUtil.getInstance(context);
mContext = context;
mStatusBarStateController = statusBarStateController;
- mShelfRefactor = new ViewRefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR);
+ mShelfRefactor = new RefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR);
mNewAodTransition = featureFlags.isEnabled(NEW_AOD_TRANSITION);
mStatusBarStateController.addCallback(this);
mMediaManager = notificationMediaManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 7cbaf63b..b15c0fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -33,6 +33,7 @@
import android.view.ViewGroup;
import android.view.animation.Interpolator;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.collection.ArrayMap;
@@ -624,12 +625,32 @@
}
public void setDozing(boolean dozing, boolean fade, long delay) {
+ setDozing(dozing, fade, delay, /* endRunnable= */ null);
+ }
+
+ public void setDozing(boolean dozing, boolean fade, long delay,
+ @Nullable Runnable endRunnable) {
mDozing = dozing;
mDisallowNextAnimation |= !fade;
- for (int i = 0; i < getChildCount(); i++) {
+ final int childCount = getChildCount();
+ // Track all the child invocations of setDozing, invoking the top-level endRunnable once
+ // they have all completed.
+ final Runnable onChildCompleted = endRunnable == null ? null : new Runnable() {
+ private int mPendingCallbacks = childCount;
+
+ @Override
+ public void run() {
+ if (--mPendingCallbacks == 0) {
+ endRunnable.run();
+ }
+ }
+ };
+ for (int i = 0; i < childCount; i++) {
View view = getChildAt(i);
if (view instanceof StatusBarIconView) {
- ((StatusBarIconView) view).setDozing(dozing, fade, delay);
+ ((StatusBarIconView) view).setDozing(dozing, fade, delay, onChildCompleted);
+ } else if (onChildCompleted != null) {
+ onChildCompleted.run();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
index 4b39854..235ed25 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
@@ -24,6 +24,7 @@
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.window.StatusBarWindowController;
@@ -39,7 +40,7 @@
private final ShadeViewController mShadeViewController;
private final NotificationStackScrollLayoutController mNsslController;
private final KeyguardBypassController mKeyguardBypassController;
- private final HeadsUpManagerPhone mHeadsUpManager;
+ private final HeadsUpManager mHeadsUpManager;
private final StatusBarStateController mStatusBarStateController;
private final NotificationRemoteInputManager mNotificationRemoteInputManager;
@@ -50,7 +51,7 @@
ShadeViewController shadeViewController,
NotificationStackScrollLayoutController nsslController,
KeyguardBypassController keyguardBypassController,
- HeadsUpManagerPhone headsUpManager,
+ HeadsUpManager headsUpManager,
StatusBarStateController statusBarStateController,
NotificationRemoteInputManager notificationRemoteInputManager) {
mNotificationShadeWindowController = notificationShadeWindowController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt
index 4d9de09..fa6d279 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt
@@ -3,12 +3,15 @@
import android.app.StatusBarManager
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
-import com.android.systemui.shade.ShadeExpansionStateManager
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.window.StatusBarWindowStateController
import com.android.systemui.util.concurrency.DelayableExecutor
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
import java.io.PrintWriter
import javax.inject.Inject
@@ -25,14 +28,14 @@
*/
@SysUISingleton
class StatusBarHideIconsForBouncerManager @Inject constructor(
- private val commandQueue: CommandQueue,
- @Main private val mainExecutor: DelayableExecutor,
- statusBarWindowStateController: StatusBarWindowStateController,
- shadeExpansionStateManager: ShadeExpansionStateManager,
- dumpManager: DumpManager
+ @Application private val scope: CoroutineScope,
+ private val commandQueue: CommandQueue,
+ @Main private val mainExecutor: DelayableExecutor,
+ statusBarWindowStateController: StatusBarWindowStateController,
+ val shadeInteractor: ShadeInteractor,
+ dumpManager: DumpManager
) : Dumpable {
// State variables set by external classes.
- private var panelExpanded: Boolean = false
private var isOccluded: Boolean = false
private var bouncerShowing: Boolean = false
private var topAppHidesStatusBar: Boolean = false
@@ -49,10 +52,9 @@
statusBarWindowStateController.addListener {
state -> setStatusBarStateAndTriggerUpdate(state)
}
- shadeExpansionStateManager.addFullExpansionListener { isExpanded ->
- if (panelExpanded != isExpanded) {
- panelExpanded = isExpanded
- updateHideIconsForBouncer(animate = false)
+ scope.launch {
+ shadeInteractor.isAnyExpanded.collect {
+ updateHideIconsForBouncer(false)
}
}
}
@@ -101,7 +103,7 @@
topAppHidesStatusBar &&
isOccluded &&
(statusBarWindowHidden || bouncerShowing)
- val hideBecauseKeyguard = !panelExpanded && !isOccluded && bouncerShowing
+ val hideBecauseKeyguard = !isShadeOrQsExpanded() && !isOccluded && bouncerShowing
val shouldHideIconsForBouncer = hideBecauseApp || hideBecauseKeyguard
if (hideIconsForBouncer != shouldHideIconsForBouncer) {
hideIconsForBouncer = shouldHideIconsForBouncer
@@ -125,9 +127,13 @@
}
}
+ private fun isShadeOrQsExpanded(): Boolean {
+ return shadeInteractor.isAnyExpanded.value
+ }
+
override fun dump(pw: PrintWriter, args: Array<out String>) {
pw.println("---- State variables set externally ----")
- pw.println("panelExpanded=$panelExpanded")
+ pw.println("isShadeOrQsExpanded=${isShadeOrQsExpanded()}")
pw.println("isOccluded=$isOccluded")
pw.println("bouncerShowing=$bouncerShowing")
pw.println("topAppHideStatusBar=$topAppHidesStatusBar")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index ffeb1a8..9ae4195 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -31,11 +31,11 @@
import androidx.annotation.VisibleForTesting;
import com.android.internal.statusbar.StatusBarIcon;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.demomode.DemoModeCommandReceiver;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.BaseStatusBarFrameLayout;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.StatusIconDisplayable;
@@ -248,7 +248,10 @@
*
*/
class TintedIconManager extends IconManager {
+ // The main tint, used as the foreground in non layer drawables
private int mColor;
+ // To be used as the main tint in drawables that wish to have a layer
+ private int mForegroundColor;
public TintedIconManager(
ViewGroup group,
@@ -268,26 +271,41 @@
protected void onIconAdded(int index, String slot, boolean blocked,
StatusBarIconHolder holder) {
StatusIconDisplayable view = addHolder(index, slot, blocked, holder);
- view.setStaticDrawableColor(mColor);
+ view.setStaticDrawableColor(mColor, mForegroundColor);
view.setDecorColor(mColor);
}
- public void setTint(int color) {
- mColor = color;
+ /**
+ * Most icons are a single layer, and tintColor will be used as the tint in those cases.
+ * For icons that have a background, foregroundColor becomes the contrasting tint used
+ * for the foreground.
+ *
+ * @param tintColor the main tint to use for the icons in the group
+ * @param foregroundColor used as the main tint for layer-ish drawables where tintColor is
+ * being used as the background
+ */
+ public void setTint(int tintColor, int foregroundColor) {
+ mColor = tintColor;
+ mForegroundColor = foregroundColor;
+
for (int i = 0; i < mGroup.getChildCount(); i++) {
View child = mGroup.getChildAt(i);
if (child instanceof StatusIconDisplayable) {
StatusIconDisplayable icon = (StatusIconDisplayable) child;
- icon.setStaticDrawableColor(mColor);
+ icon.setStaticDrawableColor(mColor, mForegroundColor);
icon.setDecorColor(mColor);
}
}
+
+ if (mDemoStatusIcons != null) {
+ mDemoStatusIcons.setColor(tintColor, foregroundColor);
+ }
}
@Override
protected DemoStatusIcons createDemoStatusIcons() {
DemoStatusIcons icons = super.createDemoStatusIcons();
- icons.setColor(mColor);
+ icons.setColor(mColor, mForegroundColor);
return icons;
}
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 eedf35f..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;
@@ -1573,6 +1571,9 @@
* notification shade's child views.
*/
public boolean shouldInterceptTouchEvent(MotionEvent event) {
+ if (mFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) {
+ return false;
+ }
return mAlternateBouncerInteractor.isVisibleState();
}
@@ -1581,13 +1582,17 @@
* showing.
*/
public boolean onTouch(MotionEvent event) {
+ if (mFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) {
+ return false;
+ }
+
boolean handleTouch = shouldInterceptTouchEvent(event);
if (handleTouch) {
final boolean actionDown = event.getActionMasked() == MotionEvent.ACTION_DOWN;
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/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index dc4c709..dbee080 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -76,6 +76,7 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowDragController;
import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.wmshell.BubblesManager;
@@ -102,7 +103,7 @@
private final Executor mUiBgExecutor;
private final NotificationVisibilityProvider mVisibilityProvider;
- private final HeadsUpManagerPhone mHeadsUpManager;
+ private final HeadsUpManager mHeadsUpManager;
private final ActivityStarter mActivityStarter;
private final NotificationClickNotifier mClickNotifier;
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -141,7 +142,7 @@
Handler mainThreadHandler,
Executor uiBgExecutor,
NotificationVisibilityProvider visibilityProvider,
- HeadsUpManagerPhone headsUpManager,
+ HeadsUpManager headsUpManager,
ActivityStarter activityStarter,
NotificationClickNotifier clickNotifier,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
@@ -213,7 +214,9 @@
*/
@Override
public void onNotificationClicked(NotificationEntry entry, ExpandableNotificationRow row) {
- mLogger.logStartingActivityFromClick(entry);
+ mLogger.logStartingActivityFromClick(entry, row.isHeadsUpState(),
+ mKeyguardStateController.isVisible(),
+ mNotificationShadeWindowController.getPanelExpanded());
if (mRemoteInputManager.isRemoteInputActive(entry)) {
// We have an active remote input typed and the user clicked on the notification.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt
index 2d30a12..4211cab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt
@@ -30,11 +30,16 @@
class StatusBarNotificationActivityStarterLogger @Inject constructor(
@NotifInteractionLog private val buffer: LogBuffer
) {
- fun logStartingActivityFromClick(entry: NotificationEntry) {
+ fun logStartingActivityFromClick(entry: NotificationEntry, isHeadsUpState: Boolean,
+ isKeyguardVisible: Boolean, isPanelExpanded: Boolean) {
buffer.log(TAG, DEBUG, {
str1 = entry.logKey
+ bool1 = isHeadsUpState
+ bool2 = isKeyguardVisible
+ bool3 = isPanelExpanded
}, {
- "(1/5) onNotificationClicked: $str1"
+ "(1/5) onNotificationClicked: $str1 isHeadsUpState: $bool1 " +
+ "isKeyguardVisible: $bool2 isPanelExpanded: $bool3"
})
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index fb7f9d1..2d14f6b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -31,11 +31,11 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.InitController;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.power.domain.interactor.PowerInteractor;
+import com.android.systemui.res.R;
import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.shade.QuickSettingsController;
import com.android.systemui.shade.ShadeViewController;
@@ -60,6 +60,7 @@
import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import javax.inject.Inject;
@@ -76,7 +77,7 @@
private final NotificationMediaManager mMediaManager;
private final NotificationGutsManager mGutsManager;
private final ShadeViewController mNotificationPanel;
- private final HeadsUpManagerPhone mHeadsUpManager;
+ private final HeadsUpManager mHeadsUpManager;
private final AboveShelfObserver mAboveShelfObserver;
private final DozeScrimController mDozeScrimController;
private final NotificationsInteractor mNotificationsInteractor;
@@ -98,7 +99,7 @@
Context context,
ShadeViewController panel,
QuickSettingsController quickSettingsController,
- HeadsUpManagerPhone headsUp,
+ HeadsUpManager headsUp,
NotificationShadeWindowView statusBarWindow,
ActivityStarter activityStarter,
NotificationStackScrollLayoutController stackScrollerController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
index 40bd8d3..6d8ec44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
@@ -31,15 +31,19 @@
import com.android.internal.policy.SystemBarUtils;
import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
import com.android.systemui.ScreenDecorations;
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.res.R;
import com.android.systemui.scene.domain.interactor.SceneInteractor;
import com.android.systemui.scene.shared.flag.SceneContainerFlags;
import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.util.kotlin.JavaAdapter;
@@ -58,13 +62,12 @@
private static final String TAG = "TouchableRegionManager";
private final Context mContext;
- private final HeadsUpManagerPhone mHeadsUpManager;
+ private final HeadsUpManager mHeadsUpManager;
private final NotificationShadeWindowController mNotificationShadeWindowController;
private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
private boolean mIsStatusBarExpanded = false;
private boolean mShouldAdjustInsets = false;
- private CentralSurfaces mCentralSurfaces;
private View mNotificationShadeWindowView;
private View mNotificationPanelView;
private boolean mForceCollapsedUntilLayout = false;
@@ -72,6 +75,8 @@
private Region mTouchableRegion = new Region();
private int mDisplayCutoutTouchableRegionSize;
private int mStatusBarHeight;
+ private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
+ private final AlternateBouncerInteractor mAlternateBouncerInteractor;
private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener;
@@ -80,12 +85,15 @@
Context context,
NotificationShadeWindowController notificationShadeWindowController,
ConfigurationController configurationController,
- HeadsUpManagerPhone headsUpManager,
+ HeadsUpManager headsUpManager,
ShadeExpansionStateManager shadeExpansionStateManager,
+ ShadeInteractor shadeInteractor,
Provider<SceneInteractor> sceneInteractor,
- Provider<JavaAdapter> javaAdapter,
+ JavaAdapter javaAdapter,
SceneContainerFlags sceneContainerFlags,
- UnlockedScreenOffAnimationController unlockedScreenOffAnimationController
+ UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
+ PrimaryBouncerInteractor primaryBouncerInteractor,
+ AlternateBouncerInteractor alternateBouncerInteractor
) {
mContext = context;
initResources();
@@ -120,21 +128,20 @@
});
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
- shadeExpansionStateManager.addFullExpansionListener(this::onShadeExpansionFullyChanged);
+ javaAdapter.alwaysCollectFlow(shadeInteractor.isAnyExpanded(), this::onShadeOrQsExpanded);
if (sceneContainerFlags.isEnabled()) {
- javaAdapter.get().alwaysCollectFlow(
+ javaAdapter.alwaysCollectFlow(
sceneInteractor.get().isVisible(),
- this::onShadeExpansionFullyChanged);
+ this::onShadeOrQsExpanded);
}
+ mPrimaryBouncerInteractor = primaryBouncerInteractor;
+ mAlternateBouncerInteractor = alternateBouncerInteractor;
mOnComputeInternalInsetsListener = this::onComputeInternalInsets;
}
- protected void setup(
- @NonNull CentralSurfaces centralSurfaces,
- @NonNull View notificationShadeWindowView) {
- mCentralSurfaces = centralSurfaces;
+ protected void setup(@NonNull View notificationShadeWindowView) {
mNotificationShadeWindowView = notificationShadeWindowView;
mNotificationPanelView = mNotificationShadeWindowView.findViewById(R.id.notification_panel);
}
@@ -146,7 +153,7 @@
pw.println(mTouchableRegion);
}
- private void onShadeExpansionFullyChanged(Boolean isExpanded) {
+ private void onShadeOrQsExpanded(Boolean isExpanded) {
if (isExpanded != mIsStatusBarExpanded) {
mIsStatusBarExpanded = isExpanded;
if (isExpanded) {
@@ -260,7 +267,8 @@
// since we don't want stray touches to go through the light reveal scrim to whatever is
// underneath.
return mIsStatusBarExpanded
- || mCentralSurfaces.isBouncerShowing()
+ || mPrimaryBouncerInteractor.isShowing().getValue()
+ || mAlternateBouncerInteractor.isVisibleState()
|| mUnlockedScreenOffAnimationController.isAnimationPlaying();
}
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/phone/SystemUIBottomSheetDialog.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialog.kt
new file mode 100644
index 0000000..85fd2af
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialog.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.phone
+
+import android.app.Dialog
+import android.content.Context
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.os.Bundle
+import android.view.Gravity
+import android.view.WindowManager
+import com.android.systemui.res.R
+
+/** A dialog shown as a bottom sheet. */
+open class SystemUIBottomSheetDialog(
+ context: Context,
+ theme: Int = R.style.Theme_SystemUI_Dialog,
+) : Dialog(context, theme) {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ window?.apply {
+ setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL)
+ addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS)
+
+ setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
+ setGravity(Gravity.BOTTOM)
+ val edgeToEdgeHorizontally =
+ context.resources.getBoolean(R.bool.config_edgeToEdgeBottomSheetDialog)
+ if (edgeToEdgeHorizontally) {
+ decorView.setPadding(0, 0, 0, 0)
+ setLayout(
+ WindowManager.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.WRAP_CONTENT
+ )
+
+ val lp = attributes
+ lp.fitInsetsSides = 0
+ lp.horizontalMargin = 0f
+ attributes = lp
+ }
+ }
+ setCanceledOnTouchOutside(true)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index babd435..63c022c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -38,11 +38,11 @@
import com.android.app.animation.InterpolatorsAndroidX;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.statusbar.CommandQueue;
@@ -74,8 +74,6 @@
import com.android.systemui.util.CarrierConfigTracker.DefaultDataSubscriptionChangedListener;
import com.android.systemui.util.settings.SecureSettings;
-import kotlin.Unit;
-
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -85,6 +83,7 @@
import java.util.concurrent.Executor;
import javax.inject.Inject;
+import kotlin.Unit;
/**
* Contains the collapsed status bar and handles hiding/showing based on disable flags
@@ -279,7 +278,8 @@
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mDumpManager.registerDumpable(getClass().getSimpleName(), this);
- mStatusBarFragmentComponent = mStatusBarFragmentComponentFactory.create(this);
+ mStatusBarFragmentComponent = mStatusBarFragmentComponentFactory.create(
+ (PhoneStatusBarView) getView());
mStatusBarFragmentComponent.init();
mStartableStates.clear();
for (Startable startable : mStatusBarFragmentComponent.getStartables()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
index d9a5844..0618abb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
@@ -27,11 +27,11 @@
import com.android.systemui.statusbar.phone.StatusBarDemoMode;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
-import java.util.Set;
-
import dagger.BindsInstance;
import dagger.Subcomponent;
+import java.util.Set;
+
/**
* A subcomponent that gets re-created each time we create a new {@link CollapsedStatusBarFragment}.
*
@@ -54,7 +54,7 @@
@Subcomponent.Factory
interface Factory {
StatusBarFragmentComponent create(
- @BindsInstance CollapsedStatusBarFragment collapsedStatusBarFragment);
+ @BindsInstance @RootView PhoneStatusBarView phoneStatusBarView);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
index 6ef877b..3741f14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
@@ -19,15 +19,14 @@
import android.view.View;
import android.view.ViewStub;
-import com.android.systemui.res.R;
import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.dagger.qualifiers.RootView;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.HeadsUpStatusBarView;
import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions;
import com.android.systemui.statusbar.phone.PhoneStatusBarView;
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
import com.android.systemui.statusbar.phone.StatusBarLocation;
-import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer;
import com.android.systemui.statusbar.policy.Clock;
import com.android.systemui.statusbar.window.StatusBarWindowController;
@@ -51,15 +50,6 @@
/** */
@Provides
- @RootView
- @StatusBarFragmentScope
- static PhoneStatusBarView providePhoneStatusBarView(
- CollapsedStatusBarFragment collapsedStatusBarFragment) {
- return (PhoneStatusBarView) collapsedStatusBarFragment.getView();
- }
-
- /** */
- @Provides
@StatusBarFragmentScope
static BatteryMeterView provideBatteryMeterView(@RootView PhoneStatusBarView view) {
return view.findViewById(R.id.battery);
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/pipeline/mobile/data/MobileInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
index aacdc63..3522b9a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
@@ -190,6 +190,24 @@
fun logOnSimStateChanged() {
buffer.log(TAG, LogLevel.INFO, "onSimStateChanged")
}
+
+ fun logPrioritizedNetworkAvailable(netId: Int) {
+ buffer.log(
+ TAG,
+ LogLevel.INFO,
+ { int1 = netId },
+ { "Found prioritized network (nedId=$int1)" },
+ )
+ }
+
+ fun logPrioritizedNetworkLost(netId: Int) {
+ buffer.log(
+ TAG,
+ LogLevel.INFO,
+ { int1 = netId },
+ { "Lost prioritized network (nedId=$int1)" },
+ )
+ }
}
private const val TAG = "MobileInputLog"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
index a89b1b2..679426d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
@@ -131,6 +131,12 @@
*/
val isAllowedDuringAirplaneMode: StateFlow<Boolean>
+ /**
+ * True if this network has NET_CAPABILITIY_PRIORITIZE_LATENCY, and can be considered to be a
+ * network slice
+ */
+ val hasPrioritizedNetworkCapabilities: StateFlow<Boolean>
+
companion object {
/** The default number of levels to use for [numberOfLevels]. */
const val DEFAULT_NUM_LEVELS = 4
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt
index c576b82..caa9d1a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt
@@ -191,6 +191,8 @@
override val isAllowedDuringAirplaneMode = MutableStateFlow(false)
+ override val hasPrioritizedNetworkCapabilities = MutableStateFlow(false)
+
/**
* Process a new demo mobile event. Note that [resolvedNetworkType] must be passed in separately
* from the event, due to the requirement to reverse the mobile mappings lookup in the top-level
@@ -225,6 +227,7 @@
_resolvedNetworkType.value = resolvedNetworkType
isAllowedDuringAirplaneMode.value = false
+ hasPrioritizedNetworkCapabilities.value = event.slice
}
fun processCarrierMergedEvent(event: FakeWifiEventModel.CarrierMerged) {
@@ -250,6 +253,7 @@
_isGsm.value = false
_carrierNetworkChangeActive.value = false
isAllowedDuringAirplaneMode.value = true
+ hasPrioritizedNetworkCapabilities.value = false
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoModeMobileConnectionDataSource.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoModeMobileConnectionDataSource.kt
index d4ddb85..4cd877e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoModeMobileConnectionDataSource.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoModeMobileConnectionDataSource.kt
@@ -76,6 +76,7 @@
val carrierNetworkChange = getString("carriernetworkchange") == "show"
val roaming = getString("roam") == "show"
val name = getString("networkname") ?: "demo mode"
+ val slice = getString("slice").toBoolean()
return Mobile(
level = level,
@@ -87,6 +88,7 @@
carrierNetworkChange = carrierNetworkChange,
roaming = roaming,
name = name,
+ slice = slice,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/model/FakeNetworkEventModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/model/FakeNetworkEventModel.kt
index 8b03f71..0aa95f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/model/FakeNetworkEventModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/model/FakeNetworkEventModel.kt
@@ -36,6 +36,7 @@
val carrierNetworkChange: Boolean,
val roaming: Boolean,
val name: String,
+ val slice: Boolean = false,
) : FakeNetworkEventModel
data class MobileDisabled(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
index 28be3be..27edd1e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
@@ -174,6 +174,13 @@
*/
override val isAllowedDuringAirplaneMode = MutableStateFlow(true).asStateFlow()
+ /**
+ * It's not currently considered possible that a carrier merged network can have these
+ * prioritized capabilities. If we need to track them, we can add the same check as is in
+ * [MobileConnectionRepositoryImpl].
+ */
+ override val hasPrioritizedNetworkCapabilities = MutableStateFlow(false).asStateFlow()
+
override val dataEnabled: StateFlow<Boolean> = wifiRepository.isWifiEnabled
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
index ee11c06..6b61921 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
@@ -309,6 +309,15 @@
activeRepo.value.isAllowedDuringAirplaneMode.value,
)
+ override val hasPrioritizedNetworkCapabilities =
+ activeRepo
+ .flatMapLatest { it.hasPrioritizedNetworkCapabilities }
+ .stateIn(
+ scope,
+ SharingStarted.WhileSubscribed(),
+ activeRepo.value.hasPrioritizedNetworkCapabilities.value,
+ )
+
class Factory
@Inject
constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
index dc50990..760dd7e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
@@ -21,6 +21,11 @@
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
+import android.net.ConnectivityManager
+import android.net.ConnectivityManager.NetworkCallback
+import android.net.Network
+import android.net.NetworkCapabilities
+import android.net.NetworkRequest
import android.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN
import android.telephony.CellSignalStrengthCdma
import android.telephony.ServiceState
@@ -91,6 +96,7 @@
subscriptionModel: StateFlow<SubscriptionModel?>,
defaultNetworkName: NetworkNameModel,
networkNameSeparator: String,
+ connectivityManager: ConnectivityManager,
private val telephonyManager: TelephonyManager,
systemUiCarrierConfig: SystemUiCarrierConfig,
broadcastDispatcher: BroadcastDispatcher,
@@ -374,11 +380,50 @@
/** Typical mobile connections aren't available during airplane mode. */
override val isAllowedDuringAirplaneMode = MutableStateFlow(false).asStateFlow()
+ /**
+ * Currently, a network with NET_CAPABILITY_PRIORITIZE_LATENCY is the only type of network that
+ * we consider to be a "network slice". _PRIORITIZE_BANDWIDTH may be added in the future. Any of
+ * these capabilities that are used here must also be represented in the
+ * self_certified_network_capabilities.xml config file
+ */
+ @SuppressLint("WrongConstant")
+ private val networkSliceRequest =
+ NetworkRequest.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY)
+ .setSubscriptionIds(setOf(subId))
+ .build()
+
+ @SuppressLint("MissingPermission")
+ override val hasPrioritizedNetworkCapabilities: StateFlow<Boolean> =
+ conflatedCallbackFlow {
+ // Our network callback listens only for this.subId && net_cap_prioritize_latency
+ // therefore our state is a simple mapping of whether or not that network exists
+ val callback =
+ object : NetworkCallback() {
+ override fun onAvailable(network: Network) {
+ logger.logPrioritizedNetworkAvailable(network.netId)
+ trySend(true)
+ }
+
+ override fun onLost(network: Network) {
+ logger.logPrioritizedNetworkLost(network.netId)
+ trySend(false)
+ }
+ }
+
+ connectivityManager.registerNetworkCallback(networkSliceRequest, callback)
+
+ awaitClose { connectivityManager.unregisterNetworkCallback(callback) }
+ }
+ .flowOn(bgDispatcher)
+ .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
class Factory
@Inject
constructor(
private val context: Context,
private val broadcastDispatcher: BroadcastDispatcher,
+ private val connectivityManager: ConnectivityManager,
private val telephonyManager: TelephonyManager,
private val logger: MobileInputLogger,
private val carrierConfigRepository: CarrierConfigRepository,
@@ -399,6 +444,7 @@
subscriptionModel,
defaultNetworkName,
networkNameSeparator,
+ connectivityManager,
telephonyManager.createForSubscriptionId(subId),
carrierConfigRepository.getOrCreateConfigForSubId(subId),
broadcastDispatcher,
@@ -421,11 +467,17 @@
*/
sealed interface CallbackEvent {
data class OnCarrierNetworkChange(val active: Boolean) : CallbackEvent
+
data class OnDataActivity(val direction: Int) : CallbackEvent
+
data class OnDataConnectionStateChanged(val dataState: Int) : CallbackEvent
+
data class OnDataEnabledChanged(val enabled: Boolean) : CallbackEvent
+
data class OnDisplayInfoChanged(val telephonyDisplayInfo: TelephonyDisplayInfo) : CallbackEvent
+
data class OnServiceStateChanged(val serviceState: ServiceState) : CallbackEvent
+
data class OnSignalStrengthChanged(val signalStrength: SignalStrength) : CallbackEvent
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
index 4bf297c..fe49c07 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
@@ -76,6 +76,9 @@
/** Observable for RAT type (network type) indicator */
val networkTypeIconGroup: StateFlow<NetworkTypeIconModel>
+ /** Whether or not to show the slice attribution */
+ val showSliceAttribution: StateFlow<Boolean>
+
/**
* Provider name for this network connection. The name can be one of 3 values:
* 1. The default network name, if one is configured
@@ -238,6 +241,9 @@
DefaultIcon(defaultMobileIconGroup.value),
)
+ override val showSliceAttribution: StateFlow<Boolean> =
+ connectionRepository.hasPrioritizedNetworkCapabilities
+
override val isRoaming: StateFlow<Boolean> =
combine(
connectionRepository.carrierNetworkChangeActive,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
index f0470ca..b93e443 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
@@ -16,11 +16,13 @@
package com.android.systemui.statusbar.pipeline.mobile.ui.binder
+import android.annotation.ColorInt
import android.content.res.ColorStateList
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup
+import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.Space
import androidx.core.view.isVisible
@@ -28,10 +30,11 @@
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.android.settingslib.graph.SignalDrawable
-import com.android.systemui.res.R
import com.android.systemui.common.ui.binder.ContentDescriptionViewBinder
import com.android.systemui.common.ui.binder.IconViewBinder
import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.plugins.DarkIconDispatcher
+import com.android.systemui.res.R
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN
import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger
@@ -43,6 +46,11 @@
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch
+private data class Colors(
+ @ColorInt val tint: Int,
+ @ColorInt val contrast: Int,
+)
+
object MobileIconBinder {
/** Binds the view to the view-model, continuing to update the former based on the latter */
@JvmStatic
@@ -57,6 +65,7 @@
val activityIn = view.requireViewById<ImageView>(R.id.mobile_in)
val activityOut = view.requireViewById<ImageView>(R.id.mobile_out)
val networkTypeView = view.requireViewById<ImageView>(R.id.mobile_type)
+ val networkTypeContainer = view.requireViewById<FrameLayout>(R.id.mobile_type_container)
val iconView = view.requireViewById<ImageView>(R.id.mobile_signal)
val mobileDrawable = SignalDrawable(view.context).also { iconView.setImageDrawable(it) }
val roamingView = view.requireViewById<ImageView>(R.id.mobile_roaming)
@@ -70,7 +79,13 @@
@StatusBarIconView.VisibleState
val visibilityState: MutableStateFlow<Int> = MutableStateFlow(initialVisibilityState)
- val iconTint: MutableStateFlow<Int> = MutableStateFlow(viewModel.defaultColor)
+ val iconTint: MutableStateFlow<Colors> =
+ MutableStateFlow(
+ Colors(
+ tint = DarkIconDispatcher.DEFAULT_ICON_TINT,
+ contrast = DarkIconDispatcher.DEFAULT_INVERSE_ICON_TINT
+ )
+ )
val decorTint: MutableStateFlow<Int> = MutableStateFlow(viewModel.defaultColor)
var isCollecting = false
@@ -139,7 +154,26 @@
dataTypeId,
)
dataTypeId?.let { IconViewBinder.bind(dataTypeId, networkTypeView) }
- networkTypeView.visibility = if (dataTypeId != null) VISIBLE else GONE
+ networkTypeContainer.visibility =
+ if (dataTypeId != null) VISIBLE else GONE
+ }
+ }
+
+ // Set the network type background
+ launch {
+ viewModel.networkTypeBackground.collect { background ->
+ networkTypeContainer.setBackgroundResource(background?.res ?: 0)
+
+ // Tint will invert when this bit changes
+ if (background?.res != null) {
+ networkTypeContainer.backgroundTintList =
+ ColorStateList.valueOf(iconTint.value.tint)
+ networkTypeView.imageTintList =
+ ColorStateList.valueOf(iconTint.value.contrast)
+ } else {
+ networkTypeView.imageTintList =
+ ColorStateList.valueOf(iconTint.value.tint)
+ }
}
}
@@ -164,14 +198,24 @@
// Set the tint
launch {
- iconTint.collect { tint ->
- val tintList = ColorStateList.valueOf(tint)
- iconView.imageTintList = tintList
- networkTypeView.imageTintList = tintList
- roamingView.imageTintList = tintList
- activityIn.imageTintList = tintList
- activityOut.imageTintList = tintList
- dotView.setDecorColor(tint)
+ iconTint.collect { colors ->
+ val tint = ColorStateList.valueOf(colors.tint)
+ val contrast = ColorStateList.valueOf(colors.contrast)
+
+ iconView.imageTintList = tint
+
+ // If the bg is visible, tint it and use the contrast for the fg
+ if (viewModel.networkTypeBackground.value != null) {
+ networkTypeContainer.backgroundTintList = tint
+ networkTypeView.imageTintList = contrast
+ } else {
+ networkTypeView.imageTintList = tint
+ }
+
+ roamingView.imageTintList = tint
+ activityIn.imageTintList = tint
+ activityOut.imageTintList = tint
+ dotView.setDecorColor(colors.tint)
}
}
@@ -196,8 +240,8 @@
visibilityState.value = state
}
- override fun onIconTintChanged(newTint: Int) {
- iconTint.value = newTint
+ override fun onIconTintChanged(newTint: Int, contrastTint: Int) {
+ iconTint.value = Colors(tint = newTint, contrast = contrastTint)
}
override fun onDecorTintChanged(newTint: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
index dfabeea..d88c9ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
@@ -19,7 +19,10 @@
import com.android.settingslib.AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags.NEW_NETWORK_SLICE_UI
import com.android.systemui.log.table.logDiffsForTable
+import com.android.systemui.res.R
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractor
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
@@ -47,6 +50,8 @@
val roaming: Flow<Boolean>
/** The RAT icon (LTE, 3G, 5G, etc) to be displayed. Null if we shouldn't show anything */
val networkTypeIcon: Flow<Icon.Resource?>
+ /** The slice attribution. Drawn as a background layer */
+ val networkTypeBackground: StateFlow<Icon.Resource?>
val activityInVisible: Flow<Boolean>
val activityOutVisible: Flow<Boolean>
val activityContainerVisible: Flow<Boolean>
@@ -67,12 +72,12 @@
*/
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@OptIn(ExperimentalCoroutinesApi::class)
-class MobileIconViewModel
-constructor(
+class MobileIconViewModel(
override val subscriptionId: Int,
iconInteractor: MobileIconInteractor,
airplaneModeInteractor: AirplaneModeInteractor,
constants: ConnectivityConstants,
+ flags: FeatureFlagsClassic,
scope: CoroutineScope,
) : MobileIconViewModelCommon {
override val isVisible: StateFlow<Boolean> =
@@ -152,6 +157,20 @@
.distinctUntilChanged()
.stateIn(scope, SharingStarted.WhileSubscribed(), null)
+ override val networkTypeBackground =
+ if (!flags.isEnabled(NEW_NETWORK_SLICE_UI)) {
+ flowOf(null)
+ } else {
+ iconInteractor.showSliceAttribution.map {
+ if (it) {
+ Icon.Resource(R.drawable.mobile_network_type_background, null)
+ } else {
+ null
+ }
+ }
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), null)
+
override val roaming: StateFlow<Boolean> =
iconInteractor.isRoaming
.logDiffsForTable(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
index 0f55910..be843ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
@@ -19,6 +19,7 @@
import androidx.annotation.VisibleForTesting
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.statusbar.phone.StatusBarLocation
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractor
@@ -54,6 +55,7 @@
private val interactor: MobileIconsInteractor,
private val airplaneModeInteractor: AirplaneModeInteractor,
private val constants: ConnectivityConstants,
+ private val flags: FeatureFlagsClassic,
@Application private val scope: CoroutineScope,
) {
@VisibleForTesting val mobileIconSubIdCache = mutableMapOf<Int, MobileIconViewModel>()
@@ -113,6 +115,7 @@
interactor.getMobileConnectionInteractorForSubId(subId),
airplaneModeInteractor,
constants,
+ flags,
scope,
)
.also { mobileIconSubIdCache[subId] = it }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/ModernStatusBarViewBinding.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/ModernStatusBarViewBinding.kt
index 81f8683..790596e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/ModernStatusBarViewBinding.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/ModernStatusBarViewBinding.kt
@@ -32,8 +32,8 @@
/** Notifies that the visibility state has changed. */
fun onVisibilityStateChanged(@StatusBarIconView.VisibleState state: Int)
- /** Notifies that the icon tint has been updated. */
- fun onIconTintChanged(newTint: Int)
+ /** Notifies that the icon tint has been updated. Includes a contrast for layered drawables */
+ fun onIconTintChanged(newTint: Int, contrastTint: Int)
/** Notifies that the decor tint has been updated (used only for the dot). */
fun onDecorTintChanged(newTint: Int)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt
index fe69d81..3b87bed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt
@@ -20,8 +20,8 @@
import android.graphics.Rect
import android.util.AttributeSet
import android.view.Gravity
-import com.android.systemui.res.R
import com.android.systemui.plugins.DarkIconDispatcher
+import com.android.systemui.res.R
import com.android.systemui.statusbar.BaseStatusBarFrameLayout
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.StatusBarIconView.STATE_DOT
@@ -51,13 +51,23 @@
override fun getSlot() = slot
override fun onDarkChanged(areas: ArrayList<Rect>?, darkIntensity: Float, tint: Int) {
+ // nop
+ }
+
+ override fun onDarkChangedWithContrast(areas: ArrayList<Rect>, tint: Int, contrastTint: Int) {
val newTint = DarkIconDispatcher.getTint(areas, this, tint)
- binding.onIconTintChanged(newTint)
+ val contrast = DarkIconDispatcher.getInverseTint(areas, this, contrastTint)
+
+ binding.onIconTintChanged(newTint, contrast)
binding.onDecorTintChanged(newTint)
}
override fun setStaticDrawableColor(color: Int) {
- binding.onIconTintChanged(color)
+ // nop
+ }
+
+ override fun setStaticDrawableColor(color: Int, foregroundColor: Int) {
+ binding.onIconTintChanged(color, foregroundColor)
}
override fun setDecorColor(color: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
index a9ac51d..6005527 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
@@ -23,9 +23,9 @@
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.res.R
import com.android.systemui.common.ui.binder.IconViewBinder
import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.res.R
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN
import com.android.systemui.statusbar.pipeline.shared.ui.binder.ModernStatusBarViewBinding
@@ -165,7 +165,7 @@
visibilityState.value = state
}
- override fun onIconTintChanged(newTint: Int) {
+ override fun onIconTintChanged(newTint: Int, contrastTint: Int /* unused */) {
iconTint.value = newTint
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
index e59ec04..cec76f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
@@ -34,8 +34,8 @@
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.EventLogTags;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
@@ -47,7 +47,8 @@
* A manager which handles heads up notifications which is a special mode where
* they simply peek from the top of the screen.
*/
-public abstract class HeadsUpManager extends AlertingNotificationManager {
+public abstract class BaseHeadsUpManager extends AlertingNotificationManager implements
+ HeadsUpManager {
private static final String TAG = "HeadsUpManager";
private static final String SETTING_HEADS_UP_SNOOZE_LENGTH_MS = "heads_up_snooze_length_ms";
@@ -81,7 +82,7 @@
}
}
- public HeadsUpManager(@NonNull final Context context,
+ public BaseHeadsUpManager(@NonNull final Context context,
HeadsUpManagerLogger logger,
@Main Handler handler,
AccessibilityManagerWrapper accessibilityManagerWrapper,
@@ -131,6 +132,7 @@
mListeners.remove(listener);
}
+ /** Updates the notification with the given key. */
public void updateNotification(@NonNull String key, boolean alert) {
super.updateNotification(key, alert);
HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
@@ -146,7 +148,7 @@
// the NotificationEntry into AlertingNotificationManager's mAlertEntries map.
return hasFullScreenIntent(entry);
}
- return hasFullScreenIntent(entry) && !headsUpEntry.wasUnpinned;
+ return hasFullScreenIntent(entry) && !headsUpEntry.mWasUnpinned;
}
protected boolean hasFullScreenIntent(@NonNull NotificationEntry entry) {
@@ -154,11 +156,11 @@
}
protected void setEntryPinned(
- @NonNull HeadsUpManager.HeadsUpEntry headsUpEntry, boolean isPinned) {
+ @NonNull BaseHeadsUpManager.HeadsUpEntry headsUpEntry, boolean isPinned) {
mLogger.logSetEntryPinned(headsUpEntry.mEntry, isPinned);
NotificationEntry entry = headsUpEntry.mEntry;
if (!isPinned) {
- headsUpEntry.wasUnpinned = true;
+ headsUpEntry.mWasUnpinned = true;
}
if (entry.isRowPinned() != isPinned) {
entry.setRowPinned(isPinned);
@@ -292,10 +294,12 @@
mUser = user;
}
+ /** Returns the ID of the current user. */
public int getUser() {
return mUser;
}
+ @Override
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("HeadsUpManager state:");
dumpInternal(pw, args);
@@ -309,9 +313,9 @@
for (AlertEntry entry: mAlertEntries.values()) {
pw.print(" HeadsUpEntry="); pw.println(entry.mEntry);
}
- int N = mSnoozedPackages.size();
- pw.println(" snoozed packages: " + N);
- for (int i = 0; i < N; i++) {
+ int n = mSnoozedPackages.size();
+ pw.println(" snoozed packages: " + n);
+ for (int i = 0; i < n; i++) {
pw.print(" "); pw.print(mSnoozedPackages.valueAt(i));
pw.print(", "); pw.println(mSnoozedPackages.keyAt(i));
}
@@ -399,20 +403,20 @@
*
* @param entry the entry that might be indirectly removed by the user's action
*
- * @see com.android.systemui.statusbar.notification.collection.coordinator.HeadsUpCoordinator#mActionPressListener
+ * @see HeadsUpCoordinator#mActionPressListener
* @see #canRemoveImmediately(String)
*/
public void setUserActionMayIndirectlyRemove(@NonNull NotificationEntry entry) {
HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.getKey());
if (headsUpEntry != null) {
- headsUpEntry.userActionMayIndirectlyRemove = true;
+ headsUpEntry.mUserActionMayIndirectlyRemove = true;
}
}
@Override
public boolean canRemoveImmediately(@NonNull String key) {
HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
- if (headsUpEntry != null && headsUpEntry.userActionMayIndirectlyRemove) {
+ if (headsUpEntry != null && headsUpEntry.mUserActionMayIndirectlyRemove) {
return true;
}
return super.canRemoveImmediately(key);
@@ -424,9 +428,6 @@
return new HeadsUpEntry();
}
- public void onDensityOrFontScaleChanged() {
- }
-
/**
* Determines if the notification is for a critical call that must display on top of an active
* input notification.
@@ -445,16 +446,16 @@
* lifecycle automatically when created.
*/
protected class HeadsUpEntry extends AlertEntry {
- public boolean remoteInputActive;
- public boolean userActionMayIndirectlyRemove;
+ public boolean mRemoteInputActive;
+ public boolean mUserActionMayIndirectlyRemove;
- protected boolean expanded;
- protected boolean wasUnpinned;
+ protected boolean mExpanded;
+ protected boolean mWasUnpinned;
@Override
public boolean isSticky() {
- return (mEntry.isRowPinned() && expanded)
- || remoteInputActive
+ return (mEntry.isRowPinned() && mExpanded)
+ || mRemoteInputActive
|| hasFullScreenIntent(mEntry);
}
@@ -490,9 +491,9 @@
return 1;
}
- if (remoteInputActive && !headsUpEntry.remoteInputActive) {
+ if (mRemoteInputActive && !headsUpEntry.mRemoteInputActive) {
return -1;
- } else if (!remoteInputActive && headsUpEntry.remoteInputActive) {
+ } else if (!mRemoteInputActive && headsUpEntry.mRemoteInputActive) {
return 1;
}
@@ -500,14 +501,14 @@
}
public void setExpanded(boolean expanded) {
- this.expanded = expanded;
+ this.mExpanded = expanded;
}
@Override
public void reset() {
super.reset();
- expanded = false;
- remoteInputActive = false;
+ mExpanded = false;
+ mRemoteInputActive = false;
}
@Override
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/ConfigurationControllerExt.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationControllerExt.kt
new file mode 100644
index 0000000..21acfb4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationControllerExt.kt
@@ -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 com.android.systemui.statusbar.policy
+
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * A [Flow] that emits whenever screen density or font scale has changed.
+ *
+ * @see ConfigurationController.ConfigurationListener.onDensityOrFontScaleChanged
+ */
+val ConfigurationController.onDensityOrFontScaleChanged: Flow<Unit>
+ get() =
+ ConflatedCallbackFlow.conflatedCallbackFlow {
+ val listener =
+ object : ConfigurationController.ConfigurationListener {
+ override fun onDensityOrFontScaleChanged() {
+ trySend(Unit)
+ }
+ }
+ addCallback(listener)
+ awaitClose { removeCallback(listener) }
+ }
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/HeadsUpManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt
new file mode 100644
index 0000000..d9527fe
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt
@@ -0,0 +1,245 @@
+package com.android.systemui.statusbar.policy
+
+import android.graphics.Region
+import com.android.systemui.Dumpable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import dagger.Binds
+import dagger.Module
+import java.io.PrintWriter
+import java.util.stream.Stream
+import javax.inject.Inject
+
+/**
+ * A manager which handles heads up notifications which is a special mode where they simply peek
+ * from the top of the screen.
+ */
+interface HeadsUpManager : Dumpable {
+ /** The stream of all current notifications managed by this manager. */
+ val allEntries: Stream<NotificationEntry>
+
+ /** Add a listener to receive callbacks onHeadsUpGoingAway. */
+ fun addHeadsUpPhoneListener(listener: OnHeadsUpPhoneListenerChange)
+
+ /** Adds an OnHeadUpChangedListener to observe events. */
+ fun addListener(listener: OnHeadsUpChangedListener)
+
+ fun addSwipedOutNotification(key: String)
+
+ /**
+ * Whether or not the alert can be removed currently. If it hasn't been on screen long enough it
+ * should not be removed unless forced
+ *
+ * @param key the key to check if removable
+ * @return true if the alert entry can be removed
+ */
+ fun canRemoveImmediately(key: String): Boolean
+
+ /**
+ * Compare two entries and decide how they should be ranked.
+ *
+ * @return -1 if the first argument should be ranked higher than the second, 1 if the second one
+ * should be ranked higher and 0 if they are equal.
+ */
+ fun compare(a: NotificationEntry?, b: NotificationEntry?): Int
+ /**
+ * Extends the lifetime of the currently showing pulsing notification so that the pulse lasts
+ * longer.
+ */
+ fun extendHeadsUp()
+
+ /** Returns when a HUN entry should be removed in milliseconds from now. */
+ fun getEarliestRemovalTime(key: String?): Long
+
+ /** Returns the top Heads Up Notification, which appears to show at first. */
+ fun getTopEntry(): NotificationEntry?
+
+ /**
+ * Gets the touchable region needed for heads up notifications. Returns null if no touchable
+ * region is required (ie: no heads up notification currently exists).
+ */
+ fun getTouchableRegion(): Region?
+
+ /**
+ * Whether or not there are any active alerting notifications.
+ *
+ * @return true if there is an alert, false otherwise
+ */
+ fun hasNotifications(): Boolean = false
+
+ /** Returns whether there are any pinned Heads Up Notifications or not. */
+ fun hasPinnedHeadsUp(): Boolean
+
+ /** Returns whether or not the given notification is alerting and managed by this manager. */
+ fun isAlerting(key: String): Boolean
+
+ fun isHeadsUpGoingAway(): Boolean
+
+ /** Returns if the given notification is snoozed or not. */
+ fun isSnoozed(packageName: String): Boolean
+
+ /** Returns whether the entry is (pinned and expanded) or (has an active remote input). */
+ fun isSticky(key: String?): Boolean
+
+ fun isTrackingHeadsUp(): Boolean
+
+ fun onExpandingFinished()
+
+ /** Removes the OnHeadUpChangedListener from the observer list. */
+ fun removeListener(listener: OnHeadsUpChangedListener)
+
+ /**
+ * Try to remove the notification. May not succeed if the notification has not been shown long
+ * enough and needs to be kept around.
+ *
+ * @param key the key of the notification to remove
+ * @param releaseImmediately force a remove regardless of earliest removal time
+ * @return true if notification is removed, false otherwise
+ */
+ fun removeNotification(key: String, releaseImmediately: Boolean): Boolean
+
+ /**
+ * Try to remove the notification. May not succeed if the notification has not been shown long
+ * enough and needs to be kept around.
+ *
+ * @param key the key of the notification to remove
+ * @param releaseImmediately force a remove regardless of earliest removal time
+ * @param animate if true, animate the removal
+ * @return true if notification is removed, false otherwise
+ */
+ fun removeNotification(key: String, releaseImmediately: Boolean, animate: Boolean): Boolean
+
+ /** Clears all managed notifications. */
+ fun releaseAllImmediately()
+
+ fun setAnimationStateHandler(handler: AnimationStateHandler)
+
+ /**
+ * Set an entry to be expanded and therefore stick in the heads up area if it's pinned until
+ * it's collapsed again.
+ */
+ fun setExpanded(entry: NotificationEntry, expanded: Boolean)
+
+ /**
+ * Sets whether an entry's guts are exposed and therefore it should stick in the heads up area
+ * if it's pinned until it's hidden again.
+ */
+ fun setGutsShown(entry: NotificationEntry, gutsShown: Boolean)
+
+ /**
+ * Set that we are exiting the headsUp pinned mode, but some notifications might still be
+ * animating out. This is used to keep the touchable regions in a reasonable state.
+ */
+ fun setHeadsUpGoingAway(headsUpGoingAway: Boolean)
+
+ /**
+ * Notifies that a remote input textbox in notification gets active or inactive.
+ *
+ * @param entry The entry of the target notification.
+ * @param remoteInputActive True to notify active, False to notify inactive.
+ */
+ fun setRemoteInputActive(entry: NotificationEntry, remoteInputActive: Boolean)
+
+ fun setTrackingHeadsUp(tracking: Boolean)
+
+ /** Sets the current user. */
+ fun setUser(user: Int)
+
+ /**
+ * Notes that the user took an action on an entry that might indirectly cause the system or the
+ * app to remove the notification.
+ *
+ * @param entry the entry that might be indirectly removed by the user's action
+ * @see
+ * com.android.systemui.statusbar.notification.collection.coordinator.HeadsUpCoordinator.mActionPressListener
+ * @see .canRemoveImmediately
+ */
+ fun setUserActionMayIndirectlyRemove(entry: NotificationEntry)
+
+ /**
+ * Decides whether a click is invalid for a notification, i.e. it has not been shown long enough
+ * that a user might have consciously clicked on it.
+ *
+ * @param key the key of the touched notification
+ * @return whether the touch is invalid and should be discarded
+ */
+ fun shouldSwallowClick(key: String): Boolean
+
+ /**
+ * Called when posting a new notification that should alert the user and appear on screen. Adds
+ * the notification to be managed.
+ *
+ * @param entry entry to show
+ */
+ fun showNotification(entry: NotificationEntry)
+
+ fun snooze()
+
+ /**
+ * Unpins all pinned Heads Up Notifications.
+ *
+ * @param userUnPinned The unpinned action is trigger by user real operation.
+ */
+ fun unpinAll(userUnPinned: Boolean)
+
+ fun updateNotification(key: String, alert: Boolean)
+}
+
+/** Sets the animation state of the HeadsUpManager. */
+interface AnimationStateHandler {
+ fun setHeadsUpGoingAwayAnimationsAllowed(allowed: Boolean)
+}
+
+/** Listener to register for HeadsUpNotification Phone changes. */
+interface OnHeadsUpPhoneListenerChange {
+ /**
+ * Called when a heads up notification is 'going away' or no longer 'going away'. See
+ * [HeadsUpManager.setHeadsUpGoingAway].
+ */
+ fun onHeadsUpGoingAwayStateChanged(headsUpGoingAway: Boolean)
+}
+
+/* No op impl of HeadsUpManager. */
+class HeadsUpManagerEmptyImpl @Inject constructor() : HeadsUpManager {
+ override val allEntries = Stream.empty<NotificationEntry>()
+ override fun addHeadsUpPhoneListener(listener: OnHeadsUpPhoneListenerChange) {}
+ override fun addListener(listener: OnHeadsUpChangedListener) {}
+ override fun addSwipedOutNotification(key: String) {}
+ override fun canRemoveImmediately(key: String) = false
+ override fun compare(a: NotificationEntry?, b: NotificationEntry?) = 0
+ override fun dump(pw: PrintWriter, args: Array<out String>) {}
+ override fun extendHeadsUp() {}
+ override fun getEarliestRemovalTime(key: String?) = 0L
+ override fun getTouchableRegion(): Region? = null
+ override fun getTopEntry() = null
+ override fun hasPinnedHeadsUp() = false
+ override fun isAlerting(key: String) = false
+ override fun isHeadsUpGoingAway() = false
+ override fun isSnoozed(packageName: String) = false
+ override fun isSticky(key: String?) = false
+ override fun isTrackingHeadsUp() = false
+ override fun onExpandingFinished() {}
+ override fun releaseAllImmediately() {}
+ override fun removeListener(listener: OnHeadsUpChangedListener) {}
+ override fun removeNotification(key: String, releaseImmediately: Boolean) = false
+ override fun removeNotification(key: String, releaseImmediately: Boolean, animate: Boolean) =
+ false
+ override fun setAnimationStateHandler(handler: AnimationStateHandler) {}
+ override fun setExpanded(entry: NotificationEntry, expanded: Boolean) {}
+ override fun setGutsShown(entry: NotificationEntry, gutsShown: Boolean) {}
+ override fun setHeadsUpGoingAway(headsUpGoingAway: Boolean) {}
+ override fun setRemoteInputActive(entry: NotificationEntry, remoteInputActive: Boolean) {}
+ override fun setTrackingHeadsUp(tracking: Boolean) {}
+ override fun setUser(user: Int) {}
+ override fun setUserActionMayIndirectlyRemove(entry: NotificationEntry) {}
+ override fun shouldSwallowClick(key: String): Boolean = false
+ override fun showNotification(entry: NotificationEntry) {}
+ override fun snooze() {}
+ override fun unpinAll(userUnPinned: Boolean) {}
+ override fun updateNotification(key: String, alert: Boolean) {}
+}
+
+@Module
+interface HeadsUpEmptyImplModule {
+ @Binds @SysUISingleton fun bindsHeadsUpManager(noOpHum: HeadsUpManagerEmptyImpl): HeadsUpManager
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ResourcesSplitShadeStateController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ResourcesSplitShadeStateController.kt
index c302d6a..859e636 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ResourcesSplitShadeStateController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ResourcesSplitShadeStateController.kt
@@ -17,6 +17,7 @@
import android.content.res.Resources
import com.android.systemui.res.R
+import javax.inject.Inject
/**
* Fake SplitShadeStateController
@@ -24,7 +25,7 @@
* Identical behaviour to legacy implementation (that used LargeScreenUtils.kt) I.E., behaviour
* based solely on resources, no extra flag logic.
*/
-class ResourcesSplitShadeStateController : SplitShadeStateController {
+class ResourcesSplitShadeStateController @Inject constructor() : SplitShadeStateController {
override fun shouldUseSplitNotificationShade(resources: Resources): Boolean {
return resources.getBoolean(R.bool.config_use_split_notification_shade)
}
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/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
index a77c692..818ba95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
@@ -70,6 +70,7 @@
import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
import com.android.systemui.statusbar.policy.bluetooth.BluetoothRepository;
import com.android.systemui.statusbar.policy.bluetooth.BluetoothRepositoryImpl;
+import com.android.systemui.statusbar.policy.data.repository.DeviceProvisioningRepositoryModule;
import dagger.Binds;
import dagger.Module;
@@ -80,7 +81,7 @@
import javax.inject.Named;
/** Dagger Module for code in the statusbar.policy package. */
-@Module
+@Module(includes = { DeviceProvisioningRepositoryModule.class })
public interface StatusBarPolicyModule {
String DEVICE_STATE_ROTATION_LOCK_DEFAULTS = "DEVICE_STATE_ROTATION_LOCK_DEFAULTS";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepository.kt
new file mode 100644
index 0000000..1160d65
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepository.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.policy.data.repository
+
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+
+/** Tracks state related to device provisioning. */
+interface DeviceProvisioningRepository {
+ /**
+ * Whether this device has been provisioned.
+ *
+ * @see android.provider.Settings.Global.DEVICE_PROVISIONED
+ */
+ val isDeviceProvisioned: Flow<Boolean>
+
+ /** Whether Factory Reset Protection (FRP) is currently active, locking the device. */
+ val isFactoryResetProtectionActive: Flow<Boolean>
+}
+
+@Module
+interface DeviceProvisioningRepositoryModule {
+ @Binds fun bindsImpl(impl: DeviceProvisioningRepositoryImpl): DeviceProvisioningRepository
+}
+
+class DeviceProvisioningRepositoryImpl
+@Inject
+constructor(
+ private val deviceProvisionedController: DeviceProvisionedController,
+) : DeviceProvisioningRepository {
+
+ override val isDeviceProvisioned: Flow<Boolean> = conflatedCallbackFlow {
+ val listener =
+ object : DeviceProvisionedController.DeviceProvisionedListener {
+ override fun onDeviceProvisionedChanged() {
+ trySend(deviceProvisionedController.isDeviceProvisioned)
+ }
+ }
+ deviceProvisionedController.addCallback(listener)
+ trySend(deviceProvisionedController.isDeviceProvisioned)
+ awaitClose { deviceProvisionedController.removeCallback(listener) }
+ }
+
+ override val isFactoryResetProtectionActive: Flow<Boolean> = conflatedCallbackFlow {
+ val listener =
+ object : DeviceProvisionedController.DeviceProvisionedListener {
+ override fun onFrpActiveChanged() {
+ trySend(deviceProvisionedController.isFrpActive)
+ }
+ }
+ deviceProvisionedController.addCallback(listener)
+ trySend(deviceProvisionedController.isFrpActive)
+ awaitClose { deviceProvisionedController.removeCallback(listener) }
+ }
+}
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/user/domain/interactor/UserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
index ce9d6fd..dbc3bf3 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
@@ -325,6 +325,7 @@
addAction(Intent.ACTION_USER_SWITCHED)
addAction(Intent.ACTION_USER_STOPPED)
addAction(Intent.ACTION_USER_UNLOCKED)
+ addAction(Intent.ACTION_LOCALE_CHANGED)
},
user = UserHandle.SYSTEM,
map = { intent, _ -> intent },
@@ -615,6 +616,7 @@
) {
val shouldRefreshAllUsers =
when (intent.action) {
+ Intent.ACTION_LOCALE_CHANGED -> true
Intent.ACTION_USER_SWITCHED -> {
dismissDialog()
val selectedUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1)
@@ -644,6 +646,11 @@
}
private fun restartSecondaryService(@UserIdInt userId: Int) {
+ // Do not start service for user that is marked for deletion.
+ if (!manager.aliveUsers.map { it.id }.contains(userId)) {
+ return
+ }
+
val intent = Intent(applicationContext, SystemUISecondaryUserService::class.java)
// Disconnect from the old secondary user's service
val secondaryUserId = repository.secondaryUserId
@@ -657,6 +664,7 @@
// Connect to the new secondary user's service (purely to ensure that a persistent
// SystemUI application is created for that user)
+
if (userId != Process.myUserHandle().identifier) {
applicationContext.startServiceAsUser(
intent,
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
index d3f83b1..20f0fa8c 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
@@ -68,6 +68,7 @@
private val hasCancelButtonBeenClicked = MutableStateFlow(false)
private val isFinishRequiredDueToExecutedAction = MutableStateFlow(false)
+ private val userSwitched = MutableStateFlow(false)
/**
* Whether the observer should finish the experience. Once consumed, [onFinished] must be called
@@ -89,6 +90,7 @@
fun onFinished() {
hasCancelButtonBeenClicked.value = false
isFinishRequiredDueToExecutedAction.value = false
+ userSwitched.value = false
}
/** Notifies that the user has clicked the "open menu" button. */
@@ -121,8 +123,9 @@
hasCancelButtonBeenClicked,
// If an executed action told us to finish, we should finish,
isFinishRequiredDueToExecutedAction,
- ) { cancelButtonClicked, executedActionFinish ->
- cancelButtonClicked || executedActionFinish
+ userSwitched,
+ ) { cancelButtonClicked, executedActionFinish, userSwitched ->
+ cancelButtonClicked || executedActionFinish || userSwitched
}
private fun toViewModel(
@@ -191,7 +194,10 @@
return if (!model.isSelectable) {
null
} else {
- { userInteractor.selectUser(model.id) }
+ {
+ userInteractor.selectUser(model.id)
+ userSwitched.value = true
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/ReferenceExt.kt b/packages/SystemUI/src/com/android/systemui/util/ReferenceExt.kt
new file mode 100644
index 0000000..ac04d31
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/ReferenceExt.kt
@@ -0,0 +1,50 @@
+package com.android.systemui.util
+
+import java.lang.ref.SoftReference
+import java.lang.ref.WeakReference
+import kotlin.properties.ReadWriteProperty
+import kotlin.reflect.KProperty
+
+/**
+ * Creates a Kotlin idiomatic weak reference.
+ *
+ * Usage:
+ * ```
+ * var weakReferenceObj: Object? by weakReference(null)
+ * weakReferenceObj = Object()
+ * ```
+ */
+fun <T> weakReference(obj: T? = null): ReadWriteProperty<Any?, T?> {
+ return object : ReadWriteProperty<Any?, T?> {
+ var weakRef = WeakReference<T?>(obj)
+ override fun getValue(thisRef: Any?, property: KProperty<*>): T? {
+ return weakRef.get()
+ }
+
+ override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
+ weakRef = WeakReference(value)
+ }
+ }
+}
+
+/**
+ * Creates a Kotlin idiomatic soft reference.
+ *
+ * Usage:
+ * ```
+ * var softReferenceObj: Object? by softReference(null)
+ * softReferenceObj = Object()
+ * ```
+ */
+fun <T> softReference(obj: T? = null): ReadWriteProperty<Any?, T?> {
+ return object : ReadWriteProperty<Any?, T?> {
+ var softRef = SoftReference<T?>(obj)
+ override fun getValue(thisRef: Any?, property: KProperty<*>): T? {
+ return softRef.get()
+ }
+
+ override fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
+ softRef = SoftReference(value)
+ }
+ }
+}
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/tests/utils/src/com/android/systemui/RoboPilotTest.java b/packages/SystemUI/src/com/android/systemui/util/annotations/WeaklyReferencedCallback.java
similarity index 62%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/RoboPilotTest.java
copy to packages/SystemUI/src/com/android/systemui/util/annotations/WeaklyReferencedCallback.java
index 3fff136..855bba6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/RoboPilotTest.java
+++ b/packages/SystemUI/src/com/android/systemui/util/annotations/WeaklyReferencedCallback.java
@@ -14,18 +14,22 @@
* limitations under the License.
*/
-package com.android.systemui;
+package com.android.systemui.util.annotations;
-import java.lang.annotation.ElementType;
+import static java.lang.annotation.ElementType.TYPE;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+
/**
- * Mark as tests for Robolectric pilot projects. The filter can better help grouping test results
- * that runs on CI
+ * 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.
*/
-@Target({ElementType.METHOD, ElementType.TYPE})
-@Retention(RetentionPolicy.RUNTIME)
-public @interface RoboPilotTest {
-}
+@Retention(RetentionPolicy.CLASS)
+@Target({TYPE})
+public @interface WeaklyReferencedCallback {}
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
index 47d505e..83ff789 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
@@ -18,19 +18,24 @@
import com.android.systemui.util.time.SystemClock
import com.android.systemui.util.time.SystemClockImpl
-import kotlinx.coroutines.CoroutineStart
import java.util.concurrent.atomic.AtomicReference
+import kotlin.math.max
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
-import kotlin.math.max
/**
* Returns a new [Flow] that combines the two most recent emissions from [this] using [transform].
@@ -44,8 +49,7 @@
var previousValue: Any? = noVal
collect { newVal ->
if (previousValue != noVal) {
- @Suppress("UNCHECKED_CAST")
- emit(transform(previousValue as T, newVal))
+ @Suppress("UNCHECKED_CAST") emit(transform(previousValue as T, newVal))
}
previousValue = newVal
}
@@ -60,13 +64,11 @@
fun <T, R> Flow<T>.pairwiseBy(
initialValue: T,
transform: suspend (previousValue: T, newValue: T) -> R,
-): Flow<R> =
- onStart { emit(initialValue) }.pairwiseBy(transform)
+): Flow<R> = onStart { emit(initialValue) }.pairwiseBy(transform)
/**
* Returns a new [Flow] that combines the two most recent emissions from [this] using [transform].
*
- *
* The output of [getInitialValue] will be used as the "old" value for the first emission. As
* opposed to the initial value in the above [pairwiseBy], [getInitialValue] can do some work before
* returning the initial value.
@@ -76,8 +78,7 @@
fun <T, R> Flow<T>.pairwiseBy(
getInitialValue: suspend () -> T,
transform: suspend (previousValue: T, newValue: T) -> R,
-): Flow<R> =
- onStart { emit(getInitialValue()) }.pairwiseBy(transform)
+): Flow<R> = onStart { emit(getInitialValue()) }.pairwiseBy(transform)
/**
* Returns a new [Flow] that produces the two most recent emissions from [this]. Note that the new
@@ -88,8 +89,8 @@
fun <T> Flow<T>.pairwise(): Flow<WithPrev<T>> = pairwiseBy(::WithPrev)
/**
- * Returns a new [Flow] that produces the two most recent emissions from [this]. [initialValue]
- * will be used as the "old" value for the first emission.
+ * Returns a new [Flow] that produces the two most recent emissions from [this]. [initialValue] will
+ * be used as the "old" value for the first emission.
*
* Useful for code that needs to compare the current value to the previous value.
*/
@@ -102,9 +103,9 @@
* Returns a new [Flow] that combines the [Set] changes between each emission from [this] using
* [transform].
*
- * If [emitFirstEvent] is `true`, then the first [Set] emitted from the upstream [Flow] will cause
- * a change event to be emitted that contains no removals, and all elements from that first [Set]
- * as additions.
+ * If [emitFirstEvent] is `true`, then the first [Set] emitted from the upstream [Flow] will cause a
+ * change event to be emitted that contains no removals, and all elements from that first [Set] as
+ * additions.
*
* If [emitFirstEvent] is `false`, then the first emission is ignored and no changes are emitted
* until a second [Set] has been emitted from the upstream [Flow].
@@ -112,22 +113,23 @@
fun <T, R> Flow<Set<T>>.setChangesBy(
transform: suspend (removed: Set<T>, added: Set<T>) -> R,
emitFirstEvent: Boolean = true,
-): Flow<R> = (if (emitFirstEvent) onStart { emit(emptySet()) } else this)
- .distinctUntilChanged()
- .pairwiseBy { old: Set<T>, new: Set<T> ->
- // If an element was present in the old set, but not the new one, then it was removed
- val removed = old - new
- // If an element is present in the new set, but on the old one, then it was added
- val added = new - old
- transform(removed, added)
- }
+): Flow<R> =
+ (if (emitFirstEvent) onStart { emit(emptySet()) } else this)
+ .distinctUntilChanged()
+ .pairwiseBy { old: Set<T>, new: Set<T> ->
+ // If an element was present in the old set, but not the new one, then it was removed
+ val removed = old - new
+ // If an element is present in the new set, but on the old one, then it was added
+ val added = new - old
+ transform(removed, added)
+ }
/**
* Returns a new [Flow] that produces the [Set] changes between each emission from [this].
*
- * If [emitFirstEvent] is `true`, then the first [Set] emitted from the upstream [Flow] will cause
- * a change event to be emitted that contains no removals, and all elements from that first [Set]
- * as additions.
+ * If [emitFirstEvent] is `true`, then the first [Set] emitted from the upstream [Flow] will cause a
+ * change event to be emitted that contains no removals, and all elements from that first [Set] as
+ * additions.
*
* If [emitFirstEvent] is `false`, then the first emission is ignored and no changes are emitted
* until a second [Set] has been emitted from the upstream [Flow].
@@ -153,14 +155,11 @@
coroutineScope {
val noVal = Any()
val sampledRef = AtomicReference(noVal)
- val job = launch(Dispatchers.Unconfined) {
- other.collect { sampledRef.set(it) }
- }
+ val job = launch(Dispatchers.Unconfined) { other.collect { sampledRef.set(it) } }
collect {
val sampled = sampledRef.get()
if (sampled != noVal) {
- @Suppress("UNCHECKED_CAST")
- emit(transform(it, sampled as B))
+ @Suppress("UNCHECKED_CAST") emit(transform(it, sampled as B))
}
}
job.cancel()
@@ -181,7 +180,6 @@
* latest value is emitted.
*
* Example:
- *
* ```kotlin
* flow {
* emit(1) // t=0ms
@@ -210,7 +208,6 @@
* during this period, only The latest value is emitted.
*
* Example:
- *
* ```kotlin
* flow {
* emit(1) // t=0ms
@@ -248,10 +245,11 @@
// We create delayJob to allow cancellation during the delay period
delayJob = launch {
delay(timeUntilNextEmit)
- sendJob = outerScope.launch(start = CoroutineStart.UNDISPATCHED) {
- send(it)
- previousEmitTimeMs = clock.elapsedRealtime()
- }
+ sendJob =
+ outerScope.launch(start = CoroutineStart.UNDISPATCHED) {
+ send(it)
+ previousEmitTimeMs = clock.elapsedRealtime()
+ }
}
} else {
send(it)
@@ -259,4 +257,15 @@
}
}
}
-}
\ No newline at end of file
+}
+
+/**
+ * Returns a [StateFlow] launched in the surrounding [CoroutineScope]. This [StateFlow] gets its
+ * value by invoking [getValue] whenever an event is emitted from [changedSignals]. It will also
+ * immediately invoke [getValue] to establish its initial value.
+ */
+inline fun <T> CoroutineScope.stateFlow(
+ changedSignals: Flow<Unit>,
+ crossinline getValue: () -> T,
+): StateFlow<T> =
+ changedSignals.map { getValue() }.stateIn(this, SharingStarted.Eagerly, getValue())
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/src/com/android/systemui/util/ui/AnimatedValue.kt b/packages/SystemUI/src/com/android/systemui/util/ui/AnimatedValue.kt
new file mode 100644
index 0000000..51d2afa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/ui/AnimatedValue.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.util.ui
+
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.firstOrNull
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.transformLatest
+
+/**
+ * A state comprised of a [value] of type [T] paired with a boolean indicating whether or not the
+ * [value] [isAnimating] in the UI.
+ */
+data class AnimatedValue<T>(
+ val value: T,
+ val isAnimating: Boolean,
+)
+
+/**
+ * An event comprised of a [value] of type [T] paired with a [boolean][startAnimating] indicating
+ * whether or not this event should start an animation.
+ */
+data class AnimatableEvent<T>(
+ val value: T,
+ val startAnimating: Boolean,
+)
+
+/**
+ * Returns a [Flow] that tracks an [AnimatedValue] state. The input [Flow] is used to update the
+ * [AnimatedValue.value], as well as [AnimatedValue.isAnimating] if the event's
+ * [AnimatableEvent.startAnimating] value is `true`. When [completionEvents] emits a value, the
+ * [AnimatedValue.isAnimating] will flip to `false`.
+ */
+fun <T> Flow<AnimatableEvent<T>>.toAnimatedValueFlow(
+ completionEvents: Flow<Any?>,
+): Flow<AnimatedValue<T>> = transformLatest { (value, startAnimating) ->
+ emit(AnimatedValue(value, isAnimating = startAnimating))
+ if (startAnimating) {
+ // Wait for a completion now that we've started animating
+ completionEvents
+ .map { Unit } // replace the event so that it's never `null`
+ .firstOrNull() // `null` indicates an empty flow
+ // emit the new state if the flow was not empty.
+ ?.run { emit(AnimatedValue(value, isAnimating = false)) }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index ea4d31b..d65a69c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -77,6 +77,8 @@
import com.android.systemui.util.RingerModeTracker;
import com.android.systemui.util.concurrency.ThreadFactory;
+import dalvik.annotation.optimization.NeverCompile;
+
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.List;
@@ -286,6 +288,7 @@
return new MediaSessions(context, looper, callbacks);
}
+ @NeverCompile
public void dump(PrintWriter pw, String[] args) {
pw.println(VolumeDialogControllerImpl.class.getSimpleName() + " state:");
pw.print(" mVolumePolicy: "); pw.println(mVolumePolicy);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 727d649..929b91c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -135,6 +135,9 @@
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.util.AlphaTintDrawableWrapper;
import com.android.systemui.util.RoundedCornerProgressDrawable;
+import com.android.systemui.util.settings.SecureSettings;
+
+import dagger.Lazy;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -304,6 +307,8 @@
private @DevicePostureController.DevicePostureInt int mDevicePosture;
private int mOrientation;
private final FeatureFlags mFeatureFlags;
+ private final Lazy<SecureSettings> mSecureSettings;
+ private int mDialogTimeoutMillis;
public VolumeDialogImpl(
Context context,
@@ -320,7 +325,8 @@
DevicePostureController devicePostureController,
Looper looper,
DumpManager dumpManager,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags,
+ Lazy<SecureSettings> secureSettings) {
mFeatureFlags = featureFlags;
mContext =
new ContextThemeWrapper(context, R.style.volume_dialog_theme);
@@ -351,6 +357,8 @@
mUseBackgroundBlur =
mContext.getResources().getBoolean(R.bool.config_volumeDialogUseBackgroundBlur);
mInteractionJankMonitor = interactionJankMonitor;
+ mSecureSettings = secureSettings;
+ mDialogTimeoutMillis = DIALOG_TIMEOUT_MILLIS;
dumpManager.registerDumpable("VolumeDialogImpl", this);
@@ -515,6 +523,8 @@
mDialog.setContentView(R.layout.volume_dialog);
mDialogView = mDialog.findViewById(R.id.volume_dialog);
mDialogView.setAlpha(0);
+ mDialogTimeoutMillis = mSecureSettings.get().getInt(
+ Settings.Secure.VOLUME_DIALOG_DISMISS_TIMEOUT, DIALOG_TIMEOUT_MILLIS);
mDialog.setCanceledOnTouchOutside(true);
mDialog.setOnShowListener(dialog -> {
mDialogView.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
@@ -527,7 +537,7 @@
.alpha(1)
.translationX(0)
.setDuration(mDialogShowAnimationDurationMs)
- .setListener(getJankListener(getDialogView(), TYPE_SHOW, DIALOG_TIMEOUT_MILLIS))
+ .setListener(getJankListener(getDialogView(), TYPE_SHOW, mDialogTimeoutMillis))
.setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator())
.withEndAction(() -> {
if (!Prefs.getBoolean(mContext, Prefs.Key.TOUCHED_RINGER_TOGGLE, false)) {
@@ -1514,7 +1524,7 @@
AccessibilityManager.FLAG_CONTENT_TEXT
| AccessibilityManager.FLAG_CONTENT_CONTROLS);
}
- return mAccessibilityMgr.getRecommendedTimeoutMillis(DIALOG_TIMEOUT_MILLIS,
+ return mAccessibilityMgr.getRecommendedTimeoutMillis(mDialogTimeoutMillis,
AccessibilityManager.FLAG_CONTENT_CONTROLS);
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
index cc9f3e1..e3b3c21 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
@@ -31,6 +31,7 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.volume.CsdWarningDialog;
import com.android.systemui.volume.VolumeComponent;
import com.android.systemui.volume.VolumeDialogComponent;
@@ -38,6 +39,7 @@
import com.android.systemui.volume.VolumePanelFactory;
import dagger.Binds;
+import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
@@ -63,7 +65,8 @@
CsdWarningDialog.Factory csdFactory,
DevicePostureController devicePostureController,
DumpManager dumpManager,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags,
+ Lazy<SecureSettings> secureSettings) {
VolumeDialogImpl impl = new VolumeDialogImpl(
context,
volumeDialogController,
@@ -79,7 +82,8 @@
devicePostureController,
Looper.getMainLooper(),
dumpManager,
- featureFlags);
+ featureFlags,
+ secureSettings);
impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
impl.setAutomute(true);
impl.setSilentMode(false);
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
index 750b6f9..2132904 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
@@ -40,12 +40,13 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.settingslib.Utils;
-import com.android.systemui.res.R;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -68,6 +69,7 @@
private final Executor mExecutor;
private final Handler mHandler;
private final FalsingManager mFalsingManager;
+ private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
private FalsingCollector mFalsingCollector;
private final UserTracker mUserTracker;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -91,7 +93,8 @@
UserTracker userTracker,
KeyguardUpdateMonitor keyguardUpdateMonitor,
StatusBarKeyguardViewManager keyguardViewManager,
- UiEventLogger uiEventLogger) {
+ UiEventLogger uiEventLogger,
+ KeyguardFaceAuthInteractor keyguardFaceAuthInteractor) {
mKeyguardStateController = keyguardStateController;
mKeyguardDismissUtil = keyguardDismissUtil;
mActivityStarter = activityStarter;
@@ -103,6 +106,7 @@
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mKeyguardViewManager = keyguardViewManager;
mUiEventLogger = uiEventLogger;
+ mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
}
@Override
@@ -209,6 +213,7 @@
true,
Utils.getColorAttrDefaultColor(
this, com.android.internal.R.attr.colorAccentPrimary));
+ mKeyguardFaceAuthInteractor.onWalletLaunched();
mKeyguardViewManager.requestFace(true);
}
diff --git a/packages/SystemUI/tests/src/com/android/TestMocksModule.kt b/packages/SystemUI/tests/src/com/android/TestMocksModule.kt
index 8990583..167e341 100644
--- a/packages/SystemUI/tests/src/com/android/TestMocksModule.kt
+++ b/packages/SystemUI/tests/src/com/android/TestMocksModule.kt
@@ -18,61 +18,94 @@
import android.app.ActivityManager
import android.app.admin.DevicePolicyManager
import android.os.UserManager
+import android.util.DisplayMetrics
+import com.android.internal.logging.MetricsLogger
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.GuestResumeSessionReceiver
import com.android.systemui.demomode.DemoModeController
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.ScreenLifecycle
+import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.dagger.BroadcastDispatcherLog
import com.android.systemui.log.dagger.SceneFrameworkLog
+import com.android.systemui.media.controls.ui.MediaHierarchyManager
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.DarkIconDispatcher
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.NotificationListener
+import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationMediaManager
+import com.android.systemui.statusbar.NotificationShadeDepthController
+import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
+import com.android.systemui.statusbar.notification.stack.AmbientState
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.phone.LSShadeTransitionLogger
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
-import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.phone.ScrimController
import com.android.systemui.statusbar.policy.DeviceProvisionedController
-import com.android.systemui.statusbar.policy.SplitShadeStateController
import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.util.mockito.mock
import com.android.wm.shell.bubbles.Bubbles
+import dagger.Binds
import dagger.Module
import dagger.Provides
import java.util.Optional
-@Module
+@Module(includes = [TestMocksModule.Bindings::class])
data class TestMocksModule(
@get:Provides val activityStarter: ActivityStarter = mock(),
+ @get:Provides val ambientState: AmbientState = mock(),
@get:Provides val bubbles: Optional<Bubbles> = Optional.of(mock()),
- @get:Provides val configurationController: ConfigurationController = mock(),
@get:Provides val darkIconDispatcher: DarkIconDispatcher = mock(),
@get:Provides val demoModeController: DemoModeController = mock(),
@get:Provides val deviceProvisionedController: DeviceProvisionedController = mock(),
@get:Provides val dozeParameters: DozeParameters = mock(),
+ @get:Provides val dumpManager: DumpManager = mock(),
@get:Provides val guestResumeSessionReceiver: GuestResumeSessionReceiver = mock(),
@get:Provides val keyguardBypassController: KeyguardBypassController = mock(),
@get:Provides val keyguardSecurityModel: KeyguardSecurityModel = mock(),
@get:Provides val keyguardUpdateMonitor: KeyguardUpdateMonitor = mock(),
- @get:Provides val notifListener: NotificationListener = mock(),
- @get:Provides val notifMediaManager: NotificationMediaManager = mock(),
- @get:Provides val screenOffAnimController: ScreenOffAnimationController = mock(),
- @get:Provides val splitShadeStateController: SplitShadeStateController = mock(),
- @get:Provides val statusBarStateController: StatusBarStateController = mock(),
+ @get:Provides val mediaHierarchyManager: MediaHierarchyManager = mock(),
+ @get:Provides val notificationListener: NotificationListener = mock(),
+ @get:Provides val notificationLockscreenUserManager: NotificationLockscreenUserManager = mock(),
+ @get:Provides val notificationMediaManager: NotificationMediaManager = mock(),
+ @get:Provides val notificationShadeDepthController: NotificationShadeDepthController = mock(),
+ @get:Provides
+ val notificationStackScrollLayoutController: NotificationStackScrollLayoutController = mock(),
+ @get:Provides val notificationStackSizeCalculator: NotificationStackSizeCalculator = mock(),
+ @get:Provides val notificationWakeUpCoordinator: NotificationWakeUpCoordinator = mock(),
+ @get:Provides val screenLifecycle: ScreenLifecycle = mock(),
+ @get:Provides val screenOffAnimationController: ScreenOffAnimationController = mock(),
+ @get:Provides val scrimController: ScrimController = mock(),
+ @get:Provides val statusBarStateController: SysuiStatusBarStateController = mock(),
@get:Provides val statusBarWindowController: StatusBarWindowController = mock(),
- @get:Provides val wakeUpCoordinator: NotificationWakeUpCoordinator = mock(),
+ @get:Provides val wakefulnessLifecycle: WakefulnessLifecycle = mock(),
// log buffers
@get:[Provides BroadcastDispatcherLog]
val broadcastDispatcherLogger: LogBuffer = mock(),
@get:[Provides SceneFrameworkLog]
val sceneLogger: LogBuffer = mock(),
+ @get:Provides val lsShadeTransitionLogger: LSShadeTransitionLogger = mock(),
// framework mocks
@get:Provides val activityManager: ActivityManager = mock(),
@get:Provides val devicePolicyManager: DevicePolicyManager = mock(),
+ @get:Provides val displayMetrics: DisplayMetrics = mock(),
+ @get:Provides val metricsLogger: MetricsLogger = mock(),
@get:Provides val userManager: UserManager = mock(),
-)
+) {
+ @Module
+ interface Bindings {
+ @Binds
+ fun bindStatusBarStateController(
+ sysuiStatusBarStateController: SysuiStatusBarStateController,
+ ): StatusBarStateController
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
index f943acd..d8a2c5f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
@@ -25,7 +25,6 @@
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.flags.FakeFeatureFlags
@@ -47,7 +46,6 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class KeyguardPasswordViewControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index e090093..dc1618d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -25,7 +25,6 @@
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorFake
@@ -53,7 +52,6 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class KeyguardPatternViewControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
index 8322b37..4a24e4a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
@@ -31,14 +31,13 @@
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.res.R;
-import com.android.systemui.RoboPilotTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.SingleTapClassifier;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.res.R;
import org.junit.Before;
import org.junit.Test;
@@ -47,7 +46,6 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4.class)
@RunWithLooper
public class KeyguardPinBasedInputViewControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
index 2f08804..9df4dd4 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
@@ -26,7 +26,6 @@
import com.android.internal.widget.LockPatternUtils
import com.android.keyguard.KeyguardSecurityModel.SecurityMode
import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorFake
@@ -53,7 +52,6 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class KeyguardPinViewControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
index f3a1b68..5102957 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
@@ -69,7 +69,8 @@
when(mMockKeyguardSliceView.getContext()).thenReturn(mContext);
when(mMockKeyguardStatusView.getContext()).thenReturn(mContext);
when(mMockKeyguardStatusView.findViewById(R.id.clock)).thenReturn(mMockKeyguardStatusView);
- when(mKeyguardStatusViewComponentFactory.build(any(KeyguardStatusView.class)))
+ when(mKeyguardStatusViewComponentFactory.build(any(KeyguardStatusView.class),
+ any(Display.class)))
.thenReturn(mKeyguardStatusViewComponent);
when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController())
.thenReturn(mKeyguardClockSwitchController);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index d54843d3..20d4eb9 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -36,7 +36,6 @@
import com.android.internal.widget.LockPatternUtils
import com.android.keyguard.KeyguardSecurityContainer.UserSwitcherViewMode.UserSwitcherCallback
import com.android.keyguard.KeyguardSecurityModel.SecurityMode
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.FaceAuthAccessibilityDelegate
import com.android.systemui.biometrics.SideFpsController
@@ -100,7 +99,6 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
@RunWithLooper
class KeyguardSecurityContainerControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index aad11d9..156e068 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -56,11 +56,10 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.systemui.res.R;
-import com.android.systemui.RoboPilotTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingA11yDelegate;
import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.user.data.source.UserRecord;
import com.android.systemui.util.settings.GlobalSettings;
@@ -76,7 +75,6 @@
import java.util.ArrayList;
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper()
public class KeyguardSecurityContainerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
index b02b1f9..7bb6ef1 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
@@ -35,10 +35,9 @@
import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.res.R;
-import com.android.systemui.RoboPilotTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.res.R;
import org.junit.Before;
import org.junit.Rule;
@@ -50,7 +49,6 @@
import org.mockito.junit.MockitoRule;
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper()
public class KeyguardSecurityViewFlipperControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
index d0bb5a9..4290b8b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
@@ -25,7 +25,6 @@
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.flags.FakeFeatureFlags
@@ -44,7 +43,6 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class KeyguardSimPinViewControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
index 59cd26c..31ee641 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
@@ -25,7 +25,6 @@
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.flags.FakeFeatureFlags
@@ -40,7 +39,6 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class KeyguardSimPukViewControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
index 989164e..3b8e02f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
@@ -20,7 +20,6 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.animation.LayoutTransition;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
@@ -69,7 +68,6 @@
@Mock protected KeyguardClockSwitch mKeyguardClockSwitch;
@Mock protected FrameLayout mMediaHostContainer;
- @Mock protected LayoutTransition mMediaLayoutTransition;
@Before
public void setup() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
index e4e2b0a..948942f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -16,22 +16,27 @@
package com.android.keyguard;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.animation.LayoutTransition;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.View;
-import com.android.systemui.res.R;
+import com.android.app.animation.Interpolators;
+import com.android.systemui.animation.ViewHierarchyAnimator;
import com.android.systemui.plugins.ClockConfig;
import com.android.systemui.plugins.ClockController;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
@@ -39,6 +44,8 @@
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import java.lang.reflect.Field;
+
@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@RunWith(AndroidTestingRunner.class)
@@ -81,7 +88,7 @@
public void updatePosition_primaryClockAnimation() {
ClockController mockClock = mock(ClockController.class);
when(mKeyguardClockSwitchController.getClock()).thenReturn(mockClock);
- when(mockClock.getConfig()).thenReturn(new ClockConfig("MOCK", false, true));
+ when(mockClock.getConfig()).thenReturn(new ClockConfig("MOCK", "", "", false, true));
mController.updatePosition(10, 15, 20f, true);
@@ -96,7 +103,7 @@
public void updatePosition_alternateClockAnimation() {
ClockController mockClock = mock(ClockController.class);
when(mKeyguardClockSwitchController.getClock()).thenReturn(mockClock);
- when(mockClock.getConfig()).thenReturn(new ClockConfig("MOCK", true, true));
+ when(mockClock.getConfig()).thenReturn(new ClockConfig("MOCK", "", "", true, true));
mController.updatePosition(10, 15, 20f, true);
@@ -142,19 +149,7 @@
}
@Test
- public void onInit_addsOnLayoutChangeListenerToMediaHostContainer() {
- when(mKeyguardStatusView.findViewById(R.id.status_view_media_container)).thenReturn(
- mMediaHostContainer);
-
- mController.onInit();
-
- ArgumentCaptor<View.OnLayoutChangeListener> captor =
- ArgumentCaptor.forClass(View.OnLayoutChangeListener.class);
- verify(mMediaHostContainer).addOnLayoutChangeListener(captor.capture());
- }
-
- @Test
- public void clockSwitchHeightChanged_mediaChangingLayoutTransitionEnabled() {
+ public void clockSwitchHeightChanged_animatesMediaHostContainer() {
when(mKeyguardStatusView.findViewById(R.id.status_view_media_container)).thenReturn(
mMediaHostContainer);
@@ -167,6 +162,10 @@
// Above here is the same as `onInit_addsOnLayoutChangeListenerToClockSwitch`.
// Below here is the actual test.
+ ViewHierarchyAnimator.Companion animator = ViewHierarchyAnimator.Companion;
+ ViewHierarchyAnimator.Companion spiedAnimator = spy(animator);
+ setCompanion(spiedAnimator);
+
View.OnLayoutChangeListener listener = captor.getValue();
mController.setSplitShadeEnabled(true);
@@ -174,17 +173,20 @@
when(mKeyguardUpdateMonitor.isKeyguardVisible()).thenReturn(true);
when(mMediaHostContainer.getVisibility()).thenReturn(View.VISIBLE);
when(mMediaHostContainer.getHeight()).thenReturn(200);
- when(mMediaHostContainer.getLayoutTransition()).thenReturn(mMediaLayoutTransition);
when(mKeyguardClockSwitch.getHeight()).thenReturn(0);
listener.onLayoutChange(mKeyguardClockSwitch, /* left= */ 0, /* top= */ 0, /* right= */
0, /* bottom= */ 0, /* oldLeft= */ 0, /* oldTop= */ 0, /* oldRight= */
0, /* oldBottom = */ 200);
- verify(mMediaLayoutTransition).enableTransitionType(LayoutTransition.CHANGING);
+ verify(spiedAnimator).animateNextUpdate(mMediaHostContainer,
+ Interpolators.STANDARD, /* duration= */ 500L, /* animateChildren= */ false);
+
+ // Resets ViewHierarchyAnimator.Companion to its original value
+ setCompanion(animator);
}
@Test
- public void clockSwitchHeightNotChanged_mediaChangingLayoutTransitionNotEnabled() {
+ public void clockSwitchHeightNotChanged_doesNotAnimateMediaOutputContainer() {
when(mKeyguardStatusView.findViewById(R.id.status_view_media_container)).thenReturn(
mMediaHostContainer);
@@ -197,6 +199,10 @@
// Above here is the same as `onInit_addsOnLayoutChangeListenerToClockSwitch`.
// Below here is the actual test.
+ ViewHierarchyAnimator.Companion animator = ViewHierarchyAnimator.Companion;
+ ViewHierarchyAnimator.Companion spiedAnimator = spy(animator);
+ setCompanion(spiedAnimator);
+
View.OnLayoutChangeListener listener = captor.getValue();
mController.setSplitShadeEnabled(true);
@@ -204,36 +210,24 @@
when(mKeyguardUpdateMonitor.isKeyguardVisible()).thenReturn(true);
when(mMediaHostContainer.getVisibility()).thenReturn(View.VISIBLE);
when(mMediaHostContainer.getHeight()).thenReturn(200);
- when(mMediaHostContainer.getLayoutTransition()).thenReturn(mMediaLayoutTransition);
when(mKeyguardClockSwitch.getHeight()).thenReturn(200);
listener.onLayoutChange(mKeyguardClockSwitch, /* left= */ 0, /* top= */ 0, /* right= */
0, /* bottom= */ 0, /* oldLeft= */ 0, /* oldTop= */ 0, /* oldRight= */
0, /* oldBottom = */ 200);
- verify(mMediaLayoutTransition, never()).enableTransitionType(LayoutTransition.CHANGING);
+ verify(spiedAnimator, never()).animateNextUpdate(any(), any(), anyLong(), anyBoolean());
+
+ // Resets ViewHierarchyAnimator.Companion to its original value
+ setCompanion(animator);
}
- @Test
- public void onMediaHostContainerLayout_disablesChangingLayoutTransition() {
- when(mKeyguardStatusView.findViewById(R.id.status_view_media_container)).thenReturn(
- mMediaHostContainer);
-
- mController.onInit();
-
- ArgumentCaptor<View.OnLayoutChangeListener> captor =
- ArgumentCaptor.forClass(View.OnLayoutChangeListener.class);
- verify(mMediaHostContainer).addOnLayoutChangeListener(captor.capture());
-
- // Above here is the same as `onInit_addsOnLayoutChangeListenerToMediaHostContainer`.
- // Below here is the actual test.
-
- View.OnLayoutChangeListener listener = captor.getValue();
-
- when(mMediaHostContainer.getLayoutTransition()).thenReturn(mMediaLayoutTransition);
-
- when(mMediaLayoutTransition.isTransitionTypeEnabled(LayoutTransition.CHANGING)).thenReturn(
- true);
- listener.onLayoutChange(mMediaHostContainer, 1, 2, 3, 4, 1, 2, 3, 4);
- verify(mMediaLayoutTransition).disableTransitionType(LayoutTransition.CHANGING);
+ private void setCompanion(ViewHierarchyAnimator.Companion companion) {
+ try {
+ Field field = ViewHierarchyAnimator.class.getDeclaredField("Companion");
+ field.setAccessible(true);
+ field.set(null, companion);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt
index 58d372c..86439e5 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt
@@ -1,6 +1,5 @@
package com.android.keyguard
-import android.animation.LayoutTransition
import android.test.suitebuilder.annotation.SmallTest
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
@@ -36,20 +35,6 @@
}
@Test
- fun mediaViewHasLayoutTransitionInDisabledState() {
- val layoutTransition = (mediaView as ViewGroup).layoutTransition
- assertThat(layoutTransition).isNotNull()
- assertThat(layoutTransition.isTransitionTypeEnabled(LayoutTransition.CHANGE_APPEARING))
- .isFalse()
- assertThat(layoutTransition.isTransitionTypeEnabled(LayoutTransition.CHANGE_DISAPPEARING))
- .isFalse()
- assertThat(layoutTransition.isTransitionTypeEnabled(LayoutTransition.APPEARING)).isFalse()
- assertThat(layoutTransition.isTransitionTypeEnabled(LayoutTransition.DISAPPEARING))
- .isFalse()
- assertThat(layoutTransition.isTransitionTypeEnabled(LayoutTransition.CHANGING)).isFalse()
- }
-
- @Test
fun setChildrenTranslationYExcludingMediaView_mediaViewIsNotTranslated() {
val translationY = 1234f
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
index 21a2822..d61ca69 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
@@ -39,10 +39,10 @@
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.AuthRippleController;
+import com.android.systemui.bouncer.domain.interactor.BouncerInteractor;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.doze.util.BurnInHelperKt;
import com.android.systemui.dump.DumpManager;
@@ -51,6 +51,8 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
+import com.android.systemui.scene.SceneTestUtils;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -75,6 +77,8 @@
protected MockitoSession mStaticMockSession;
+ protected final SceneTestUtils mSceneTestUtils = new SceneTestUtils(this);
+ protected @Mock BouncerInteractor mBouncerInteractor;
protected @Mock LockIconView mLockIconView;
protected @Mock AnimatedStateListDrawable mIconDrawable;
protected @Mock Context mContext;
@@ -93,6 +97,7 @@
protected @Mock AuthRippleController mAuthRippleController;
protected FakeExecutor mDelayableExecutor = new FakeExecutor(new FakeSystemClock());
protected FakeFeatureFlags mFeatureFlags;
+
protected @Mock PrimaryBouncerInteractor mPrimaryBouncerInteractor;
protected LockIconViewController mUnderTest;
@@ -148,6 +153,7 @@
mFeatureFlags.set(MIGRATE_LOCK_ICON, false);
mFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false);
mFeatureFlags.set(LOCKSCREEN_ENABLE_LANDSCAPE, false);
+
mUnderTest = new LockIconViewController(
mStatusBarStateController,
mKeyguardUpdateMonitor,
@@ -168,7 +174,9 @@
KeyguardInteractorFactory.create(mFeatureFlags).getKeyguardInteractor(),
mFeatureFlags,
mPrimaryBouncerInteractor,
- mContext
+ mContext,
+ () -> mBouncerInteractor,
+ mSceneTestUtils.getSceneContainerFlags()
);
}
@@ -227,8 +235,8 @@
setupLockIconViewMocks();
}
- protected void init(boolean useMigrationFlag) {
- mFeatureFlags.set(DOZING_MIGRATION_1, useMigrationFlag);
+ protected void init(boolean useDozeMigrationFlag) {
+ mFeatureFlags.set(DOZING_MIGRATION_1, useDozeMigrationFlag);
mUnderTest.setLockIconView(mLockIconView);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
index 7b920939..4bacc3d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
@@ -26,6 +26,7 @@
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -404,6 +405,49 @@
// THEN uses perform haptic feedback
verify(mVibrator).performHapticFeedback(any(), eq(UdfpsController.LONG_PRESS));
+ }
+ @Test
+ public void longPress_showBouncer_sceneContainerNotEnabled() {
+ init(/* useMigrationFlag= */ false);
+ mSceneTestUtils.getSceneContainerFlags().setEnabled(false);
+ mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
+ when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(false);
+
+ // WHEN longPress
+ mUnderTest.onLongPress();
+
+ // THEN show primary bouncer via keyguard view controller, not scene container
+ verify(mKeyguardViewController).showPrimaryBouncer(anyBoolean());
+ verify(mBouncerInteractor, never()).showOrUnlockDevice(any());
+ }
+
+ @Test
+ public void longPress_showBouncer() {
+ init(/* useMigrationFlag= */ false);
+ mSceneTestUtils.getSceneContainerFlags().setEnabled(true);
+ mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
+ when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(false);
+
+ // WHEN longPress
+ mUnderTest.onLongPress();
+
+ // THEN show primary bouncer
+ verify(mKeyguardViewController, never()).showPrimaryBouncer(anyBoolean());
+ verify(mBouncerInteractor).showOrUnlockDevice(any());
+ }
+
+ @Test
+ public void longPress_falsingTriggered_doesNotShowBouncer() {
+ init(/* useMigrationFlag= */ false);
+ mSceneTestUtils.getSceneContainerFlags().setEnabled(true);
+ mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
+ when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(true);
+
+ // WHEN longPress
+ mUnderTest.onLongPress();
+
+ // THEN don't show primary bouncer
+ verify(mBouncerInteractor, never()).showOrUnlockDevice(any());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
index 59c7e76..8faf715 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
@@ -166,6 +166,9 @@
waitForIdleSync()
verify(controller).onLaunchAnimationCancelled()
verify(controller, never()).onLaunchAnimationStart(anyBoolean())
+ verify(listener).onLaunchAnimationCancelled()
+ verify(listener, never()).onLaunchAnimationStart()
+ assertNull(runner.delegate)
}
@Test
@@ -176,6 +179,9 @@
waitForIdleSync()
verify(controller).onLaunchAnimationCancelled()
verify(controller, never()).onLaunchAnimationStart(anyBoolean())
+ verify(listener).onLaunchAnimationCancelled()
+ verify(listener, never()).onLaunchAnimationStart()
+ assertNull(runner.delegate)
}
@Test
@@ -194,6 +200,15 @@
}
}
+ @Test
+ fun disposeRunner_delegateDereferenced() {
+ val runner = activityLaunchAnimator.createRunner(controller)
+ assertNotNull(runner.delegate)
+ runner.dispose()
+ waitForIdleSync()
+ assertNull(runner.delegate)
+ }
+
private fun fakeWindow(): RemoteAnimationTarget {
val bounds = Rect(10 /* left */, 20 /* top */, 30 /* right */, 40 /* bottom */)
val taskInfo = ActivityManager.RunningTaskInfo()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt
index a7e7dd0..2b51ac5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt
@@ -19,6 +19,7 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import androidx.core.animation.doOnEnd
+import androidx.test.filters.FlakyTest
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.doOnEnd
@@ -30,6 +31,7 @@
@RunWith(AndroidTestingRunner::class)
@SmallTest
@RunWithLooper
+@FlakyTest(bugId = 302149604)
class AnimatorTestRuleOrderTest : SysuiTestCase() {
@get:Rule val animatorTestRule = AnimatorTestRule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index 2bc0171..885abcb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -17,12 +17,15 @@
package com.android.systemui.biometrics;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+
import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotSame;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -79,7 +82,6 @@
import com.android.internal.R;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.widget.LockPatternUtils;
-import com.android.systemui.RoboPilotTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.domain.interactor.LogContextInteractor;
import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor;
@@ -114,7 +116,6 @@
@RunWith(AndroidJUnit4.class)
@RunWithLooper
@SmallTest
-@RoboPilotTest
public class AuthControllerTest extends SysuiTestCase {
private static final long REQUEST_ID = 22;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
index 7365460..d68a313 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
@@ -16,36 +16,18 @@
package com.android.systemui.biometrics
-import android.app.ActivityManager
-import android.os.UserManager
import androidx.test.filters.SmallTest
-import com.android.internal.logging.UiEventLogger
-import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.SysUITestModule
import com.android.systemui.SysuiTestCase
-import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
-import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FakeFeatureFlagsClassicModule
import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
-import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.scene.SceneTestUtils
-import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
import com.android.systemui.shade.data.repository.FakeShadeRepository
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository
-import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository
-import com.android.systemui.statusbar.policy.DeviceProvisionedController
-import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
-import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
-import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
-import com.android.systemui.user.data.repository.FakeUserRepository
-import com.android.systemui.user.domain.interactor.GuestUserInteractor
-import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode
-import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
-import com.android.systemui.user.domain.interactor.UserInteractor
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.user.domain.UserDomainLayerModule
+import dagger.BindsInstance
+import dagger.Component
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -58,91 +40,27 @@
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
class AuthDialogPanelInteractionDetectorTest : SysuiTestCase() {
- private val utils = SceneTestUtils(this)
- private val testScope = utils.testScope
- private val testDispatcher = utils.testDispatcher
- private val disableFlagsRepository = FakeDisableFlagsRepository()
- private val featureFlags = FakeFeatureFlags()
- private val keyguardRepository = FakeKeyguardRepository()
- private val shadeRepository = FakeShadeRepository()
- private val sceneContainerFlags = FakeSceneContainerFlags()
- private val sceneInteractor = utils.sceneInteractor()
- private val userSetupRepository = FakeUserSetupRepository()
- private val userRepository = FakeUserRepository()
- private val configurationRepository = FakeConfigurationRepository()
- private val sharedNotificationContainerInteractor =
- SharedNotificationContainerInteractor(
- configurationRepository,
- mContext,
- ResourcesSplitShadeStateController()
- )
- private lateinit var detector: AuthDialogPanelInteractionDetector
- private lateinit var shadeInteractor: ShadeInteractor
- private lateinit var userInteractor: UserInteractor
+ private val testComponent: TestComponent =
+ DaggerAuthDialogPanelInteractionDetectorTest_TestComponent.factory()
+ .create(
+ test = this,
+ featureFlags =
+ FakeFeatureFlagsClassicModule {
+ set(Flags.FACE_AUTH_REFACTOR, false)
+ set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+ },
+ )
@Mock private lateinit var action: Runnable
- @Mock private lateinit var activityManager: ActivityManager
- @Mock private lateinit var activityStarter: ActivityStarter
- @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
- @Mock private lateinit var guestInteractor: GuestUserInteractor
- @Mock private lateinit var headlessSystemUserMode: HeadlessSystemUserMode
- @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
- @Mock private lateinit var manager: UserManager
- @Mock private lateinit var uiEventLogger: UiEventLogger
+
+ private val testScope = testComponent.testScope
+ private val shadeRepository = testComponent.shadeRepository
+ private val detector = testComponent.detector
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
-
- featureFlags.set(Flags.FACE_AUTH_REFACTOR, false)
- featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
-
- val refreshUsersScheduler =
- RefreshUsersScheduler(
- applicationScope = testScope.backgroundScope,
- mainDispatcher = testDispatcher,
- repository = userRepository,
- )
- userInteractor =
- UserInteractor(
- applicationContext = context,
- repository = userRepository,
- activityStarter = activityStarter,
- keyguardInteractor =
- KeyguardInteractorFactory.create(featureFlags = featureFlags)
- .keyguardInteractor,
- featureFlags = featureFlags,
- manager = manager,
- headlessSystemUserMode = headlessSystemUserMode,
- applicationScope = testScope.backgroundScope,
- telephonyInteractor =
- TelephonyInteractor(
- repository = FakeTelephonyRepository(),
- ),
- broadcastDispatcher = fakeBroadcastDispatcher,
- keyguardUpdateMonitor = keyguardUpdateMonitor,
- backgroundDispatcher = testDispatcher,
- activityManager = activityManager,
- refreshUsersScheduler = refreshUsersScheduler,
- guestUserInteractor = guestInteractor,
- uiEventLogger = uiEventLogger,
- userRestrictionChecker = mock(),
- )
- shadeInteractor =
- ShadeInteractor(
- testScope.backgroundScope,
- disableFlagsRepository,
- sceneContainerFlags,
- { sceneInteractor },
- keyguardRepository,
- userSetupRepository,
- deviceProvisionedController,
- userInteractor,
- sharedNotificationContainerInteractor,
- shadeRepository,
- )
- detector = AuthDialogPanelInteractionDetector(testScope, { shadeInteractor })
}
@Test
@@ -215,4 +133,27 @@
// Clean up job
detector.disable()
}
+
+ @SysUISingleton
+ @Component(
+ modules =
+ [
+ SysUITestModule::class,
+ UserDomainLayerModule::class,
+ ]
+ )
+ interface TestComponent {
+
+ val detector: AuthDialogPanelInteractionDetector
+ val shadeRepository: FakeShadeRepository
+ val testScope: TestScope
+
+ @Component.Factory
+ interface Factory {
+ fun create(
+ @BindsInstance test: SysuiTestCase,
+ featureFlags: FakeFeatureFlagsClassicModule,
+ ): TestComponent
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java
index 8547fa3..714461b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDisplayListenerTest.java
@@ -38,7 +38,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.systemui.RoboPilotTest;
import com.android.systemui.SysuiTestCase;
import kotlin.Unit;
@@ -53,7 +52,6 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4.class)
@RunWithLooper(setAsMainLooper = true)
public class BiometricDisplayListenerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
index ab5d8bea5..39f0d57 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
@@ -19,7 +19,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.logging.BiometricMessageDeferralLogger
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import org.junit.Assert.assertEquals
@@ -34,7 +33,6 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class FaceHelpMessageDeferralTest : SysuiTestCase() {
val threshold = .75f
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
index 57cf834..ef06e0e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
@@ -53,7 +53,6 @@
import com.airbnb.lottie.LottieAnimationView
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.SysuiTestableContext
import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
@@ -98,7 +97,6 @@
private const val REAR_DISPLAY_MODE_DEVICE_STATE = 3
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class SideFpsControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt
index 469f65a..e2aa984 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt
@@ -19,7 +19,6 @@
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dump.DumpManager
@@ -35,7 +34,6 @@
import org.mockito.junit.MockitoJUnit
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class UdfpsBpViewControllerTest : SysuiTestCase() {
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 21e614f..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
@@ -38,7 +37,6 @@
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
@@ -46,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
@@ -83,7 +80,6 @@
@ExperimentalCoroutinesApi
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
@RunWithLooper(setAsMainLooper = true)
class UdfpsControllerOverlayTest : SysuiTestCase() {
@@ -143,7 +139,6 @@
) {
controllerOverlay = UdfpsControllerOverlay(
context,
- fingerprintManager,
inflater,
windowManager,
accessibilityManager,
@@ -157,7 +152,6 @@
keyguardStateController,
unlockedScreenOffAnimationController,
udfpsDisplayMode,
- secureSettings,
REQUEST_ID,
reason,
controllerCallback,
@@ -167,7 +161,6 @@
primaryBouncerInteractor,
alternateBouncerInteractor,
isDebuggable,
- udfpsUtils,
udfpsKeyguardAccessibilityDelegate,
udfpsKeyguardViewModels,
)
@@ -214,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)
}
}
@@ -232,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)
}
}
@@ -249,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)
}
}
@@ -266,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)
}
}
@@ -347,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(
@@ -365,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 ee3bd0d..dcb5398 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -20,12 +20,15 @@
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_UP;
+
import static com.android.internal.util.FunctionalUtils.ThrowingConsumer;
import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION;
import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
@@ -74,8 +77,6 @@
import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.util.LatencyTracker;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.res.R;
-import com.android.systemui.RoboPilotTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
@@ -87,13 +88,13 @@
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;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -103,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;
@@ -120,12 +120,10 @@
import java.util.ArrayList;
import java.util.List;
-import java.util.Optional;
import javax.inject.Provider;
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4.class)
@RunWithLooper(setAsMainLooper = true)
public class UdfpsControllerTest extends SysuiTestCase {
@@ -205,8 +203,6 @@
@Mock
private ActivityLaunchAnimator mActivityLaunchAnimator;
@Mock
- private AlternateUdfpsTouchProvider mAlternateTouchProvider;
- @Mock
private PrimaryBouncerInteractor mPrimaryBouncerInteractor;
@Mock
private SinglePointerTouchProcessor mSinglePointerTouchProcessor;
@@ -215,8 +211,6 @@
@Mock
private AlternateBouncerInteractor mAlternateBouncerInteractor;
@Mock
- private SecureSettings mSecureSettings;
- @Mock
private UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate;
@Mock
private Provider<UdfpsKeyguardViewModels> mUdfpsKeyguardViewModels;
@@ -238,7 +232,6 @@
private ScreenLifecycle.Observer mScreenObserver;
private FingerprintSensorPropertiesInternal mOpticalProps;
private FingerprintSensorPropertiesInternal mUltrasonicProps;
- private UdfpsUtils mUdfpsUtils;
@Mock
private InputManager mInputManager;
@Mock
@@ -249,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))
@@ -291,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(),
@@ -338,15 +318,12 @@
mSystemUIDialogManager,
mLatencyTracker,
mActivityLaunchAnimator,
- alternateTouchProvider,
mBiometricExecutor,
mPrimaryBouncerInteractor,
mSinglePointerTouchProcessor,
mSessionTracker,
mAlternateBouncerInteractor,
- mSecureSettings,
mInputManager,
- mUdfpsUtils,
mock(KeyguardFaceAuthInteractor.class),
mUdfpsKeyguardAccessibilityDelegate,
mUdfpsKeyguardViewModels
@@ -373,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();
@@ -407,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);
@@ -435,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();
@@ -592,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));
}
}
@@ -620,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,
@@ -653,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,
@@ -676,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,
@@ -699,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,
@@ -723,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
@@ -743,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) {
@@ -799,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);
@@ -833,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,
@@ -854,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
@@ -906,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);
@@ -917,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();
@@ -930,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();
@@ -1014,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);
@@ -1032,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();
@@ -1042,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) {
@@ -1121,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(
@@ -1158,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(
@@ -1186,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(
@@ -1220,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(
@@ -1307,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();
@@ -1401,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();
@@ -1452,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();
@@ -1490,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();
@@ -1530,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);
@@ -1563,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();
@@ -1577,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();
@@ -1688,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/UdfpsDialogMeasureAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java
index 280bfdf..cd9189b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java
@@ -27,7 +27,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.systemui.RoboPilotTest;
import com.android.systemui.SysuiTestCase;
import org.junit.Test;
@@ -38,7 +37,6 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
-@RoboPilotTest
public class UdfpsDialogMeasureAdapterTest extends SysuiTestCase {
@Test
public void testUdfpsBottomSpacerHeightForPortrait() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java
index 1afb223..5239966 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java
@@ -30,7 +30,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.systemui.RoboPilotTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.util.concurrency.FakeExecution;
@@ -41,7 +40,6 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4.class)
@RunWithLooper(setAsMainLooper = true)
public class UdfpsDisplayModeTest extends SysuiTestCase {
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 8508f45..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,27 +18,22 @@
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;
-import com.android.systemui.RoboPilotTest;
import com.android.systemui.statusbar.StatusBarState;
import org.junit.Test;
import org.junit.runner.RunWith;
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@@ -46,8 +41,7 @@
UdfpsKeyguardViewLegacyControllerBaseTest {
@Override
public UdfpsKeyguardViewControllerLegacy createUdfpsKeyguardViewController() {
- return createUdfpsKeyguardViewController(/* useModernBouncer */ false,
- /* useExpandedOverlay */ false);
+ return createUdfpsKeyguardViewController(/* useModernBouncer */ false);
}
@Test
@@ -218,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 17f435b..02ee53879 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
@@ -21,7 +21,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardSecurityModel
-import com.android.systemui.RoboPilotTest
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepositoryImpl
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
@@ -58,7 +57,6 @@
@RunWith(AndroidJUnit4::class)
@SmallTest
-@RoboPilotTest
@TestableLooper.RunWithLooper
@kotlinx.coroutines.ExperimentalCoroutinesApi
class UdfpsKeyguardViewLegacyControllerWithCoroutinesTest :
@@ -108,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/UdfpsShellTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsShellTest.kt
index 6d55254..8b374ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsShellTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsShellTest.kt
@@ -21,7 +21,6 @@
import android.view.MotionEvent
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.UdfpsController.UdfpsOverlayController
import com.android.systemui.statusbar.commandline.CommandRegistry
@@ -40,7 +39,6 @@
import org.mockito.junit.MockitoJUnit
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class UdfpsShellTest : SysuiTestCase() {
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 ebadfc7..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
@@ -26,7 +24,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.util.mockito.any
@@ -43,14 +40,12 @@
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
private const val SENSOR_RADIUS = 10
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class UdfpsViewTest : SysuiTestCase() {
@@ -82,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/biometrics/domain/interactor/SideFpsOverlayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractorTest.kt
deleted file mode 100644
index 712eef1..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsOverlayInteractorTest.kt
+++ /dev/null
@@ -1,108 +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.biometrics.domain.interactor
-
-import android.hardware.biometrics.SensorLocationInternal
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
-import com.android.systemui.biometrics.shared.model.FingerprintSensorType
-import com.android.systemui.biometrics.shared.model.SensorStrength
-import com.android.systemui.coroutines.collectLastValue
-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.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-import org.mockito.junit.MockitoJUnit
-
-@SmallTest
-@RunWith(JUnit4::class)
-class SideFpsOverlayInteractorTest : SysuiTestCase() {
-
- @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
- private lateinit var testScope: TestScope
-
- private val fingerprintRepository = FakeFingerprintPropertyRepository()
-
- private lateinit var interactor: SideFpsOverlayInteractor
-
- @Before
- fun setup() {
- testScope = TestScope(StandardTestDispatcher())
- interactor = SideFpsOverlayInteractorImpl(fingerprintRepository)
- }
-
- @Test
- fun testOverlayOffsetUpdates() =
- testScope.runTest {
- fingerprintRepository.setProperties(
- sensorId = 1,
- strength = SensorStrength.STRONG,
- sensorType = FingerprintSensorType.REAR,
- sensorLocations =
- mapOf(
- "" to
- SensorLocationInternal(
- "" /* displayId */,
- 540 /* sensorLocationX */,
- 1636 /* sensorLocationY */,
- 130 /* sensorRadius */
- ),
- "display_id_1" to
- SensorLocationInternal(
- "display_id_1" /* displayId */,
- 100 /* sensorLocationX */,
- 300 /* sensorLocationY */,
- 20 /* sensorRadius */
- )
- )
- )
-
- val displayId by collectLastValue(interactor.displayId)
- val offsets by collectLastValue(interactor.overlayOffsets)
-
- // Assert offsets of empty displayId.
- assertThat(displayId).isEqualTo("")
- assertThat(offsets?.displayId).isEqualTo("")
- assertThat(offsets?.sensorLocationX).isEqualTo(540)
- assertThat(offsets?.sensorLocationY).isEqualTo(1636)
- assertThat(offsets?.sensorRadius).isEqualTo(130)
-
- // Offsets should be updated correctly.
- interactor.onDisplayChanged("display_id_1")
- assertThat(displayId).isEqualTo("display_id_1")
- assertThat(offsets?.displayId).isEqualTo("display_id_1")
- assertThat(offsets?.sensorLocationX).isEqualTo(100)
- assertThat(offsets?.sensorLocationY).isEqualTo(300)
- assertThat(offsets?.sensorRadius).isEqualTo(20)
-
- // Should return default offset when the displayId is invalid.
- interactor.onDisplayChanged("invalid_display_id")
- assertThat(displayId).isEqualTo("invalid_display_id")
- assertThat(offsets?.displayId).isEqualTo(SensorLocationInternal.DEFAULT.displayId)
- assertThat(offsets?.sensorLocationX)
- .isEqualTo(SensorLocationInternal.DEFAULT.sensorLocationX)
- assertThat(offsets?.sensorLocationY)
- .isEqualTo(SensorLocationInternal.DEFAULT.sensorLocationY)
- assertThat(offsets?.sensorRadius).isEqualTo(SensorLocationInternal.DEFAULT.sensorRadius)
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt
new file mode 100644
index 0000000..99501c42
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt
@@ -0,0 +1,410 @@
+/*
+ * 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.biometrics.domain.interactor
+
+import android.graphics.Rect
+import android.hardware.biometrics.SensorLocationInternal
+import android.hardware.display.DisplayManagerGlobal
+import android.view.Display
+import android.view.DisplayInfo
+import android.view.WindowInsets
+import android.view.WindowManager
+import android.view.WindowMetrics
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.biometrics.shared.model.DisplayRotation
+import com.android.systemui.biometrics.shared.model.DisplayRotation.ROTATION_0
+import com.android.systemui.biometrics.shared.model.DisplayRotation.ROTATION_180
+import com.android.systemui.biometrics.shared.model.DisplayRotation.ROTATION_270
+import com.android.systemui.biometrics.shared.model.DisplayRotation.ROTATION_90
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
+import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.flags.Flags.REST_TO_UNLOCK
+import com.android.systemui.res.R
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.spy
+import org.mockito.junit.MockitoJUnit
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class SideFpsSensorInteractorTest : SysuiTestCase() {
+
+ @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
+ private lateinit var testScope: TestScope
+
+ private val fingerprintRepository = FakeFingerprintPropertyRepository()
+
+ private lateinit var underTest: SideFpsSensorInteractor
+
+ @Mock private lateinit var windowManager: WindowManager
+ @Mock private lateinit var displayStateInteractor: DisplayStateInteractor
+
+ private val contextDisplayInfo = DisplayInfo()
+ private val displayChangeEvent = MutableStateFlow(0)
+ private val currentRotation = MutableStateFlow(ROTATION_0)
+
+ @Before
+ fun setup() {
+ testScope = TestScope(StandardTestDispatcher())
+ mContext = spy(mContext)
+
+ val displayManager = mock(DisplayManagerGlobal::class.java)
+ val resources = mContext.resources
+ whenever(mContext.display)
+ .thenReturn(Display(displayManager, 1, contextDisplayInfo, resources))
+ whenever(displayStateInteractor.displayChanges).thenReturn(displayChangeEvent)
+ whenever(displayStateInteractor.currentRotation).thenReturn(currentRotation)
+
+ contextDisplayInfo.uniqueId = "current-display"
+
+ underTest =
+ SideFpsSensorInteractor(
+ mContext,
+ fingerprintRepository,
+ windowManager,
+ displayStateInteractor,
+ FakeFeatureFlagsClassic().apply { set(REST_TO_UNLOCK, true) }
+ )
+ }
+
+ @Test
+ fun testSfpsSensorAvailable() =
+ testScope.runTest {
+ val isAvailable by collectLastValue(underTest.isAvailable)
+
+ setupFingerprint(FingerprintSensorType.POWER_BUTTON)
+ assertThat(isAvailable).isTrue()
+
+ setupFingerprint(FingerprintSensorType.HOME_BUTTON)
+ assertThat(isAvailable).isFalse()
+
+ setupFingerprint(FingerprintSensorType.REAR)
+ assertThat(isAvailable).isFalse()
+
+ setupFingerprint(FingerprintSensorType.UDFPS_OPTICAL)
+ assertThat(isAvailable).isFalse()
+
+ setupFingerprint(FingerprintSensorType.UDFPS_ULTRASONIC)
+ assertThat(isAvailable).isFalse()
+
+ setupFingerprint(FingerprintSensorType.UNKNOWN)
+ assertThat(isAvailable).isFalse()
+ }
+
+ @Test
+ fun authenticationDurationIsAvailableWhenSFPSSensorIsAvailable() =
+ testScope.runTest {
+ assertThat(collectLastValue(underTest.authenticationDuration)())
+ .isEqualTo(context.resources.getInteger(R.integer.config_restToUnlockDuration))
+ }
+
+ @Test
+ fun verticalSensorLocationIsAdjustedToScreenPositionForRotation0() =
+ testScope.runTest {
+ /*
+ (0,0) (1000,0)
+ ------------------
+ | ^^^^^ | (1000, 200)
+ | status bar || <--- start of sensor at Rotation_0
+ | || <--- end of sensor
+ | | (1000, 300)
+ | |
+ ------------------ (1000, 800)
+ */
+ setupFPLocationAndDisplaySize(
+ width = 1000,
+ height = 800,
+ rotation = ROTATION_0,
+ sensorLocationY = 200,
+ sensorWidth = 100,
+ )
+
+ val sensorLocation by collectLastValue(underTest.sensorLocation)
+ assertThat(sensorLocation!!.left).isEqualTo(1000)
+ assertThat(sensorLocation!!.top).isEqualTo(200)
+ assertThat(sensorLocation!!.isSensorVerticalInDefaultOrientation).isEqualTo(true)
+ assertThat(sensorLocation!!.width).isEqualTo(100)
+ }
+
+ @Test
+ fun verticalSensorLocationIsAdjustedToScreenPositionForRotation270() =
+ testScope.runTest {
+ /*
+ (800,0) (800, 1000)
+ ---------------------
+ | | (600, 1000)
+ | < || <--- end of sensor at Rotation_270
+ | < status bar || <--- start of sensor
+ | < | (500, 1000)
+ | < |
+ (0,0) ---------------------
+ */
+ setupFPLocationAndDisplaySize(
+ width = 800,
+ height = 1000,
+ rotation = ROTATION_270,
+ sensorLocationY = 200,
+ sensorWidth = 100,
+ )
+
+ val sensorLocation by collectLastValue(underTest.sensorLocation)
+ assertThat(sensorLocation!!.left).isEqualTo(500)
+ assertThat(sensorLocation!!.top).isEqualTo(1000)
+ assertThat(sensorLocation!!.isSensorVerticalInDefaultOrientation).isEqualTo(true)
+ assertThat(sensorLocation!!.width).isEqualTo(100)
+ }
+
+ @Test
+ fun verticalSensorLocationIsAdjustedToScreenPositionForRotation90() =
+ testScope.runTest {
+ /*
+ (0,0)
+ ---------------------
+ | | (200, 0)
+ | > || <--- end of sensor at Rotation_270
+ | status bar > || <--- start of sensor
+ | > | (300, 0)
+ | > |
+ (800,1000) ---------------------
+ */
+ setupFPLocationAndDisplaySize(
+ width = 800,
+ height = 1000,
+ rotation = ROTATION_90,
+ sensorLocationY = 200,
+ sensorWidth = 100,
+ )
+
+ val sensorLocation by collectLastValue(underTest.sensorLocation)
+ assertThat(sensorLocation!!.left).isEqualTo(200)
+ assertThat(sensorLocation!!.top).isEqualTo(0)
+ assertThat(sensorLocation!!.isSensorVerticalInDefaultOrientation).isEqualTo(true)
+ assertThat(sensorLocation!!.width).isEqualTo(100)
+ }
+
+ @Test
+ fun verticalSensorLocationIsAdjustedToScreenPositionForRotation180() =
+ testScope.runTest {
+ /*
+
+ (1000,800) ---------------------
+ | | (0, 600)
+ | || <--- end of sensor at Rotation_270
+ | status bar || <--- start of sensor
+ | \/\/\/\/\/\/\/ | (0, 500)
+ | |
+ --------------------- (0,0)
+ */
+ setupFPLocationAndDisplaySize(
+ width = 1000,
+ height = 800,
+ rotation = ROTATION_180,
+ sensorLocationY = 200,
+ sensorWidth = 100,
+ )
+
+ val sensorLocation by collectLastValue(underTest.sensorLocation)
+ assertThat(sensorLocation!!.left).isEqualTo(0)
+ assertThat(sensorLocation!!.top).isEqualTo(500)
+ }
+
+ @Test
+ fun horizontalSensorLocationIsAdjustedToScreenPositionForRotation0() =
+ testScope.runTest {
+ /*
+ (0,0) (500,0) (600,0) (1000,0)
+ ____________===_________
+ | |
+ | ^^^^^ |
+ | status bar |
+ | |
+ ------------------------ (1000, 800)
+ */
+ setupFPLocationAndDisplaySize(
+ width = 1000,
+ height = 800,
+ rotation = ROTATION_0,
+ sensorLocationX = 500,
+ sensorWidth = 100,
+ )
+
+ val sensorLocation by collectLastValue(underTest.sensorLocation)
+ assertThat(sensorLocation!!.left).isEqualTo(500)
+ assertThat(sensorLocation!!.top).isEqualTo(0)
+ assertThat(sensorLocation!!.isSensorVerticalInDefaultOrientation).isEqualTo(false)
+ assertThat(sensorLocation!!.width).isEqualTo(100)
+ }
+
+ @Test
+ fun horizontalSensorLocationIsAdjustedToScreenPositionForRotation90() =
+ testScope.runTest {
+ /*
+ (0,1000) (0,500) (0,400) (0,0)
+ ____________===_________
+ | |
+ | > |
+ | status bar > |
+ | > |
+ (800, 1000) ------------------------
+ */
+ setupFPLocationAndDisplaySize(
+ width = 800,
+ height = 1000,
+ rotation = ROTATION_90,
+ sensorLocationX = 500,
+ sensorWidth = 100,
+ )
+
+ val sensorLocation by collectLastValue(underTest.sensorLocation)
+ assertThat(sensorLocation!!.left).isEqualTo(0)
+ assertThat(sensorLocation!!.top).isEqualTo(400)
+ assertThat(sensorLocation!!.isSensorVerticalInDefaultOrientation).isEqualTo(false)
+ assertThat(sensorLocation!!.width).isEqualTo(100)
+ }
+
+ @Test
+ fun horizontalSensorLocationIsAdjustedToScreenPositionForRotation180() =
+ testScope.runTest {
+ /*
+ (1000, 800) (500, 800) (400, 800) (0,800)
+ ____________===_________
+ | |
+ | |
+ | status bar |
+ | \/ \/ \/ \/ \/ \/ \/ |
+ ------------------------ (0,0)
+ */
+ setupFPLocationAndDisplaySize(
+ width = 1000,
+ height = 800,
+ rotation = ROTATION_180,
+ sensorLocationX = 500,
+ sensorWidth = 100,
+ )
+
+ val sensorLocation by collectLastValue(underTest.sensorLocation)
+ assertThat(sensorLocation!!.left).isEqualTo(400)
+ assertThat(sensorLocation!!.top).isEqualTo(800)
+ assertThat(sensorLocation!!.isSensorVerticalInDefaultOrientation).isEqualTo(false)
+ assertThat(sensorLocation!!.width).isEqualTo(100)
+ }
+
+ @Test
+ fun horizontalSensorLocationIsAdjustedToScreenPositionForRotation270() =
+ testScope.runTest {
+ /*
+ (800, 500) (800, 600)
+ (800, 0) ____________===_________ (800,1000)
+ | < |
+ | < |
+ | < status bar |
+ | < |
+ (0,0) ------------------------
+ */
+ setupFPLocationAndDisplaySize(
+ width = 800,
+ height = 1000,
+ rotation = ROTATION_270,
+ sensorLocationX = 500,
+ sensorWidth = 100,
+ )
+
+ val sensorLocation by collectLastValue(underTest.sensorLocation)
+ assertThat(sensorLocation!!.left).isEqualTo(800)
+ assertThat(sensorLocation!!.top).isEqualTo(500)
+ assertThat(sensorLocation!!.isSensorVerticalInDefaultOrientation).isEqualTo(false)
+ assertThat(sensorLocation!!.width).isEqualTo(100)
+ }
+
+ private suspend fun TestScope.setupFPLocationAndDisplaySize(
+ width: Int,
+ height: Int,
+ sensorLocationX: Int = 0,
+ sensorLocationY: Int = 0,
+ rotation: DisplayRotation,
+ sensorWidth: Int
+ ) {
+ overrideResource(R.integer.config_sfpsSensorWidth, sensorWidth)
+ setupDisplayDimensions(width, height)
+ currentRotation.value = rotation
+ setupFingerprint(x = sensorLocationX, y = sensorLocationY, displayId = "expanded_display")
+ }
+
+ private fun setupDisplayDimensions(displayWidth: Int, displayHeight: Int) {
+ whenever(windowManager.maximumWindowMetrics)
+ .thenReturn(
+ WindowMetrics(
+ Rect(0, 0, displayWidth, displayHeight),
+ mock(WindowInsets::class.java)
+ )
+ )
+ }
+
+ private suspend fun TestScope.setupFingerprint(
+ fingerprintSensorType: FingerprintSensorType = FingerprintSensorType.POWER_BUTTON,
+ x: Int = 0,
+ y: Int = 0,
+ displayId: String = "display_id_1"
+ ) {
+ contextDisplayInfo.uniqueId = displayId
+ fingerprintRepository.setProperties(
+ sensorId = 1,
+ strength = SensorStrength.STRONG,
+ sensorType = fingerprintSensorType,
+ sensorLocations =
+ mapOf(
+ "someOtherDisplayId" to
+ SensorLocationInternal(
+ "someOtherDisplayId",
+ x + 100,
+ y + 100,
+ 0,
+ ),
+ displayId to
+ SensorLocationInternal(
+ displayId,
+ x,
+ y,
+ 0,
+ )
+ )
+ )
+ // Emit a display change event, this happens whenever any display related change happens,
+ // rotation, active display changing etc, display switched off/on.
+ displayChangeEvent.emit(1)
+
+ runCurrent()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
index 0d17270..2d8adca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
@@ -19,7 +19,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepositoryImpl
@@ -43,7 +42,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class AlternateBouncerInteractorTest : SysuiTestCase() {
private lateinit var underTest: AlternateBouncerInteractor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt
index a81ca86..4aea4f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt
@@ -19,7 +19,6 @@
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import org.junit.Before
import org.junit.Test
@@ -29,7 +28,6 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class PrimaryBouncerCallbackInteractorTest : SysuiTestCase() {
private val mPrimaryBouncerCallbackInteractor = PrimaryBouncerCallbackInteractor()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
index cb0b74f..2018e61 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
@@ -21,7 +21,6 @@
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.bouncer.ui.BouncerView
@@ -43,7 +42,6 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class PrimaryBouncerInteractorWithCoroutinesTest : SysuiTestCase() {
private lateinit var repository: FakeKeyguardBouncerRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
index 333bd21..802f8e6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
@@ -21,7 +21,6 @@
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor
@@ -50,7 +49,6 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
@kotlinx.coroutines.ExperimentalCoroutinesApi
class KeyguardBouncerViewModelTest : SysuiTestCase() {
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/data/repository/CommunalWidgetRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
index 7fa828f..3df9cbb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
@@ -9,7 +9,6 @@
import android.os.UserManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.coroutines.collectLastValue
@@ -40,7 +39,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
@Mock private lateinit var appWidgetManager: AppWidgetManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index ddf788e..cdc42e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -19,7 +19,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.repository.FakeCommunalRepository
import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
@@ -37,7 +36,6 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
class CommunalInteractorTest : SysuiTestCase() {
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 a10eb29..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
@@ -5,8 +5,8 @@
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
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
@@ -15,11 +15,11 @@
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
@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
@@ -27,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)
}
@@ -41,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/deviceentry/data/repository/DeviceEntryRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt
index 679dfef..2c80035 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt
@@ -12,6 +12,7 @@
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.runBlocking
@@ -22,6 +23,7 @@
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito
+import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@@ -54,6 +56,7 @@
keyguardBypassController = keyguardBypassController,
keyguardStateController = keyguardStateController,
)
+ testScope.runCurrent()
}
@Test
@@ -66,8 +69,7 @@
assertThat(isUnlocked).isFalse()
val captor = argumentCaptor<KeyguardStateController.Callback>()
- Mockito.verify(keyguardStateController, Mockito.atLeastOnce())
- .addCallback(captor.capture())
+ verify(keyguardStateController, Mockito.atLeastOnce()).addCallback(captor.capture())
whenever(keyguardStateController.isUnlocked).thenReturn(true)
captor.value.onUnlockedChanged()
@@ -98,7 +100,12 @@
testScope.runTest {
whenever(keyguardBypassController.isBypassEnabled).thenAnswer { false }
whenever(keyguardBypassController.bypassEnabled).thenAnswer { false }
- assertThat(underTest.isBypassEnabled()).isFalse()
+ withArgCaptor {
+ verify(keyguardBypassController).registerOnBypassStateChangedListener(capture())
+ }
+ .onBypassStateChanged(false)
+ runCurrent()
+ assertThat(underTest.isBypassEnabled.value).isFalse()
}
@Test
@@ -106,7 +113,12 @@
testScope.runTest {
whenever(keyguardBypassController.isBypassEnabled).thenAnswer { true }
whenever(keyguardBypassController.bypassEnabled).thenAnswer { true }
- assertThat(underTest.isBypassEnabled()).isTrue()
+ withArgCaptor {
+ verify(keyguardBypassController).registerOnBypassStateChangedListener(capture())
+ }
+ .onBypassStateChanged(true)
+ runCurrent()
+ assertThat(underTest.isBypassEnabled.value).isTrue()
}
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
index c4cd8a4..c13fde7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
@@ -218,14 +218,14 @@
fun isBypassEnabled_enabledInRepository_true() =
testScope.runTest {
utils.deviceEntryRepository.setBypassEnabled(true)
- assertThat(underTest.isBypassEnabled()).isTrue()
+ assertThat(underTest.isBypassEnabled.value).isTrue()
}
@Test
fun isBypassEnabled_disabledInRepository_false() =
testScope.runTest {
utils.deviceEntryRepository.setBypassEnabled(false)
- assertThat(underTest.isBypassEnabled()).isFalse()
+ assertThat(underTest.isBypassEnabled.value).isFalse()
}
private fun switchToScene(sceneKey: SceneKey) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
index 781ad6b..8a35ef1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
@@ -6,7 +6,6 @@
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.complication.ComplicationHostViewController
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel
@@ -30,7 +29,6 @@
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-@RoboPilotTest
@SmallTest
@RunWith(AndroidJUnit4::class)
class DreamOverlayAnimationsControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt
index 21192fa..2c6c793 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayCallbackControllerTest.kt
@@ -17,7 +17,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -30,7 +29,6 @@
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-@RoboPilotTest
@SmallTest
@RunWith(AndroidJUnit4::class)
class DreamOverlayCallbackControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
index 7c36642..2af6566 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
@@ -38,7 +38,6 @@
import com.android.dream.lowlight.LowLightTransitionCoordinator;
import com.android.keyguard.BouncerPanelExpansionCalculator;
-import com.android.systemui.RoboPilotTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
@@ -53,7 +52,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@RoboPilotTest
@SmallTest
@RunWith(AndroidJUnit4.class)
public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java
index be7638e..d379dc6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java
@@ -26,7 +26,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.systemui.RoboPilotTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
@@ -38,7 +37,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@RoboPilotTest
@SmallTest
@RunWith(AndroidJUnit4.class)
public class DreamOverlayNotificationCountProviderTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
index 8379f73..e5f9972 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
@@ -49,7 +49,6 @@
import com.android.internal.logging.UiEventLogger;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.RoboPilotTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.complication.ComplicationLayoutEngine;
import com.android.systemui.dreams.complication.HideComplicationTouchHandler;
@@ -72,7 +71,6 @@
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-@RoboPilotTest
@SmallTest
@RunWith(AndroidJUnit4.class)
public class DreamOverlayServiceTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
index 2ef227c..365f67b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
@@ -29,7 +29,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.systemui.RoboPilotTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.complication.Complication;
import com.android.systemui.flags.FeatureFlags;
@@ -48,7 +47,6 @@
import java.util.Collection;
-@RoboPilotTest
@SmallTest
@RunWith(AndroidJUnit4.class)
public class DreamOverlayStateControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java
index 12cb332..7ff345c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarItemsProviderTest.java
@@ -24,7 +24,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.systemui.RoboPilotTest;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
@@ -36,7 +35,6 @@
import java.util.List;
import java.util.concurrent.Executor;
-@RoboPilotTest
@SmallTest
@RunWith(AndroidJUnit4.class)
public class DreamOverlayStatusBarItemsProviderTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
index 9566e81..39db2be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
@@ -46,11 +46,10 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.systemui.res.R;
-import com.android.systemui.RoboPilotTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.core.FakeLogBuffer;
+import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
import com.android.systemui.statusbar.policy.NextAlarmController;
@@ -72,7 +71,6 @@
import java.util.Optional;
import java.util.concurrent.Executor;
-@RoboPilotTest
@SmallTest
@RunWith(AndroidJUnit4.class)
public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java
index d32788d..315a24b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/HideComplicationTouchHandlerTest.java
@@ -30,7 +30,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.systemui.RoboPilotTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.complication.Complication;
import com.android.systemui.dreams.DreamOverlayStateController;
@@ -49,7 +48,6 @@
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-@RoboPilotTest
@SmallTest
@RunWith(AndroidJUnit4.class)
public class HideComplicationTouchHandlerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java
index 4a7700f..e0c6ab2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/AssistantAttentionConditionTest.java
@@ -26,7 +26,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.systemui.RoboPilotTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.assist.AssistManager.VisualQueryAttentionListener;
@@ -41,7 +40,6 @@
import kotlinx.coroutines.CoroutineScope;
-@RoboPilotTest
@SmallTest
@RunWith(AndroidJUnit4.class)
public class AssistantAttentionConditionTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java
index cd2efde..480754c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java
@@ -31,7 +31,6 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.systemui.RoboPilotTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.shared.condition.Condition;
@@ -44,7 +43,6 @@
import kotlinx.coroutines.CoroutineScope;
-@RoboPilotTest
@SmallTest
@RunWith(AndroidJUnit4.class)
public class DreamConditionTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
index ffcaeee..3d1efa5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.dreams.touch;
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.eq;
@@ -40,7 +41,6 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.widget.LockPatternUtils;
-import com.android.systemui.RoboPilotTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants;
import com.android.systemui.dreams.touch.scrim.ScrimController;
@@ -64,7 +64,6 @@
import java.util.Collections;
import java.util.Optional;
-@RoboPilotTest
@SmallTest
@RunWith(AndroidJUnit4.class)
public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java
index ff6d97d..6aa821f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java
@@ -27,7 +27,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.systemui.RoboPilotTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.shared.system.InputChannelCompat;
@@ -43,7 +42,6 @@
import java.util.Optional;
-@RoboPilotTest
@SmallTest
@RunWith(AndroidJUnit4.class)
public class ShadeTouchHandlerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java
index da39381..017fdbe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/BouncerlessScrimControllerTest.java
@@ -26,7 +26,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.systemui.RoboPilotTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -38,7 +37,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@RoboPilotTest
@SmallTest
@RunWith(AndroidJUnit4.class)
public class BouncerlessScrimControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java
index 81f6fe3..4ee4a60 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/scrim/ScrimManagerTest.java
@@ -25,7 +25,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.systemui.RoboPilotTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -38,7 +37,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@RoboPilotTest
@SmallTest
@RunWith(AndroidJUnit4.class)
public class ScrimManagerTest extends SysuiTestCase {
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 8d8bc6d0..f51745b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt
@@ -37,7 +37,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
@@ -478,7 +477,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/keyevent/domain/interactor/KeyEventInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractorTest.kt
index 632d149..f373062 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyevent/domain/interactor/KeyEventInteractorTest.kt
@@ -16,23 +16,17 @@
package com.android.systemui.keyevent.domain.interactor
-import android.view.KeyEvent
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.back.domain.interactor.BackActionInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
-import com.android.systemui.keyguard.domain.interactor.KeyguardKeyEventInteractor
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.whenever
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyevent.data.repository.FakeKeyEventRepository
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
@SmallTest
@@ -40,108 +34,27 @@
class KeyEventInteractorTest : SysuiTestCase() {
@JvmField @Rule var mockitoRule = MockitoJUnit.rule()
- private lateinit var keyguardInteractorWithDependencies:
- KeyguardInteractorFactory.WithDependencies
- @Mock private lateinit var keyguardKeyEventInteractor: KeyguardKeyEventInteractor
- @Mock private lateinit var backActionInteractor: BackActionInteractor
+ private lateinit var repository: FakeKeyEventRepository
private lateinit var underTest: KeyEventInteractor
@Before
fun setup() {
- keyguardInteractorWithDependencies = KeyguardInteractorFactory.create()
+ repository = FakeKeyEventRepository()
underTest =
KeyEventInteractor(
- backActionInteractor,
- keyguardKeyEventInteractor,
+ repository,
)
}
@Test
- fun dispatchBackKey_notHandledByKeyguardKeyEventInteractor_handledByBackActionInteractor() {
- val backKeyEventActionDown = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK)
- val backKeyEventActionUp = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK)
+ fun dispatchBackKey_notHandledByKeyguardKeyEventInteractor_handledByBackActionInteractor() =
+ runTest {
+ val isPowerDown by collectLastValue(underTest.isPowerButtonDown)
+ repository.setPowerButtonDown(false)
+ assertThat(isPowerDown).isFalse()
- // GIVEN back key ACTION_DOWN and ACTION_UP aren't handled by the keyguardKeyEventInteractor
- whenever(keyguardKeyEventInteractor.dispatchKeyEvent(backKeyEventActionDown))
- .thenReturn(false)
- whenever(keyguardKeyEventInteractor.dispatchKeyEvent(backKeyEventActionUp))
- .thenReturn(false)
-
- // WHEN back key event ACTION_DOWN, the event is handled even though back isn't requested
- assertThat(underTest.dispatchKeyEvent(backKeyEventActionDown)).isTrue()
- // THEN back event isn't handled on ACTION_DOWN
- verify(backActionInteractor, never()).onBackRequested()
-
- // WHEN back key event ACTION_UP
- assertThat(underTest.dispatchKeyEvent(backKeyEventActionUp)).isTrue()
- // THEN back event is handled on ACTION_UP
- verify(backActionInteractor).onBackRequested()
- }
-
- @Test
- fun dispatchKeyEvent_isNotHandledByKeyguardKeyEventInteractor() {
- val keyEvent =
- KeyEvent(
- KeyEvent.ACTION_UP,
- KeyEvent.KEYCODE_SPACE,
- )
- whenever(keyguardKeyEventInteractor.dispatchKeyEvent(eq(keyEvent))).thenReturn(false)
- assertThat(underTest.dispatchKeyEvent(keyEvent)).isFalse()
- }
-
- @Test
- fun dispatchKeyEvent_handledByKeyguardKeyEventInteractor() {
- val keyEvent =
- KeyEvent(
- KeyEvent.ACTION_UP,
- KeyEvent.KEYCODE_SPACE,
- )
- whenever(keyguardKeyEventInteractor.dispatchKeyEvent(eq(keyEvent))).thenReturn(true)
- assertThat(underTest.dispatchKeyEvent(keyEvent)).isTrue()
- }
-
- @Test
- fun interceptMediaKey_isNotHandledByKeyguardKeyEventInteractor() {
- val keyEvent =
- KeyEvent(
- KeyEvent.ACTION_UP,
- KeyEvent.KEYCODE_SPACE,
- )
- whenever(keyguardKeyEventInteractor.interceptMediaKey(eq(keyEvent))).thenReturn(false)
- assertThat(underTest.interceptMediaKey(keyEvent)).isFalse()
- }
-
- @Test
- fun interceptMediaKey_handledByKeyguardKeyEventInteractor() {
- val keyEvent =
- KeyEvent(
- KeyEvent.ACTION_UP,
- KeyEvent.KEYCODE_SPACE,
- )
- whenever(keyguardKeyEventInteractor.interceptMediaKey(eq(keyEvent))).thenReturn(true)
- assertThat(underTest.interceptMediaKey(keyEvent)).isTrue()
- }
-
- @Test
- fun dispatchKeyEventPreIme_isNotHandledByKeyguardKeyEventInteractor() {
- val keyEvent =
- KeyEvent(
- KeyEvent.ACTION_UP,
- KeyEvent.KEYCODE_SPACE,
- )
- whenever(keyguardKeyEventInteractor.dispatchKeyEventPreIme(eq(keyEvent))).thenReturn(false)
- assertThat(underTest.dispatchKeyEventPreIme(keyEvent)).isFalse()
- }
-
- @Test
- fun dispatchKeyEventPreIme_handledByKeyguardKeyEventInteractor() {
- val keyEvent =
- KeyEvent(
- KeyEvent.ACTION_UP,
- KeyEvent.KEYCODE_SPACE,
- )
- whenever(keyguardKeyEventInteractor.dispatchKeyEventPreIme(eq(keyEvent))).thenReturn(true)
- assertThat(underTest.dispatchKeyEventPreIme(keyEvent)).isTrue()
- }
+ repository.setPowerButtonDown(true)
+ assertThat(isPowerDown).isTrue()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyevent/domain/interactor/SysUIKeyEventHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyevent/domain/interactor/SysUIKeyEventHandlerTest.kt
new file mode 100644
index 0000000..af00a48
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyevent/domain/interactor/SysUIKeyEventHandlerTest.kt
@@ -0,0 +1,147 @@
+/*
+ * 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.keyevent.domain.interactor
+
+import android.view.KeyEvent
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.back.domain.interactor.BackActionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
+import com.android.systemui.keyguard.domain.interactor.KeyguardKeyEventInteractor
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SysUIKeyEventHandlerTest : SysuiTestCase() {
+ @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
+
+ private lateinit var keyguardInteractorWithDependencies:
+ KeyguardInteractorFactory.WithDependencies
+ @Mock private lateinit var keyguardKeyEventInteractor: KeyguardKeyEventInteractor
+ @Mock private lateinit var backActionInteractor: BackActionInteractor
+
+ private lateinit var underTest: SysUIKeyEventHandler
+
+ @Before
+ fun setup() {
+ keyguardInteractorWithDependencies = KeyguardInteractorFactory.create()
+ underTest =
+ SysUIKeyEventHandler(
+ backActionInteractor,
+ keyguardKeyEventInteractor,
+ )
+ }
+
+ @Test
+ fun dispatchBackKey_notHandledByKeyguardKeyEventInteractor_handledByBackActionInteractor() {
+ val backKeyEventActionDown = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK)
+ val backKeyEventActionUp = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK)
+
+ // GIVEN back key ACTION_DOWN and ACTION_UP aren't handled by the keyguardKeyEventInteractor
+ whenever(keyguardKeyEventInteractor.dispatchKeyEvent(backKeyEventActionDown))
+ .thenReturn(false)
+ whenever(keyguardKeyEventInteractor.dispatchKeyEvent(backKeyEventActionUp))
+ .thenReturn(false)
+
+ // WHEN back key event ACTION_DOWN, the event is handled even though back isn't requested
+ assertThat(underTest.dispatchKeyEvent(backKeyEventActionDown)).isTrue()
+ // THEN back event isn't handled on ACTION_DOWN
+ verify(backActionInteractor, never()).onBackRequested()
+
+ // WHEN back key event ACTION_UP
+ assertThat(underTest.dispatchKeyEvent(backKeyEventActionUp)).isTrue()
+ // THEN back event is handled on ACTION_UP
+ verify(backActionInteractor).onBackRequested()
+ }
+
+ @Test
+ fun dispatchKeyEvent_isNotHandledByKeyguardKeyEventInteractor() {
+ val keyEvent =
+ KeyEvent(
+ KeyEvent.ACTION_UP,
+ KeyEvent.KEYCODE_SPACE,
+ )
+ whenever(keyguardKeyEventInteractor.dispatchKeyEvent(eq(keyEvent))).thenReturn(false)
+ assertThat(underTest.dispatchKeyEvent(keyEvent)).isFalse()
+ }
+
+ @Test
+ fun dispatchKeyEvent_handledByKeyguardKeyEventInteractor() {
+ val keyEvent =
+ KeyEvent(
+ KeyEvent.ACTION_UP,
+ KeyEvent.KEYCODE_SPACE,
+ )
+ whenever(keyguardKeyEventInteractor.dispatchKeyEvent(eq(keyEvent))).thenReturn(true)
+ assertThat(underTest.dispatchKeyEvent(keyEvent)).isTrue()
+ }
+
+ @Test
+ fun interceptMediaKey_isNotHandledByKeyguardKeyEventInteractor() {
+ val keyEvent =
+ KeyEvent(
+ KeyEvent.ACTION_UP,
+ KeyEvent.KEYCODE_SPACE,
+ )
+ whenever(keyguardKeyEventInteractor.interceptMediaKey(eq(keyEvent))).thenReturn(false)
+ assertThat(underTest.interceptMediaKey(keyEvent)).isFalse()
+ }
+
+ @Test
+ fun interceptMediaKey_handledByKeyguardKeyEventInteractor() {
+ val keyEvent =
+ KeyEvent(
+ KeyEvent.ACTION_UP,
+ KeyEvent.KEYCODE_SPACE,
+ )
+ whenever(keyguardKeyEventInteractor.interceptMediaKey(eq(keyEvent))).thenReturn(true)
+ assertThat(underTest.interceptMediaKey(keyEvent)).isTrue()
+ }
+
+ @Test
+ fun dispatchKeyEventPreIme_isNotHandledByKeyguardKeyEventInteractor() {
+ val keyEvent =
+ KeyEvent(
+ KeyEvent.ACTION_UP,
+ KeyEvent.KEYCODE_SPACE,
+ )
+ whenever(keyguardKeyEventInteractor.dispatchKeyEventPreIme(eq(keyEvent))).thenReturn(false)
+ assertThat(underTest.dispatchKeyEventPreIme(keyEvent)).isFalse()
+ }
+
+ @Test
+ fun dispatchKeyEventPreIme_handledByKeyguardKeyEventInteractor() {
+ val keyEvent =
+ KeyEvent(
+ KeyEvent.ACTION_UP,
+ KeyEvent.KEYCODE_SPACE,
+ )
+ whenever(keyguardKeyEventInteractor.dispatchKeyEventPreIme(eq(keyEvent))).thenReturn(true)
+ assertThat(underTest.dispatchKeyEventPreIme(keyEvent)).isTrue()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 291dda20..a646823 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -16,7 +16,6 @@
package com.android.systemui.keyguard;
-import static android.os.PowerManager.WAKE_REASON_WAKE_MOTION;
import static android.provider.Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
import static android.view.WindowManagerPolicyConstants.OFF_BECAUSE_OF_TIMEOUT;
@@ -635,29 +634,6 @@
}
@Test
- public void lockAfterSpecifiedAfterDreamStarted() {
- int currentUserId = 99;
- int userSpecificTimeout = 5999;
- KeyguardUpdateMonitor.setCurrentUser(currentUserId);
-
- // set mDeviceInteractive to true
- mViewMediator.onStartedWakingUp(WAKE_REASON_WAKE_MOTION, false);
- mFeatureFlags.set(Flags.LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING, false);
- when(mLockPatternUtils.isSecure(currentUserId)).thenReturn(true);
- when(mDevicePolicyManager.getMaximumTimeToLock(null, currentUserId)).thenReturn(0L);
- when(mSecureSettings.getIntForUser(LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
- KEYGUARD_LOCK_AFTER_DELAY_DEFAULT, currentUserId)).thenReturn(userSpecificTimeout);
- mSystemClock.setElapsedRealtime(0L);
- ArgumentCaptor<PendingIntent> pendingIntent = ArgumentCaptor.forClass(PendingIntent.class);
-
- mViewMediator.onDreamingStarted();
-
- verify(mAlarmManager).setExactAndAllowWhileIdle(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
- eq(Long.valueOf(userSpecificTimeout)), pendingIntent.capture());
- assertEquals(DELAYED_KEYGUARD_ACTION, pendingIntent.getValue().getIntent().getAction());
- }
-
- @Test
public void testHideSurfaceBehindKeyguardMarksKeyguardNotGoingAway() {
mViewMediator.hideSurfaceBehindKeyguard();
@@ -1023,7 +999,6 @@
mViewMediator.setShowingLocked(false);
when(mKeyguardStateController.isShowing()).thenReturn(false);
- mFeatureFlags.set(Flags.LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING, false);
mViewMediator.onDreamingStarted();
assertFalse(mViewMediator.isShowingAndNotOccluded());
}
@@ -1034,7 +1009,6 @@
mViewMediator.setShowingLocked(true);
when(mKeyguardStateController.isShowing()).thenReturn(true);
- mFeatureFlags.set(Flags.LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING, true);
mViewMediator.onDreamingStarted();
assertTrue(mViewMediator.isShowingAndNotOccluded());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
index cfee3b8..e20d3af 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
@@ -23,7 +23,6 @@
import android.content.pm.PackageManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.camera.CameraGestureHelper
import com.android.systemui.settings.UserTracker
@@ -43,7 +42,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class CameraQuickAffordanceConfigTest : SysuiTestCase() {
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 49168d0..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,14 +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.RoboPilotTest
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
@@ -61,7 +60,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
index c85c7f67..548b564 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt
@@ -17,7 +17,6 @@
package com.android.systemui.keyguard.data.quickaffordance
-import com.android.systemui.RoboPilotTest
import com.android.systemui.animation.Expandable
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnTriggeredResult
import kotlinx.coroutines.flow.Flow
@@ -25,7 +24,6 @@
import kotlinx.coroutines.yield
/** Fake implementation of a quick affordance data source. */
-@RoboPilotTest
class FakeKeyguardQuickAffordanceConfig(
override val key: String,
private val pickerName: String = key,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
index c3e28ae..4ae144c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
@@ -21,7 +21,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
import com.android.systemui.statusbar.policy.FlashlightController
@@ -42,7 +41,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
index ef56a98..7d68cc0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
@@ -20,7 +20,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.Expandable
import com.android.systemui.controls.controller.ControlsController
@@ -41,7 +40,6 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class HomeControlsKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
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 4f071bd..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,9 +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.RoboPilotTest
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
@@ -49,7 +48,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class KeyguardQuickAffordanceLegacySettingSyncerTest : SysuiTestCase() {
@@ -72,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/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
index bd0b71d..99a0185 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
@@ -23,7 +23,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.backup.BackupHelper
import com.android.systemui.settings.FakeUserTracker
@@ -53,7 +52,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class KeyguardQuickAffordanceLocalUserSelectionManagerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
index 0797d07..a1c9f87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
@@ -21,7 +21,6 @@
import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.shared.customization.data.content.FakeCustomizationProviderClient
@@ -44,7 +43,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class KeyguardQuickAffordanceRemoteUserSelectionManagerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt
index d8c0341..b15352b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt
@@ -21,7 +21,6 @@
import android.media.AudioManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
@@ -46,7 +45,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class MuteQuickAffordanceConfigTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
index 9d983b8..521dea3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
@@ -20,7 +20,6 @@
import android.content.Intent
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnTriggeredResult
import com.android.systemui.qrcodescanner.controller.QRCodeScannerController
@@ -40,7 +39,6 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class QrCodeScannerKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
index 613c4ce..02db0d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
@@ -24,7 +24,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.animation.Expandable
@@ -51,7 +50,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
index 1414bac..a9b9c90 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
@@ -21,7 +21,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.ActivityIntentHelper
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.camera.CameraIntentsWrapper
import com.android.systemui.coroutines.collectLastValue
@@ -45,7 +44,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class VideoCameraQuickAffordanceConfigTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
index 944b059..d8cdf29 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
@@ -32,7 +32,6 @@
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
import com.android.systemui.biometrics.data.repository.FaceSensorInfo
@@ -79,7 +78,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@RunWith(AndroidJUnit4::class)
class BiometricSettingsRepositoryTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index 307204da..819d08a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -44,7 +44,6 @@
import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
@@ -122,7 +121,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
private lateinit var underTest: DeviceEntryFaceAuthRepositoryImpl
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
index def016a..a58bc52 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
@@ -22,7 +22,6 @@
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
import com.android.systemui.coroutines.collectLastValue
@@ -49,7 +48,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class DeviceEntryFingerprintAuthRepositoryTest : SysuiTestCase() {
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt
index 7eb8a26..9be5558 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt
@@ -18,7 +18,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.shared.model.DevicePosture
@@ -39,7 +38,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class DevicePostureRepositoryTest : SysuiTestCase() {
private lateinit var underTest: DevicePostureRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
index 126b841..567e0a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
@@ -22,7 +22,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
@@ -56,7 +55,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index 7983e30..b9119e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -22,7 +22,6 @@
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
import com.android.systemui.common.shared.model.Position
@@ -64,7 +63,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class KeyguardRepositoryImplTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
index 4942cf8..799bd5a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
@@ -20,7 +20,6 @@
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.AnimatorTestRule
import com.android.systemui.coroutines.collectLastValue
@@ -46,7 +45,6 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
class LightRevealScrimRepositoryTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt
index 7f784d8..ee47c58f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt
@@ -21,7 +21,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.logging.TrustRepositoryLogger
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.FlowValue
import com.android.systemui.coroutines.collectLastValue
@@ -46,7 +45,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class TrustRepositoryTest : SysuiTestCase() {
@Mock private lateinit var trustManager: TrustManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
index 181cc88..d6e19cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
@@ -19,7 +19,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
@@ -43,7 +42,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class KeyguardDismissActionInteractorTest : SysuiTestCase() {
private lateinit var keyguardRepository: FakeKeyguardRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorTest.kt
index c407b14..a5cfbbf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorTest.kt
@@ -22,7 +22,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.TrustGrantFlags
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.shared.model.DismissAction
@@ -40,7 +39,6 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class KeyguardDismissInteractorTest : SysuiTestCase() {
private lateinit var dispatcher: TestDispatcher
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
index b527510..06eb0dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
@@ -28,8 +28,10 @@
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FaceSensorInfo
import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
import com.android.systemui.biometrics.shared.model.LockoutMode
+import com.android.systemui.biometrics.shared.model.SensorStrength
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor
@@ -156,7 +158,6 @@
fakeDeviceEntryFingerprintAuthRepository,
fakeUserRepository,
facePropertyRepository,
- fakeKeyguardRepository,
faceWakeUpTriggersConfig,
powerInteractor,
)
@@ -440,6 +441,43 @@
}
@Test
+ fun faceAuthIsRequestedWhenWalletIsLaunchedAndIfFaceAuthIsStrong() =
+ testScope.runTest {
+ underTest.start()
+ facePropertyRepository.setSensorInfo(FaceSensorInfo(1, SensorStrength.STRONG))
+
+ underTest.onWalletLaunched()
+
+ runCurrent()
+ assertThat(faceAuthRepository.runningAuthRequest.value)
+ .isEqualTo(Pair(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_OCCLUDING_APP_REQUESTED, true))
+ }
+
+ @Test
+ fun faceAuthIsNotTriggeredIfFaceAuthIsWeak() =
+ testScope.runTest {
+ underTest.start()
+ facePropertyRepository.setSensorInfo(FaceSensorInfo(1, SensorStrength.WEAK))
+
+ underTest.onWalletLaunched()
+
+ runCurrent()
+ assertThat(faceAuthRepository.runningAuthRequest.value).isNull()
+ }
+
+ @Test
+ fun faceAuthIsNotTriggeredIfFaceAuthIsConvenience() =
+ testScope.runTest {
+ underTest.start()
+ facePropertyRepository.setSensorInfo(FaceSensorInfo(1, SensorStrength.CONVENIENCE))
+
+ underTest.onWalletLaunched()
+
+ runCurrent()
+ assertThat(faceAuthRepository.runningAuthRequest.value).isNull()
+ }
+
+ @Test
fun faceUnlockIsDisabledWhenFpIsLockedOut() =
testScope.runTest {
underTest.start()
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 ca52cdb..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
@@ -20,7 +20,6 @@
import android.app.StatusBarManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
@@ -47,7 +46,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class KeyguardInteractorTest : SysuiTestCase() {
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 bbe6823..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
@@ -57,8 +57,6 @@
KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_UP)
private val backKeyEvent = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK)
- private lateinit var keyguardInteractorWithDependencies:
- KeyguardInteractorFactory.WithDependencies
private lateinit var powerInteractor: PowerInteractor
@Mock private lateinit var statusBarStateController: StatusBarStateController
@Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
@@ -73,14 +71,12 @@
fun setup() {
whenever(mediaSessionLegacyHelperWrapper.getHelper(any()))
.thenReturn(mediaSessionLegacyHelper)
- keyguardInteractorWithDependencies = KeyguardInteractorFactory.create()
powerInteractor = PowerInteractorFactory.create().powerInteractor
underTest =
KeyguardKeyEventInteractor(
context,
statusBarStateController,
- keyguardInteractorWithDependencies.keyguardInteractor,
statusBarKeyguardViewManager,
shadeController,
mediaSessionLegacyHelperWrapper,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
index 13025a0..0c74a38 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
@@ -22,7 +22,6 @@
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FakeFeatureFlags
@@ -51,7 +50,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class KeyguardLongPressInteractorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 8c13bb4..347d580 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -23,7 +23,6 @@
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.common.shared.model.ContentDescription
@@ -73,7 +72,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt
index fdcc66b..71fcf6f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt
@@ -18,7 +18,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepository
@@ -41,7 +40,6 @@
import org.mockito.MockitoAnnotations.initMocks
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
@kotlinx.coroutines.ExperimentalCoroutinesApi
class KeyguardSurfaceBehindInteractorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
index fa93253..29b546b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
@@ -19,7 +19,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
@@ -42,7 +41,6 @@
import org.junit.runner.RunWith
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
@kotlinx.coroutines.ExperimentalCoroutinesApi
class KeyguardTransitionInteractorTest : SysuiTestCase() {
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/domain/interactor/LightRevealScrimInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
index 906d948..c02add1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
@@ -18,7 +18,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.FakeLightRevealScrimRepository
@@ -45,7 +44,6 @@
import org.mockito.Spy
@SmallTest
-@RoboPilotTest
@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
class LightRevealScrimInteractorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
index 73ecae5..16f2fa2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
@@ -18,7 +18,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
@@ -38,7 +37,6 @@
import org.mockito.MockitoAnnotations.initMocks
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
@kotlinx.coroutines.ExperimentalCoroutinesApi
class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplierTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplierTest.kt
index a22f603..5b29a86 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplierTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplierTest.kt
@@ -20,7 +20,6 @@
import android.view.RemoteAnimationTarget
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardViewController
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.AnimatorTestRule
import com.android.systemui.keyguard.domain.interactor.KeyguardSurfaceBehindInteractor
@@ -41,7 +40,6 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@RunWithLooper(setAsMainLooper = true)
@kotlinx.coroutines.ExperimentalCoroutinesApi
class KeyguardSurfaceBehindParamsApplierTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
index 7a17435..9b2db3e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
@@ -19,7 +19,6 @@
import android.app.IActivityTaskManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -35,7 +34,6 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
@kotlinx.coroutines.ExperimentalCoroutinesApi
class WindowManagerLockscreenVisibilityManagerTest : SysuiTestCase() {
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/AlternateBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
new file mode 100644
index 0000000..1768f8c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
@@ -0,0 +1,197 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
+import com.google.common.collect.Range
+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.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@ExperimentalCoroutinesApi
+@RunWith(JUnit4::class)
+@SmallTest
+class AlternateBouncerViewModelTest : SysuiTestCase() {
+
+ private lateinit var testScope: TestScope
+
+ @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
+ @Mock private lateinit var falsingManager: FalsingManager
+
+ private lateinit var transitionRepository: FakeKeyguardTransitionRepository
+ private lateinit var transitionInteractor: KeyguardTransitionInteractor
+ private lateinit var underTest: AlternateBouncerViewModel
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ testScope = TestScope()
+
+ val transitionInteractorWithDependencies =
+ KeyguardTransitionInteractorFactory.create(testScope.backgroundScope)
+ transitionInteractor = transitionInteractorWithDependencies.keyguardTransitionInteractor
+ transitionRepository = transitionInteractorWithDependencies.repository
+ underTest =
+ AlternateBouncerViewModel(
+ statusBarKeyguardViewManager,
+ transitionInteractor,
+ falsingManager,
+ )
+ }
+
+ @Test
+ fun transitionToAlternateBouncer_scrimAlphaUpdate() =
+ runTest(UnconfinedTestDispatcher()) {
+ val scrimAlphas by collectValues(underTest.scrimAlpha)
+
+ transitionRepository.sendTransitionStep(
+ stepToAlternateBouncer(0f, TransitionState.STARTED)
+ )
+ transitionRepository.sendTransitionStep(stepToAlternateBouncer(.4f))
+ transitionRepository.sendTransitionStep(stepToAlternateBouncer(.6f))
+ transitionRepository.sendTransitionStep(stepToAlternateBouncer(1f))
+
+ assertThat(scrimAlphas.size).isEqualTo(4)
+ scrimAlphas.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
+ }
+
+ @Test
+ fun transitionFromAlternateBouncer_scrimAlphaUpdate() =
+ runTest(UnconfinedTestDispatcher()) {
+ val scrimAlphas by collectValues(underTest.scrimAlpha)
+
+ transitionRepository.sendTransitionStep(
+ stepFromAlternateBouncer(0f, TransitionState.STARTED)
+ )
+ transitionRepository.sendTransitionStep(stepFromAlternateBouncer(.4f))
+ transitionRepository.sendTransitionStep(stepFromAlternateBouncer(.6f))
+ transitionRepository.sendTransitionStep(stepFromAlternateBouncer(1f))
+
+ assertThat(scrimAlphas.size).isEqualTo(4)
+ scrimAlphas.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
+ }
+
+ @Test
+ fun clickListenerUpdate() =
+ runTest(UnconfinedTestDispatcher()) {
+ val clickListener by collectLastValue(underTest.onClickListener)
+
+ // keyguard state => ALTERNATE_BOUNCER
+ transitionRepository.sendTransitionStep(
+ stepToAlternateBouncer(0f, TransitionState.STARTED)
+ )
+ assertThat(clickListener).isNull()
+ transitionRepository.sendTransitionStep(stepToAlternateBouncer(.3f))
+ assertThat(clickListener).isNull()
+ transitionRepository.sendTransitionStep(stepToAlternateBouncer(.6f))
+ assertThat(clickListener).isNull()
+ transitionRepository.sendTransitionStep(stepToAlternateBouncer(1f))
+ assertThat(clickListener).isNotNull()
+
+ // ALTERNATE_BOUNCER -> keyguard state
+ transitionRepository.sendTransitionStep(
+ stepFromAlternateBouncer(0f, TransitionState.STARTED)
+ )
+ assertThat(clickListener).isNotNull()
+ transitionRepository.sendTransitionStep(stepFromAlternateBouncer(.3f))
+ assertThat(clickListener).isNull()
+ transitionRepository.sendTransitionStep(stepFromAlternateBouncer(.6f))
+ assertThat(clickListener).isNull()
+ transitionRepository.sendTransitionStep(stepFromAlternateBouncer(1f))
+ assertThat(clickListener).isNull()
+ }
+
+ @Test
+ fun forcePluginOpen() =
+ runTest(UnconfinedTestDispatcher()) {
+ val forcePluginOpen by collectLastValue(underTest.forcePluginOpen)
+ transitionRepository.sendTransitionStep(
+ stepToAlternateBouncer(0f, TransitionState.STARTED)
+ )
+ transitionRepository.sendTransitionStep(stepToAlternateBouncer(.3f))
+ transitionRepository.sendTransitionStep(stepToAlternateBouncer(.6f))
+ transitionRepository.sendTransitionStep(stepToAlternateBouncer(1f))
+ assertThat(forcePluginOpen).isTrue()
+
+ transitionRepository.sendTransitionStep(
+ stepFromAlternateBouncer(0f, TransitionState.STARTED)
+ )
+ transitionRepository.sendTransitionStep(stepFromAlternateBouncer(.3f))
+ transitionRepository.sendTransitionStep(stepFromAlternateBouncer(.6f))
+ transitionRepository.sendTransitionStep(stepFromAlternateBouncer(1f))
+ assertThat(forcePluginOpen).isFalse()
+ }
+
+ private fun stepToAlternateBouncer(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return step(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.ALTERNATE_BOUNCER,
+ value = value,
+ transitionState = state,
+ )
+ }
+
+ private fun stepFromAlternateBouncer(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return step(
+ from = KeyguardState.ALTERNATE_BOUNCER,
+ to = KeyguardState.LOCKSCREEN,
+ value = value,
+ transitionState = state,
+ )
+ }
+
+ private fun step(
+ from: KeyguardState,
+ to: KeyguardState,
+ value: Float,
+ transitionState: TransitionState
+ ): TransitionStep {
+ return TransitionStep(
+ from = from,
+ to = to,
+ value = value,
+ transitionState = transitionState,
+ ownerName = "AlternateBouncerViewModelTest"
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
index bfc6f31..6d47aed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
@@ -18,7 +18,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
@@ -46,7 +45,6 @@
import org.junit.runner.RunWith
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class DreamingToLockscreenTransitionViewModelTest : SysuiTestCase() {
private lateinit var underTest: DreamingToLockscreenTransitionViewModel
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
index 75c8bff..cf20129 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
@@ -18,7 +18,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
@@ -37,7 +36,6 @@
import org.junit.runner.RunWith
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class GoneToDreamingTransitionViewModelTest : SysuiTestCase() {
private lateinit var underTest: GoneToDreamingTransitionViewModel
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
index 12fe07f..89a1d2b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
@@ -18,7 +18,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
@@ -37,7 +36,6 @@
import org.junit.runner.RunWith
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() {
private lateinit var underTest: LockscreenToDreamingTransitionViewModel
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
index 83ae631..41f8856 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
@@ -18,7 +18,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
@@ -37,7 +36,6 @@
import org.junit.runner.RunWith
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class LockscreenToOccludedTransitionViewModelTest : SysuiTestCase() {
private lateinit var underTest: LockscreenToOccludedTransitionViewModel
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
index 8860399..ec95cb8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
@@ -18,7 +18,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
@@ -37,7 +36,6 @@
import org.junit.runner.RunWith
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class OccludedToLockscreenTransitionViewModelTest : SysuiTestCase() {
private lateinit var underTest: OccludedToLockscreenTransitionViewModel
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 da372ea..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
@@ -18,7 +18,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.coroutines.collectValues
@@ -46,7 +45,6 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() {
private lateinit var underTest: PrimaryBouncerToGoneTransitionViewModel
@@ -62,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/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
index 9ab9b3d..32acefe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
@@ -18,7 +18,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
@@ -46,7 +45,6 @@
@ExperimentalCoroutinesApi
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class UdfpsAodViewModelTest : SysuiTestCase() {
private val defaultPadding = 12
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/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt
index 906420d..9630ee3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt
@@ -46,6 +46,7 @@
private val mockContext = mock<Context>()
private val resources = mock<Resources>()
private val windowManager = mock<WindowManager>()
+ private val windowMetricsProvider = mock<WindowMetricsProvider>()
private val sizeUpdates = arrayListOf<Rect>()
private val testConfigurationController = FakeConfigurationController()
@@ -112,13 +113,12 @@
}
private fun givenTaskbarSize(size: Int) {
- val windowInsets =
- WindowInsets.Builder()
- .setInsets(Type.tappableElement(), Insets.of(Rect(0, 0, 0, size)))
- .build()
+ val insets = Insets.of(Rect(0, 0, 0, size))
+ val windowInsets = WindowInsets.Builder().setInsets(Type.tappableElement(), insets).build()
val windowMetrics = WindowMetrics(windowManager.maximumWindowMetrics.bounds, windowInsets)
whenever(windowManager.maximumWindowMetrics).thenReturn(windowMetrics)
whenever(windowManager.currentWindowMetrics).thenReturn(windowMetrics)
+ whenever(windowMetricsProvider.currentWindowInsets).thenReturn(insets)
}
private fun givenDisplay(width: Int, height: Int, isTablet: Boolean = false) {
@@ -126,6 +126,7 @@
val windowMetrics = WindowMetrics(bounds, { null }, 1.0f)
whenever(windowManager.maximumWindowMetrics).thenReturn(windowMetrics)
whenever(windowManager.currentWindowMetrics).thenReturn(windowMetrics)
+ whenever(windowMetricsProvider.maximumWindowBounds).thenReturn(bounds)
val minDimension = min(width, height)
@@ -147,7 +148,11 @@
}
}
- return TaskPreviewSizeProvider(mockContext, windowManager, testConfigurationController)
+ return TaskPreviewSizeProvider(
+ mockContext,
+ windowMetricsProvider,
+ testConfigurationController
+ )
.also { it.addCallback(listener) }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/WindowMetricsProviderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/WindowMetricsProviderImplTest.kt
new file mode 100644
index 0000000..fe18454
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/WindowMetricsProviderImplTest.kt
@@ -0,0 +1,44 @@
+package com.android.systemui.mediaprojection.appselector.view
+
+import android.graphics.Insets
+import android.graphics.Rect
+import android.view.WindowManager
+import android.view.WindowMetrics
+import androidx.core.view.WindowInsetsCompat
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+@SmallTest
+class WindowMetricsProviderImplTest : SysuiTestCase() {
+
+ private val windowManager = mock<WindowManager>()
+ private val windowMetricsProvider = WindowMetricsProviderImpl(windowManager)
+
+ @Test
+ fun getMaximumWindowBounds_returnsValueFromWMMaxWindowMetrics() {
+ val bounds = Rect(/* left= */ 100, /* top= */ 200, /* right= */ 300, /* bottom= */ 400)
+ val metrics =
+ WindowMetrics(bounds, /* windowInsetsSupplier= */ { null }, /* density= */ 1.0f)
+ whenever(windowManager.maximumWindowMetrics).thenReturn(metrics)
+
+ assertThat(windowMetricsProvider.maximumWindowBounds).isEqualTo(bounds)
+ }
+
+ @Test
+ fun getCurrentWindowInsets_returnsFromWMCurrentWindowMetrics() {
+ val bounds = Rect()
+ val insets =
+ Insets.of(Rect(/* left= */ 123, /* top= */ 456, /* right= */ 789, /* bottom= */ 1012))
+ val windowInsets =
+ android.view.WindowInsets.Builder()
+ .setInsets(WindowInsetsCompat.Type.tappableElement(), insets)
+ .build()
+ whenever(windowManager.currentWindowMetrics).thenReturn(WindowMetrics(bounds, windowInsets))
+
+ assertThat(windowMetricsProvider.currentWindowInsets).isEqualTo(insets)
+ }
+}
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/QSImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
index d57765c..c8c134a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.qs;
+import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
+
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
@@ -41,6 +43,7 @@
import android.os.Bundle;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
+import android.view.Display;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -49,7 +52,6 @@
import androidx.test.filters.SmallTest;
import com.android.keyguard.BouncerPanelExpansionCalculator;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlagsClassic;
@@ -60,6 +62,7 @@
import com.android.systemui.qs.footer.ui.binder.FooterActionsViewBinder;
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel;
import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.res.R;
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.CommandQueue;
@@ -110,6 +113,9 @@
@Mock private FeatureFlagsClassic mFeatureFlags;
private View mQsView;
+ private final CommandQueue mCommandQueue =
+ new CommandQueue(mContext, new FakeDisplayTracker(mContext));
+
private QSImpl mUnderTest;
@@ -120,6 +126,16 @@
mUnderTest.onComponentCreated(mQsComponent, null);
}
+ /*
+ * Regression test for b/303180152.
+ */
+ @Test
+ public void testDisableCallbackOnDisabledQuickSettingsUponCreationDoesntCrash() {
+ QSImpl other = instantiate();
+ mCommandQueue.disable(Display.DEFAULT_DISPLAY, 0, DISABLE2_QUICK_SETTINGS);
+
+ other.onComponentCreated(mQsComponent, null);
+ }
@Test
public void testSaveState() {
@@ -473,7 +489,6 @@
private QSImpl instantiate() {
MockitoAnnotations.initMocks(this);
- CommandQueue commandQueue = new CommandQueue(mContext, new FakeDisplayTracker(mContext));
setupQsComponent();
setUpViews();
@@ -484,11 +499,11 @@
return new QSImpl(
new RemoteInputQuickSettingsDisabler(
mContext,
- commandQueue,
+ mCommandQueue,
new ResourcesSplitShadeStateController(),
mock(ConfigurationController.class)),
mStatusBarStateController,
- commandQueue,
+ mCommandQueue,
mQSMediaHost,
mQQSMediaHost,
mBypassController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index e2ac7bc..b825c08 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -23,6 +23,7 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -42,7 +43,6 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.logging.testing.UiEventLoggerFake;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.media.controls.ui.MediaHost;
@@ -50,6 +50,7 @@
import com.android.systemui.qs.customize.QSCustomizerController;
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.ResourcesSplitShadeStateController;
import com.android.systemui.util.animation.DisappearParameters;
@@ -341,11 +342,92 @@
}
@Test
- public void onViewDetached_removesJustTheAssociatedCallback() {
+ public void onDestroy_removesJustTheAssociatedCallback() {
+ QSPanelControllerBase.TileRecord record = mController.mRecords.get(0);
+
+ mController.destroy();
+ verify(mQSTile).removeCallback(record.callback);
+ verify(mQSTile, never()).removeCallbacks();
+
+ assertThat(mController.mRecords).isEmpty();
+ }
+
+ @Test
+ public void onViewDettached_callbackNotRemoved() {
QSPanelControllerBase.TileRecord record = mController.mRecords.get(0);
mController.onViewDetached();
- verify(mQSTile).removeCallback(record.callback);
+ verify(mQSTile, never()).removeCallback(record.callback);
verify(mQSTile, never()).removeCallbacks();
}
+
+ @Test
+ public void onInit_qsHostCallbackAdded() {
+ verify(mQSHost).addCallback(any());
+ }
+
+ @Test
+ public void onViewDettached_qsHostCallbackNotRemoved() {
+ mController.onViewDetached();
+ verify(mQSHost, never()).removeCallback(any());
+ }
+
+ @Test
+ public void onDestroy_qsHostCallbackRemoved() {
+ mController.destroy();
+ verify(mQSHost).removeCallback(any());
+ }
+
+ @Test
+ public void setTiles_sameTiles_doesntRemoveAndReaddViews() {
+ when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile));
+ mController.setTiles();
+
+ clearInvocations(mQSPanel);
+
+ mController.setTiles();
+ verify(mQSPanel, never()).removeTile(any());
+ verify(mQSPanel, never()).addTile(any());
+ }
+
+ @Test
+ public void setTiles_differentTiles_allTilesRemovedAndNewTilesAdded() {
+ when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile));
+ mController.setTiles();
+
+ clearInvocations(mQSPanel);
+
+ when(mQSHost.getTiles()).thenReturn(List.of(mQSTile));
+ mController.setTiles();
+
+ verify(mQSPanel, times(2)).removeTile(any());
+ verify(mQSPanel).addTile(any());
+ }
+
+ @Test
+ public void detachAndReattach_sameTiles_doesntRemoveAndReAddViews() {
+ when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile));
+ mController.setTiles();
+
+ clearInvocations(mQSPanel);
+
+ mController.onViewDetached();
+ mController.onViewAttached();
+ verify(mQSPanel, never()).removeTile(any());
+ verify(mQSPanel, never()).addTile(any());
+ }
+
+ @Test
+ public void setTiles_sameTilesDifferentOrder_removesAndReadds() {
+ when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile));
+ mController.setTiles();
+
+ clearInvocations(mQSPanel);
+
+ when(mQSHost.getTiles()).thenReturn(List.of(mOtherTile, mQSTile));
+ mController.setTiles();
+
+ verify(mQSPanel, times(2)).removeTile(any());
+ verify(mQSPanel, times(2)).addTile(any());
+ }
}
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/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt
index 9a55f72..d277fca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt
@@ -19,7 +19,6 @@
import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qs.pipeline.shared.TileSpec
@@ -39,7 +38,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class AutoAddSettingsRepositoryTest : SysuiTestCase() {
private val secureSettings = FakeSettings()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt
index 30f5811..3db676d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt
@@ -20,7 +20,6 @@
import android.content.SharedPreferences
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.settings.UserFileManager
import com.android.systemui.util.FakeSharedPreferences
@@ -30,7 +29,6 @@
import org.junit.runner.RunWith
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class CustomTileAddedSharedPreferencesRepositoryTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt
index 995de66..070e07a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt
@@ -32,7 +32,6 @@
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.util.mockito.any
@@ -62,7 +61,6 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
@OptIn(ExperimentalCoroutinesApi::class)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt
index dc09a33..ff8a9bd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt
@@ -4,7 +4,6 @@
import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.FakeBroadcastDispatcher
import com.android.systemui.coroutines.collectLastValue
@@ -24,7 +23,6 @@
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
-@RoboPilotTest
class QSSettingsRestoredBroadcastRepositoryTest : SysuiTestCase() {
private val dispatcher = StandardTestDispatcher()
private val testScope = TestScope(dispatcher)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
index 08adebb..f7c3b21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
@@ -20,7 +20,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qs.pipeline.shared.TileSpec
@@ -40,7 +39,6 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalCoroutinesApi::class)
class TileSpecSettingsRepositoryTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt
index 2087623..9516c21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt
@@ -2,14 +2,12 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
-@RoboPilotTest
@SmallTest
@RunWith(AndroidJUnit4::class)
class TilesSettingConverterTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt
index 81fd72b..36e860e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt
@@ -3,7 +3,6 @@
import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qs.pipeline.data.model.RestoreData
@@ -24,7 +23,6 @@
import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
-@RoboPilotTest
@SmallTest
@RunWith(AndroidJUnit4::class)
class UserAutoAddRepositoryTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt
index 389580c1..d4a9fab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt
@@ -3,7 +3,6 @@
import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qs.pipeline.data.model.RestoreData
@@ -23,7 +22,6 @@
import org.mockito.Mock
import org.mockito.MockitoAnnotations
-@RoboPilotTest
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt
index 15e401d..4454a3c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingListTest.kt
@@ -20,7 +20,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.util.mockito.mock
@@ -29,7 +28,6 @@
import org.junit.runner.RunWith
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class AutoAddableSettingListTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt
index 7c6dd24..d153e9d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt
@@ -18,7 +18,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
@@ -36,7 +35,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class AutoAddableSettingTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt
index 469eee3..ec139e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CallbackControllerAutoAddableTest.kt
@@ -18,7 +18,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
@@ -36,7 +35,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class CallbackControllerAutoAddableTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt
index b6eaa39..4fae532 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt
@@ -18,7 +18,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
@@ -42,7 +41,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class CastAutoAddableTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt
index a755fbb..9e2d1f8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DataSaverAutoAddableTest.kt
@@ -18,7 +18,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
@@ -41,7 +40,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class DataSaverAutoAddableTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt
index daacca51..0116bd9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/DeviceControlsAutoAddableTest.kt
@@ -18,7 +18,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
@@ -44,7 +43,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class DeviceControlsAutoAddableTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt
index 4b5f7f6..e7ea9a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/HotspotAutoAddableTest.kt
@@ -18,7 +18,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
@@ -41,7 +40,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class HotspotAutoAddableTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddableTest.kt
index 32d9db2..20fd360 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddableTest.kt
@@ -19,7 +19,6 @@
import android.hardware.display.NightDisplayListener
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.dagger.NightDisplayListenerModule
@@ -50,7 +49,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class NightDisplayAutoAddableTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt
index fb513a6..19ac63c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/ReduceBrightColorsAutoAddableTest.kt
@@ -18,7 +18,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qs.ReduceBrightColorsController
@@ -44,7 +43,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class ReduceBrightColorsAutoAddableTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt
index 5ce15fa..d645ee3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/SafetyCenterAutoAddableTest.kt
@@ -21,7 +21,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
@@ -52,7 +51,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class SafetyCenterAutoAddableTest : SysuiTestCase() {
private val testDispatcher = StandardTestDispatcher()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt
index 1c8cb54..83ff35d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WalletAutoAddableTest.kt
@@ -18,7 +18,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
@@ -38,7 +37,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class WalletAutoAddableTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
index de1d29fd..adccc84 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
@@ -23,7 +23,6 @@
import android.content.pm.UserInfo.FLAG_PROFILE
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
@@ -41,7 +40,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class WorkTileAutoAddableTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt
index bb18115..41a7ec0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt
@@ -18,7 +18,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.dump.DumpManager
@@ -47,7 +46,6 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalCoroutinesApi::class)
class AutoAddInteractorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
index a750524..a89338a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
@@ -24,7 +24,6 @@
import android.service.quicksettings.Tile
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.dump.nano.SystemUIProtoDump
@@ -71,7 +70,6 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalCoroutinesApi::class)
class CurrentTilesInteractorImplTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt
index 151b256..0d97115 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt
@@ -17,7 +17,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.shade.ShadeController
import org.junit.Before
@@ -27,7 +26,6 @@
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
@SmallTest
class PanelInteractorImplTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt
index 2e6b50b..f73cab8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt
@@ -2,7 +2,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qs.pipeline.data.model.RestoreData
@@ -20,7 +19,6 @@
import org.junit.runner.RunWith
import org.mockito.MockitoAnnotations
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
@SmallTest
class RestoreReconciliationInteractorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt
index 34c4c98..558e769 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt
@@ -19,14 +19,12 @@
import android.content.ComponentName
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class TileSpecTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
index e537131..4c5a214 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -54,11 +54,7 @@
import com.android.systemui.statusbar.connectivity.SignalCallback;
import com.android.systemui.statusbar.connectivity.WifiIndicators;
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository;
-import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository;
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor;
-import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl;
-import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel;
-import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel.Inactive;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.CastDevice;
import com.android.systemui.statusbar.policy.HotspotController;
@@ -111,7 +107,8 @@
private WifiInteractor mWifiInteractor;
private final TileJavaAdapter mJavaAdapter = new TileJavaAdapter();
- private final FakeWifiRepository mWifiRepository = new FakeWifiRepository();
+ private final FakeConnectivityRepository mConnectivityRepository =
+ new FakeConnectivityRepository();
private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
private final TestScope mTestScope = TestScopeProvider.getTestScope();
@@ -124,12 +121,6 @@
mTestableLooper = TestableLooper.get(this);
when(mHost.getContext()).thenReturn(mContext);
-
- mWifiInteractor = new WifiInteractorImpl(
- new FakeConnectivityRepository(),
- mWifiRepository,
- mTestScope
- );
}
@After
@@ -204,25 +195,41 @@
// SIGNAL_CALLBACK_DEPRECATION flag set to true
@Test
- public void stateUnavailable_wifiDisabled_newPipeline() {
+ public void stateUnavailable_noDefaultNetworks_newPipeline() {
createAndStartTileNewImpl();
- mWifiRepository.setIsWifiEnabled(false);
mTestableLooper.processAllMessages();
assertEquals(Tile.STATE_UNAVAILABLE, mCastTile.getState().state);
}
@Test
- public void stateUnavailable_wifiEnabled_notConnected_newPipeline() {
+ public void stateUnavailable_mobileConnected_newPipeline() {
createAndStartTileNewImpl();
- mWifiRepository.setIsWifiEnabled(true);
- mWifiRepository.setWifiNetwork(Inactive.INSTANCE);
+ mConnectivityRepository.setMobileConnected(true);
mTestableLooper.processAllMessages();
assertEquals(Tile.STATE_UNAVAILABLE, mCastTile.getState().state);
}
@Test
+ public void stateInactive_wifiConnected_newPipeline() {
+ createAndStartTileNewImpl();
+ mConnectivityRepository.setWifiConnected(true);
+ mTestableLooper.processAllMessages();
+
+ assertEquals(Tile.STATE_INACTIVE, mCastTile.getState().state);
+ }
+
+ @Test
+ public void stateInactive_ethernetConnected_newPipeline() {
+ createAndStartTileNewImpl();
+ mConnectivityRepository.setEthernetConnected(true);
+ mTestableLooper.processAllMessages();
+
+ assertEquals(Tile.STATE_INACTIVE, mCastTile.getState().state);
+ }
+
+ @Test
public void stateActive_wifiConnectedAndCasting_newPipeline() {
createAndStartTileNewImpl();
CastController.CastDevice device = new CastController.CastDevice();
@@ -231,40 +238,27 @@
devices.add(device);
when(mController.getCastDevices()).thenReturn(devices);
- mWifiRepository.setWifiNetwork(
- new WifiNetworkModel.Active(
- 1 /* networkId */,
- true /* isValidated */,
- 3 /* level */,
- "test" /* ssid */,
- WifiNetworkModel.HotspotDeviceType.NONE,
- false /* isPasspointAccessPoint */,
- false /* isOnlineSignUpforPasspointAccessPoint */,
- null /* passpointProviderFriendlyName */
- ));
+ mConnectivityRepository.setWifiConnected(true);
+
mTestableLooper.processAllMessages();
assertEquals(Tile.STATE_ACTIVE, mCastTile.getState().state);
}
@Test
- public void stateInactive_wifiConnectedNotCasting_newPipeline() {
+ public void stateActive_ethernetConnectedAndCasting_newPipeline() {
createAndStartTileNewImpl();
+ CastController.CastDevice device = new CastController.CastDevice();
+ device.state = CastDevice.STATE_CONNECTED;
+ List<CastDevice> devices = new ArrayList<>();
+ devices.add(device);
+ when(mController.getCastDevices()).thenReturn(devices);
- mWifiRepository.setWifiNetwork(
- new WifiNetworkModel.Active(
- 1 /* networkId */,
- true /* isValidated */,
- 3 /* level */,
- "test" /* ssid */,
- WifiNetworkModel.HotspotDeviceType.NONE,
- false /* isPasspointAccessPoint */,
- false /* isOnlineSignUpforPasspointAccessPoint */,
- null /* passpointProviderFriendlyName */
- ));
+ mConnectivityRepository.setEthernetConnected(true);
+
mTestableLooper.processAllMessages();
- assertEquals(Tile.STATE_INACTIVE, mCastTile.getState().state);
+ assertEquals(Tile.STATE_ACTIVE, mCastTile.getState().state);
}
// -------------------------------------------------
@@ -512,7 +506,7 @@
mNetworkController,
mHotspotController,
mDialogLaunchAnimator,
- mWifiInteractor,
+ mConnectivityRepository,
mJavaAdapter,
mFeatureFlags
);
@@ -555,7 +549,7 @@
mNetworkController,
mHotspotController,
mDialogLaunchAnimator,
- mWifiInteractor,
+ mConnectivityRepository,
mJavaAdapter,
mFeatureFlags
);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandlerTest.kt
index 06b7a9f..5659f01 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandlerTest.kt
@@ -19,7 +19,6 @@
import android.content.Intent
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.ActivityStarter
import org.junit.Before
@@ -32,7 +31,6 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class QSTileIntentUserActionHandlerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt
index 2c4e10e..9861606 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt
@@ -20,7 +20,6 @@
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
import com.android.internal.logging.UiEventLogger
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.qs.QSEvent
import com.android.systemui.qs.tiles.viewmodel.QSTileConfigTestBuilder
@@ -34,7 +33,6 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class QSTileAnalyticsTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt
index 4f25d12..a6199c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt
@@ -24,7 +24,6 @@
import androidx.test.filters.SmallTest
import com.android.settingslib.RestrictedLockUtils
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.util.mockito.any
@@ -46,7 +45,6 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class DisabledByPolicyInteractorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
index 4401e0d..f1fcee3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
@@ -18,7 +18,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
@@ -39,7 +38,6 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class QSTileLoggerTest : SysuiTestCase() {
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/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
index 4760dfa..2084aeb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
@@ -20,7 +20,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import com.android.internal.logging.InstanceId
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.common.shared.model.ContentDescription
@@ -49,7 +48,6 @@
// TODO(b/299909368): Add more tests
@MediumTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class QSTileViewModelInterfaceComplianceTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
index 58e36be..10c7c43 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
@@ -21,6 +21,8 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.flags.Flags
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
@@ -49,6 +51,7 @@
private val testScope = utils.testScope
private val sceneInteractor = utils.sceneInteractor()
private val mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
+ private val flags = FakeFeatureFlagsClassic().also { it.set(Flags.NEW_NETWORK_SLICE_UI, false) }
private var mobileIconsViewModel: MobileIconsViewModel =
MobileIconsViewModel(
@@ -61,6 +64,7 @@
FakeConnectivityRepository(),
),
constants = mock(),
+ flags,
scope = testScope.backgroundScope,
)
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 61dd69a..88a5c17 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -25,6 +25,8 @@
import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainLayerAuthenticationMethodModel
import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
import com.android.systemui.model.SysUiState
@@ -137,6 +139,7 @@
)
private val mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
+ private val flags = FakeFeatureFlagsClassic().also { it.set(Flags.NEW_NETWORK_SLICE_UI, false) }
private var mobileIconsViewModel: MobileIconsViewModel =
MobileIconsViewModel(
@@ -149,6 +152,7 @@
FakeConnectivityRepository(),
),
constants = mock(),
+ flags,
scope = testScope.backgroundScope,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
index 3ae1f35..da4dc85 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
@@ -32,6 +32,7 @@
import com.android.systemui.settings.UserContextProvider
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
+import junit.framework.Assert.assertEquals
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -111,6 +112,15 @@
}
@Test
+ fun showDialog_singleAppIsDefault() {
+ dialog.show()
+
+ val spinner = dialog.requireViewById<Spinner>(R.id.screen_share_mode_spinner)
+ val singleApp = context.getString(R.string.screen_share_permission_dialog_option_single_app)
+ assertEquals(spinner.adapter.getItem(0), singleApp)
+ }
+
+ @Test
fun showDialog_cancelClicked_dialogIsDismissed() {
dialog.show()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
index a105c15..d8821aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
@@ -63,8 +63,8 @@
@Before
fun setUp() {
- whenever(controllerFactory.create(eq(0))).thenReturn(controller0)
- whenever(controllerFactory.create(eq(1))).thenReturn(controller1)
+ whenever(controllerFactory.create(eq(0), any())).thenReturn(controller0)
+ whenever(controllerFactory.create(eq(1), any())).thenReturn(controller1)
}
@Test
@@ -74,8 +74,8 @@
val onSaved = { _: Uri -> }
screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
- verify(controllerFactory).create(eq(0))
- verify(controllerFactory).create(eq(1))
+ verify(controllerFactory).create(eq(0), any())
+ verify(controllerFactory).create(eq(1), any())
val capturer = ArgumentCaptor<ScreenshotData>()
@@ -107,8 +107,8 @@
callback
)
- verify(controllerFactory).create(eq(0))
- verify(controllerFactory, never()).create(eq(1))
+ verify(controllerFactory).create(eq(0), any())
+ verify(controllerFactory, never()).create(eq(1), any())
val capturer = ArgumentCaptor<ScreenshotData>()
@@ -139,7 +139,7 @@
@Test
fun executeScreenshots_allowedTypes_allCaptured() =
testScope.runTest {
- whenever(controllerFactory.create(any())).thenReturn(controller0)
+ whenever(controllerFactory.create(any(), any())).thenReturn(controller0)
setDisplays(
display(TYPE_INTERNAL, id = 0),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
index 6205d90..5091a70 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
@@ -86,7 +86,7 @@
)
.thenReturn(false)
whenever(userManager.isUserUnlocked).thenReturn(true)
- whenever(controllerFactory.create(any())).thenReturn(controller)
+ whenever(controllerFactory.create(any(), any())).thenReturn(controller)
// Stub request processor as a synchronous no-op for tests with the flag enabled
doAnswer {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
index 1bb2ff8..263b001 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
@@ -42,6 +42,7 @@
@Parameterized.Parameters
fun data(): Iterable<String> =
listOf(
+ Intent.ACTION_LOCALE_CHANGED,
Intent.ACTION_USER_INFO_CHANGED,
Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
index 52b25f8..c32d259 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
@@ -177,7 +177,8 @@
verify(context)
.registerReceiverForAllUsers(eq(tracker), capture(captor), isNull(), eq(handler))
with(captor.value) {
- assertThat(countActions()).isEqualTo(6)
+ assertThat(countActions()).isEqualTo(7)
+ assertThat(hasAction(Intent.ACTION_LOCALE_CHANGED)).isTrue()
assertThat(hasAction(Intent.ACTION_USER_INFO_CHANGED)).isTrue()
assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)).isTrue()
assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)).isTrue()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 7b12931..700c9ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -79,12 +79,12 @@
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
import com.android.keyguard.dagger.KeyguardUserSwitcherComponent;
import com.android.keyguard.logging.KeyguardLogger;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
import com.android.systemui.common.ui.view.LongPressHandlingView;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.dump.DumpManager;
@@ -120,6 +120,9 @@
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.qs.QSFragmentLegacy;
+import com.android.systemui.res.R;
+import com.android.systemui.scene.SceneTestUtils;
+import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.shade.data.repository.FakeShadeRepository;
import com.android.systemui.shade.data.repository.ShadeRepository;
@@ -137,6 +140,7 @@
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository;
import com.android.systemui.statusbar.notification.ConversationNotificationManager;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
@@ -153,11 +157,11 @@
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
import com.android.systemui.statusbar.phone.KeyguardBottomAreaView;
import com.android.systemui.statusbar.phone.KeyguardBottomAreaViewController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.phone.KeyguardClockPositionAlgorithm;
import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
import com.android.systemui.statusbar.phone.KeyguardStatusBarViewController;
import com.android.systemui.statusbar.phone.LightBarController;
@@ -168,20 +172,26 @@
import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
import com.android.systemui.statusbar.phone.TapAgainViewController;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView;
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
+import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
+import com.android.systemui.user.data.repository.FakeUserRepository;
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.util.time.SystemClock;
import com.android.wm.shell.animation.FlingAnimationUtils;
+import dagger.Lazy;
+
import org.junit.After;
import org.junit.Before;
import org.mockito.ArgumentCaptor;
@@ -193,8 +203,9 @@
import java.util.List;
import java.util.Optional;
-import dagger.Lazy;
import kotlinx.coroutines.CoroutineDispatcher;
+import kotlinx.coroutines.flow.StateFlowKt;
+import kotlinx.coroutines.test.TestScope;
public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
@@ -208,7 +219,7 @@
@Mock protected KeyguardBottomAreaViewController mKeyguardBottomAreaViewController;
@Mock protected ViewPropertyAnimator mViewPropertyAnimator;
@Mock protected KeyguardBottomAreaView mQsFrame;
- @Mock protected HeadsUpManagerPhone mHeadsUpManager;
+ @Mock protected HeadsUpManager mHeadsUpManager;
@Mock protected NotificationShelfController mNotificationShelfController;
@Mock protected NotificationGutsManager mGutsManager;
@Mock protected KeyguardStatusBarView mKeyguardStatusBar;
@@ -322,16 +333,19 @@
mEmptySpaceClickListenerCaptor;
@Mock protected ActivityStarter mActivityStarter;
@Mock protected KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
- @Mock private ShadeInteractor mShadeInteractor;
@Mock private JavaAdapter mJavaAdapter;
@Mock private CastController mCastController;
@Mock private KeyguardRootView mKeyguardRootView;
@Mock private SharedNotificationContainerInteractor mSharedNotificationContainerInteractor;
+ @Mock private KeyguardClockPositionAlgorithm mKeyguardClockPositionAlgorithm;
protected final int mMaxUdfpsBurnInOffsetY = 5;
protected KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor;
protected FakeKeyguardRepository mFakeKeyguardRepository;
protected KeyguardInteractor mKeyguardInteractor;
+ protected SceneTestUtils mUtils = new SceneTestUtils(this);
+ protected TestScope mTestScope = mUtils.getTestScope();
+ protected ShadeInteractor mShadeInteractor;
protected PowerInteractor mPowerInteractor;
protected NotificationPanelViewController.TouchHandler mTouchHandler;
protected ConfigurationController mConfigurationController;
@@ -367,10 +381,31 @@
mKeyguardInteractor = keyguardInteractorDeps.getKeyguardInteractor();
mShadeRepository = new FakeShadeRepository();
mPowerInteractor = keyguardInteractorDeps.getPowerInteractor();
+ when(mKeyguardTransitionInteractor.isInTransitionToStateWhere(any())).thenReturn(
+ StateFlowKt.MutableStateFlow(false));
+ mShadeInteractor = new ShadeInteractor(
+ mTestScope.getBackgroundScope(),
+ new FakeDeviceProvisioningRepository(),
+ new FakeDisableFlagsRepository(),
+ mDozeParameters,
+ new FakeSceneContainerFlags(),
+ mUtils::sceneInteractor,
+ mFakeKeyguardRepository,
+ mKeyguardTransitionInteractor,
+ mPowerInteractor,
+ new FakeUserSetupRepository(),
+ new FakeUserRepository(),
+ new SharedNotificationContainerInteractor(
+ new FakeConfigurationRepository(),
+ mContext,
+ new ResourcesSplitShadeStateController()
+ ),
+ mShadeRepository
+ );
SystemClock systemClock = new FakeSystemClock();
mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger, mDumpManager,
- mInteractionJankMonitor, mShadeExpansionStateManager);
+ mInteractionJankMonitor, () -> mShadeInteractor);
KeyguardStatusView keyguardStatusView = new KeyguardStatusView(mContext);
keyguardStatusView.setId(R.id.keyguard_status_view);
@@ -527,9 +562,10 @@
NotificationWakeUpCoordinator coordinator =
new NotificationWakeUpCoordinator(
mDumpManager,
- mock(HeadsUpManagerPhone.class),
+ mock(HeadsUpManager.class),
new StatusBarStateControllerImpl(new UiEventLoggerFake(), mDumpManager,
- mInteractionJankMonitor, mShadeExpansionStateManager),
+ mInteractionJankMonitor,
+ () -> mShadeInteractor),
mKeyguardBypassController,
mDozeParameters,
mScreenOffAnimationController,
@@ -547,7 +583,7 @@
mLockscreenShadeTransitionController,
new FalsingCollectorFake(),
mDumpManager);
- when(mKeyguardStatusViewComponentFactory.build(any()))
+ when(mKeyguardStatusViewComponentFactory.build(any(), any()))
.thenReturn(mKeyguardStatusViewComponent);
when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController())
.thenReturn(mKeyguardClockSwitchController);
@@ -666,7 +702,8 @@
mKeyguardViewConfigurator,
mKeyguardFaceAuthInteractor,
new ResourcesSplitShadeStateController(),
- mPowerInteractor);
+ mPowerInteractor,
+ mKeyguardClockPositionAlgorithm);
mNotificationPanelViewController.initDependencies(
mCentralSurfaces,
null,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index 2ed2090..d4fb387 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -47,18 +47,34 @@
import androidx.test.filters.SmallTest;
import com.android.internal.colorextraction.ColorExtractor;
+import com.android.keyguard.KeyguardSecurityModel;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository;
+import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
+import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FakeFeatureFlagsClassic;
import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.keyguard.data.repository.FakeCommandQueue;
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository;
+import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor;
+import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.power.data.repository.FakePowerRepository;
+import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.res.R;
import com.android.systemui.scene.FakeWindowRootViewComponent;
import com.android.systemui.scene.SceneTestUtils;
+import com.android.systemui.scene.data.repository.SceneContainerRepository;
+import com.android.systemui.scene.domain.interactor.SceneInteractor;
import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags;
+import com.android.systemui.scene.shared.logger.SceneLogger;
import com.android.systemui.shade.data.repository.FakeShadeRepository;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.StatusBarState;
@@ -71,10 +87,10 @@
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository;
import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
-import com.android.systemui.user.domain.interactor.UserInteractor;
+import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository;
+import com.android.systemui.user.data.repository.FakeUserRepository;
import com.google.common.util.concurrent.MoreExecutors;
@@ -109,6 +125,7 @@
@Mock private SysuiColorExtractor mColorExtractor;
@Mock ColorExtractor.GradientColors mGradientColors;
@Mock private DumpManager mDumpManager;
+ @Mock private KeyguardSecurityModel mKeyguardSecurityModel;
@Mock private KeyguardStateController mKeyguardStateController;
@Mock private ScreenOffAnimationController mScreenOffAnimationController;
@Mock private AuthController mAuthController;
@@ -123,6 +140,8 @@
private NotificationShadeWindowControllerImpl mNotificationShadeWindowController;
private float mPreferredRefreshRate = -1;
+ private FromLockscreenTransitionInteractor mFromLockscreenTransitionInteractor;
+ private FromPrimaryBouncerTransitionInteractor mFromPrimaryBouncerTransitionInteractor;
@Before
public void setUp() {
@@ -137,22 +156,86 @@
when(mDozeParameters.getAlwaysOn()).thenReturn(true);
when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors);
+ FakeKeyguardRepository keyguardRepository = new FakeKeyguardRepository();
+ FakeFeatureFlagsClassic featureFlags = new FakeFeatureFlagsClassic();
+ FakeShadeRepository shadeRepository = new FakeShadeRepository();
+ FakePowerRepository powerRepository = new FakePowerRepository();
+
+ PowerInteractor powerInteractor = new PowerInteractor(
+ powerRepository,
+ new FalsingCollectorFake(),
+ mScreenOffAnimationController,
+ mStatusBarStateController);
+
+ SceneInteractor sceneInteractor = new SceneInteractor(
+ mTestScope.getBackgroundScope(),
+ new SceneContainerRepository(
+ mTestScope.getBackgroundScope(),
+ mUtils.fakeSceneContainerConfig(mUtils.fakeSceneKeys())),
+ powerRepository,
+ mock(SceneLogger.class));
+
+ FakeConfigurationRepository configurationRepository = new FakeConfigurationRepository();
+ FakeSceneContainerFlags sceneContainerFlags = new FakeSceneContainerFlags();
+ KeyguardInteractor keyguardInteractor = new KeyguardInteractor(
+ keyguardRepository,
+ new FakeCommandQueue(),
+ powerInteractor,
+ featureFlags,
+ sceneContainerFlags,
+ new FakeDeviceEntryRepository(),
+ new FakeKeyguardBouncerRepository(),
+ configurationRepository,
+ shadeRepository,
+ () -> sceneInteractor);
+
+ FakeKeyguardTransitionRepository keyguardTransitionRepository =
+ new FakeKeyguardTransitionRepository();
+
+ KeyguardTransitionInteractor keyguardTransitionInteractor =
+ new KeyguardTransitionInteractor(
+ mTestScope.getBackgroundScope(),
+ keyguardTransitionRepository,
+ () -> keyguardInteractor,
+ () -> mFromLockscreenTransitionInteractor,
+ () -> mFromPrimaryBouncerTransitionInteractor);
+
+ mFromLockscreenTransitionInteractor = new FromLockscreenTransitionInteractor(
+ keyguardTransitionRepository,
+ keyguardTransitionInteractor,
+ mTestScope.getBackgroundScope(),
+ keyguardInteractor,
+ featureFlags,
+ shadeRepository,
+ powerInteractor);
+
+ mFromPrimaryBouncerTransitionInteractor = new FromPrimaryBouncerTransitionInteractor(
+ keyguardTransitionRepository,
+ keyguardTransitionInteractor,
+ mTestScope.getBackgroundScope(),
+ keyguardInteractor,
+ featureFlags,
+ mKeyguardSecurityModel,
+ powerInteractor);
+
mShadeInteractor =
new ShadeInteractor(
mTestScope.getBackgroundScope(),
+ new FakeDeviceProvisioningRepository(),
new FakeDisableFlagsRepository(),
- new FakeSceneContainerFlags(),
- mUtils::sceneInteractor,
- new FakeKeyguardRepository(),
+ mock(DozeParameters.class),
+ sceneContainerFlags,
+ () -> sceneInteractor,
+ keyguardRepository,
+ keyguardTransitionInteractor,
+ powerInteractor,
new FakeUserSetupRepository(),
- mock(DeviceProvisionedController.class),
- mock(UserInteractor.class),
+ new FakeUserRepository(),
new SharedNotificationContainerInteractor(
- new FakeConfigurationRepository(),
+ configurationRepository,
mContext,
new ResourcesSplitShadeStateController()),
- new FakeShadeRepository()
- );
+ shadeRepository);
mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(
mContext,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 9651072..677d9db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -34,6 +34,7 @@
import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
import com.android.systemui.bouncer.data.repository.BouncerMessageRepositoryImpl
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor
import com.android.systemui.bouncer.domain.interactor.CountDownTimerUtil
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor
@@ -48,7 +49,7 @@
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.flags.SystemPropertiesHelper
-import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor
+import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
@@ -83,7 +84,6 @@
import com.android.systemui.util.mockito.any
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
-import java.util.Optional
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.test.TestScope
@@ -97,8 +97,9 @@
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+import java.util.Optional
+import org.mockito.Mockito.`when` as whenever
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -143,7 +144,9 @@
@Mock lateinit var dragDownHelper: DragDownHelper
@Mock
lateinit var primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel
- @Mock lateinit var keyEventInteractor: KeyEventInteractor
+ @Mock lateinit var sysUIKeyEventHandler: SysUIKeyEventHandler
+ @Mock lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
+ @Mock lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
private val notificationExpansionRepository = NotificationExpansionRepository()
private lateinit var fakeClock: FakeSystemClock
@@ -176,6 +179,7 @@
featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
featureFlags.set(Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false)
featureFlags.set(Flags.MIGRATE_NSSL, false)
+ featureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, false)
testScope = TestScope()
fakeClock = FakeSystemClock()
@@ -247,7 +251,9 @@
securityModel = mock(KeyguardSecurityModel::class.java),
),
BouncerLogger(logcatLogBuffer("BouncerLog")),
- keyEventInteractor,
+ sysUIKeyEventHandler,
+ primaryBouncerInteractor,
+ alternateBouncerInteractor,
)
underTest.setupExpandedStatusBar()
underTest.setDragDownHelper(dragDownHelper)
@@ -425,29 +431,37 @@
}
@Test
- fun shouldInterceptTouchEvent_notificationPanelViewControllerShouldIntercept() {
- // GIVEN not dozing
- whenever(sysuiStatusBarStateController.isDozing()).thenReturn(false)
+ fun shouldInterceptTouchEvent_dozing_touchInLockIconArea_touchNotIntercepted() {
+ // GIVEN dozing
+ whenever(sysuiStatusBarStateController.isDozing).thenReturn(true)
// AND alternate bouncer doesn't want the touch
whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
.thenReturn(false)
- // AND the lock icon doesn't want the touch
- whenever(lockIconViewController.onInterceptTouchEvent(DOWN_EVENT)).thenReturn(false)
- // AND the notification panel can accept touches
- whenever(notificationPanelViewController.isFullyExpanded()).thenReturn(true)
- whenever(dragDownHelper.isDragDownEnabled).thenReturn(true)
- whenever(centralSurfaces.isBouncerShowing()).thenReturn(false)
-
- // AND the drag down helper doesn't want the touch (to pull the shade down)
- whenever(dragDownHelper.onInterceptTouchEvent(DOWN_EVENT)).thenReturn(false)
+ // AND the lock icon wants the touch
+ whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT))
+ .thenReturn(true)
featureFlags.set(Flags.MIGRATE_NSSL, true)
- // WHEN asked if should intercept touch
- interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)
+ // THEN touch should NOT be intercepted by NotificationShade
+ assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isFalse()
+ }
- // Verify that NPVC gets a chance to use the touch
- verify(notificationPanelViewController).handleExternalInterceptTouch(DOWN_EVENT)
+ @Test
+ fun shouldInterceptTouchEvent_dozing_touchNotInLockIconArea_touchIntercepted() {
+ // GIVEN dozing
+ whenever(sysuiStatusBarStateController.isDozing).thenReturn(true)
+ // AND alternate bouncer doesn't want the touch
+ whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
+ .thenReturn(false)
+ // AND the lock icon does NOT want the touch
+ whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT))
+ .thenReturn(false)
+
+ featureFlags.set(Flags.MIGRATE_NSSL, true)
+
+ // THEN touch should NOT be intercepted by NotificationShade
+ assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue()
}
@Test
@@ -461,21 +475,21 @@
fun forwardsDispatchKeyEvent() {
val keyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_B)
interactionEventHandler.dispatchKeyEvent(keyEvent)
- verify(keyEventInteractor).dispatchKeyEvent(keyEvent)
+ verify(sysUIKeyEventHandler).dispatchKeyEvent(keyEvent)
}
@Test
fun forwardsDispatchKeyEventPreIme() {
val keyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_B)
interactionEventHandler.dispatchKeyEventPreIme(keyEvent)
- verify(keyEventInteractor).dispatchKeyEventPreIme(keyEvent)
+ verify(sysUIKeyEventHandler).dispatchKeyEventPreIme(keyEvent)
}
@Test
fun forwardsInterceptMediaKey() {
val keyEvent = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_UP)
interactionEventHandler.interceptMediaKey(keyEvent)
- verify(keyEventInteractor).interceptMediaKey(keyEvent)
+ verify(sysUIKeyEventHandler).interceptMediaKey(keyEvent)
}
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index 0023020..a4a2ca0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -33,6 +33,7 @@
import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
import com.android.systemui.bouncer.data.repository.BouncerMessageRepositoryImpl
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor
import com.android.systemui.bouncer.domain.interactor.CountDownTimerUtil
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor
@@ -47,7 +48,7 @@
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.flags.SystemPropertiesHelper
-import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor
+import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
@@ -141,6 +142,8 @@
Optional<UnfoldTransitionProgressProvider>
@Mock private lateinit var notificationInsetsController: NotificationInsetsController
@Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
+ @Mock lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
+ @Mock lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
@Mock
private lateinit var primaryBouncerToGoneTransitionViewModel:
PrimaryBouncerToGoneTransitionViewModel
@@ -180,6 +183,7 @@
featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
featureFlags.set(Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false)
featureFlags.set(Flags.MIGRATE_NSSL, false)
+ featureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, false)
testScope = TestScope()
controller =
NotificationShadeWindowViewController(
@@ -249,7 +253,9 @@
securityModel = Mockito.mock(KeyguardSecurityModel::class.java),
),
BouncerLogger(logcatLogBuffer("BouncerLog")),
- Mockito.mock(KeyEventInteractor::class.java),
+ Mockito.mock(SysUIKeyEventHandler::class.java),
+ primaryBouncerInteractor,
+ alternateBouncerInteractor,
)
controller.setupExpandedStatusBar()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
index 8138b32..8a876c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -32,23 +32,39 @@
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
+import com.android.keyguard.KeyguardSecurityModel;
import com.android.keyguard.KeyguardStatusView;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository;
+import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
+import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FakeFeatureFlagsClassic;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.fragments.FragmentHostManager;
+import com.android.systemui.keyguard.data.repository.FakeCommandQueue;
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository;
+import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor;
+import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.media.controls.pipeline.MediaDataManager;
import com.android.systemui.media.controls.ui.MediaHierarchyManager;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.power.data.repository.FakePowerRepository;
+import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.qs.QSFragmentLegacy;
import com.android.systemui.res.R;
import com.android.systemui.scene.SceneTestUtils;
+import com.android.systemui.scene.data.repository.SceneContainerRepository;
+import com.android.systemui.scene.domain.interactor.SceneInteractor;
import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags;
+import com.android.systemui.scene.shared.logger.SceneLogger;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.shade.data.repository.FakeShadeRepository;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
@@ -64,28 +80,31 @@
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor;
+import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardBottomAreaView;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
+import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository;
import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
-import com.android.systemui.user.domain.interactor.UserInteractor;
+import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository;
+import com.android.systemui.user.data.repository.FakeUserRepository;
import com.android.systemui.util.kotlin.JavaAdapter;
+import dagger.Lazy;
+
import org.junit.After;
import org.junit.Before;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import dagger.Lazy;
import kotlinx.coroutines.test.TestScope;
public class QuickSettingsControllerBaseTest extends SysuiTestCase {
@@ -103,8 +122,7 @@
protected SceneTestUtils mUtils = new SceneTestUtils(this);
protected TestScope mTestScope = mUtils.getTestScope();
- @Mock
- protected Resources mResources;
+ @Mock protected Resources mResources;
@Mock protected KeyguardBottomAreaView mQsFrame;
@Mock protected KeyguardStatusBarView mKeyguardStatusBar;
@Mock protected QS mQs;
@@ -126,6 +144,7 @@
@Mock protected NotificationShadeDepthController mNotificationShadeDepthController;
@Mock protected ShadeHeaderController mShadeHeaderController;
@Mock protected StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
+ @Mock protected DozeParameters mDozeParameters;
@Mock protected KeyguardStateController mKeyguardStateController;
@Mock protected KeyguardBypassController mKeyguardBypassController;
@Mock protected KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -144,8 +163,6 @@
@Mock protected DumpManager mDumpManager;
@Mock protected UiEventLogger mUiEventLogger;
@Mock protected CastController mCastController;
- @Mock protected DeviceProvisionedController mDeviceProvisionedController;
- @Mock protected UserInteractor mUserInteractor;
protected FakeDisableFlagsRepository mDisableFlagsRepository =
new FakeDisableFlagsRepository();
protected FakeKeyguardRepository mKeyguardRepository = new FakeKeyguardRepository();
@@ -161,29 +178,99 @@
new ShadeExpansionStateManager();
protected FragmentHostManager.FragmentListener mFragmentListener;
+ private FromLockscreenTransitionInteractor mFromLockscreenTransitionInteractor;
+ private FromPrimaryBouncerTransitionInteractor mFromPrimaryBouncerTransitionInteractor;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
when(mPanelViewControllerLazy.get()).thenReturn(mNotificationPanelViewController);
mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger, mDumpManager,
- mInteractionJankMonitor, mShadeExpansionStateManager);
+ mInteractionJankMonitor, () -> mShadeInteractor);
- when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
+ FakeDeviceProvisioningRepository deviceProvisioningRepository =
+ new FakeDeviceProvisioningRepository();
+ deviceProvisioningRepository.setDeviceProvisioned(true);
+ FakeFeatureFlagsClassic featureFlags = new FakeFeatureFlagsClassic();
+ FakePowerRepository powerRepository = new FakePowerRepository();
+ FakeConfigurationRepository configurationRepository = new FakeConfigurationRepository();
+
+ PowerInteractor powerInteractor = new PowerInteractor(
+ powerRepository,
+ new FalsingCollectorFake(),
+ mock(ScreenOffAnimationController.class),
+ mStatusBarStateController);
+
+ SceneInteractor sceneInteractor = new SceneInteractor(
+ mTestScope.getBackgroundScope(),
+ new SceneContainerRepository(
+ mTestScope.getBackgroundScope(),
+ mUtils.fakeSceneContainerConfig(mUtils.fakeSceneKeys())),
+ powerRepository,
+ mock(SceneLogger.class));
+
+ FakeSceneContainerFlags sceneContainerFlags = new FakeSceneContainerFlags();
+ KeyguardInteractor keyguardInteractor = new KeyguardInteractor(
+ mKeyguardRepository,
+ new FakeCommandQueue(),
+ powerInteractor,
+ featureFlags,
+ sceneContainerFlags,
+ new FakeDeviceEntryRepository(),
+ new FakeKeyguardBouncerRepository(),
+ configurationRepository,
+ mShadeRepository,
+ () -> sceneInteractor);
+
+ FakeKeyguardTransitionRepository keyguardTransitionRepository =
+ new FakeKeyguardTransitionRepository();
+
+ KeyguardTransitionInteractor keyguardTransitionInteractor =
+ new KeyguardTransitionInteractor(
+ mTestScope.getBackgroundScope(),
+ keyguardTransitionRepository,
+ () -> keyguardInteractor,
+ () -> mFromLockscreenTransitionInteractor,
+ () -> mFromPrimaryBouncerTransitionInteractor);
+
+ mFromLockscreenTransitionInteractor = new FromLockscreenTransitionInteractor(
+ keyguardTransitionRepository,
+ keyguardTransitionInteractor,
+ mTestScope.getBackgroundScope(),
+ keyguardInteractor,
+ featureFlags,
+ mShadeRepository,
+ powerInteractor);
+
+ mFromPrimaryBouncerTransitionInteractor = new FromPrimaryBouncerTransitionInteractor(
+ keyguardTransitionRepository,
+ keyguardTransitionInteractor,
+ mTestScope.getBackgroundScope(),
+ keyguardInteractor,
+ featureFlags,
+ mock(KeyguardSecurityModel.class),
+ powerInteractor);
+
+ ResourcesSplitShadeStateController splitShadeStateController =
+ new ResourcesSplitShadeStateController();
+
mShadeInteractor =
new ShadeInteractor(
mTestScope.getBackgroundScope(),
+ deviceProvisioningRepository,
mDisableFlagsRepository,
- new FakeSceneContainerFlags(),
- () -> mUtils.sceneInteractor(),
+ mDozeParameters,
+ sceneContainerFlags,
+ () -> sceneInteractor,
mKeyguardRepository,
+ keyguardTransitionInteractor,
+ powerInteractor,
new FakeUserSetupRepository(),
- mDeviceProvisionedController,
- mUserInteractor,
+ new FakeUserRepository(),
new SharedNotificationContainerInteractor(
- new FakeConfigurationRepository(),
+ configurationRepository,
mContext,
- new ResourcesSplitShadeStateController()),
+ splitShadeStateController),
mShadeRepository
);
@@ -262,7 +349,7 @@
mShadeInteractor,
new JavaAdapter(mTestScope.getBackgroundScope()),
mCastController,
- new ResourcesSplitShadeStateController()
+ splitShadeStateController
);
mQsController.init();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
index b5841a9..215f8b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.shade
import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
import android.view.Display
import android.view.WindowManager
import androidx.test.filters.SmallTest
@@ -54,6 +55,7 @@
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
+@RunWithLooper(setAsMainLooper = true)
@SmallTest
class ShadeControllerImplTest : SysuiTestCase() {
private val executor = FakeExecutor(FakeSystemClock())
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 2be1c09..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
@@ -16,50 +16,52 @@
package com.android.systemui.shade.data.repository
-import android.app.ActivityManager
import android.app.StatusBarManager.DISABLE2_NONE
import android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE
import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS
import android.content.pm.UserInfo
import android.os.UserManager
import androidx.test.filters.SmallTest
-import com.android.internal.logging.UiEventLogger
-import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.SysUITestModule
+import com.android.TestMocksModule
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FakeFeatureFlagsClassicModule
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.DozeStateModel
+import com.android.systemui.keyguard.shared.model.DozeTransitionModel
+import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.StatusBarState
-import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.power.data.repository.FakePowerRepository
+import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.res.R
-import com.android.systemui.scene.SceneTestUtils
-import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
+import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.ObservableTransitionState
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository
-import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
+import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository
-import com.android.systemui.statusbar.policy.DeviceProvisionedController
-import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
-import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
-import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
+import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
import com.android.systemui.user.data.repository.FakeUserRepository
-import com.android.systemui.user.domain.interactor.GuestUserInteractor
-import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode
-import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
-import com.android.systemui.user.domain.interactor.UserInteractor
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.user.domain.UserDomainLayerModule
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
+import dagger.BindsInstance
+import dagger.Component
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -70,50 +72,55 @@
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
class ShadeInteractorTest : SysuiTestCase() {
+
+ @Mock private lateinit var dozeParameters: DozeParameters
+
+ private lateinit var testComponent: TestComponent
+
+ private val configurationRepository
+ get() = testComponent.configurationRepository
+ private val deviceProvisioningRepository
+ get() = testComponent.deviceProvisioningRepository
+ private val disableFlagsRepository
+ get() = testComponent.disableFlagsRepository
+ private val keyguardRepository
+ get() = testComponent.keyguardRepository
+ private val keyguardTransitionRepository
+ get() = testComponent.keygaurdTransitionRepository
+ private val powerRepository
+ get() = testComponent.powerRepository
+ private val sceneInteractor
+ get() = testComponent.sceneInteractor
+ private val shadeRepository
+ get() = testComponent.shadeRepository
+ private val testScope
+ get() = testComponent.testScope
+ private val userRepository
+ get() = testComponent.userRepository
+ private val userSetupRepository
+ get() = testComponent.userSetupRepository
+
private lateinit var underTest: ShadeInteractor
- private val utils = SceneTestUtils(this)
- private val testScope = utils.testScope
- private val featureFlags = FakeFeatureFlags()
- private val sceneContainerFlags = FakeSceneContainerFlags()
- private val sceneInteractor = utils.sceneInteractor()
- private val userSetupRepository = FakeUserSetupRepository()
- private val userRepository = FakeUserRepository()
- private val disableFlagsRepository = FakeDisableFlagsRepository()
- private val keyguardRepository = FakeKeyguardRepository()
- private val shadeRepository = FakeShadeRepository()
- private val configurationRepository = FakeConfigurationRepository()
- private val sharedNotificationContainerInteractor =
- SharedNotificationContainerInteractor(
- configurationRepository,
- mContext,
- ResourcesSplitShadeStateController()
- )
-
- @Mock private lateinit var manager: UserManager
- @Mock private lateinit var headlessSystemUserMode: HeadlessSystemUserMode
- @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
- @Mock private lateinit var activityStarter: ActivityStarter
- @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
- @Mock private lateinit var activityManager: ActivityManager
- @Mock private lateinit var uiEventLogger: UiEventLogger
- @Mock private lateinit var guestInteractor: GuestUserInteractor
-
- private lateinit var userInteractor: UserInteractor
-
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- featureFlags.set(Flags.FACE_AUTH_REFACTOR, false)
- featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
-
- val refreshUsersScheduler =
- RefreshUsersScheduler(
- applicationScope = testScope.backgroundScope,
- mainDispatcher = utils.testDispatcher,
- repository = userRepository,
- )
+ testComponent =
+ DaggerShadeInteractorTest_TestComponent.factory()
+ .create(
+ test = this,
+ featureFlags =
+ FakeFeatureFlagsClassicModule {
+ set(Flags.FACE_AUTH_REFACTOR, false)
+ set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+ },
+ mocks =
+ TestMocksModule(
+ dozeParameters = dozeParameters,
+ ),
+ )
+ underTest = testComponent.underTest
runBlocking {
val userInfos =
@@ -131,44 +138,6 @@
userRepository.setUserInfos(userInfos)
userRepository.setSelectedUserInfo(userInfos[0])
}
- userInteractor =
- UserInteractor(
- applicationContext = context,
- repository = userRepository,
- activityStarter = activityStarter,
- keyguardInteractor =
- KeyguardInteractorFactory.create(featureFlags = featureFlags)
- .keyguardInteractor,
- featureFlags = featureFlags,
- manager = manager,
- headlessSystemUserMode = headlessSystemUserMode,
- applicationScope = testScope.backgroundScope,
- telephonyInteractor =
- TelephonyInteractor(
- repository = FakeTelephonyRepository(),
- ),
- broadcastDispatcher = fakeBroadcastDispatcher,
- keyguardUpdateMonitor = keyguardUpdateMonitor,
- backgroundDispatcher = utils.testDispatcher,
- activityManager = activityManager,
- refreshUsersScheduler = refreshUsersScheduler,
- guestUserInteractor = guestInteractor,
- uiEventLogger = uiEventLogger,
- userRestrictionChecker = mock(),
- )
- underTest =
- ShadeInteractor(
- testScope.backgroundScope,
- disableFlagsRepository,
- sceneContainerFlags,
- { sceneInteractor },
- keyguardRepository,
- userSetupRepository,
- deviceProvisionedController,
- userInteractor,
- sharedNotificationContainerInteractor,
- shadeRepository,
- )
}
@Test
@@ -188,7 +157,7 @@
@Test
fun isExpandToQsEnabled_deviceNotProvisioned_false() =
testScope.runTest {
- whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(false)
+ deviceProvisioningRepository.setDeviceProvisioned(false)
val actual by collectLastValue(underTest.isExpandToQsEnabled)
@@ -198,7 +167,7 @@
@Test
fun isExpandToQsEnabled_userNotSetupAndSimpleUserSwitcher_false() =
testScope.runTest {
- whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ deviceProvisioningRepository.setDeviceProvisioned(true)
userSetupRepository.setUserSetup(false)
userRepository.setSettings(UserSwitcherSettingsModel(isSimpleUserSwitcher = true))
@@ -211,7 +180,7 @@
@Test
fun isExpandToQsEnabled_shadeNotEnabled_false() =
testScope.runTest {
- whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ deviceProvisioningRepository.setDeviceProvisioned(true)
userSetupRepository.setUserSetup(true)
disableFlagsRepository.disableFlags.value =
@@ -227,7 +196,7 @@
@Test
fun isExpandToQsEnabled_quickSettingsNotEnabled_false() =
testScope.runTest {
- whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ deviceProvisioningRepository.setDeviceProvisioned(true)
userSetupRepository.setUserSetup(true)
disableFlagsRepository.disableFlags.value =
@@ -242,7 +211,7 @@
@Test
fun isExpandToQsEnabled_dozing_false() =
testScope.runTest {
- whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ deviceProvisioningRepository.setDeviceProvisioned(true)
userSetupRepository.setUserSetup(true)
disableFlagsRepository.disableFlags.value =
DisableFlagsModel(
@@ -259,7 +228,7 @@
@Test
fun isExpandToQsEnabled_userSetup_true() =
testScope.runTest {
- whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ deviceProvisioningRepository.setDeviceProvisioned(true)
keyguardRepository.setIsDozing(false)
disableFlagsRepository.disableFlags.value =
DisableFlagsModel(
@@ -276,7 +245,7 @@
@Test
fun isExpandToQsEnabled_notSimpleUserSwitcher_true() =
testScope.runTest {
- whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ deviceProvisioningRepository.setDeviceProvisioned(true)
keyguardRepository.setIsDozing(false)
disableFlagsRepository.disableFlags.value =
DisableFlagsModel(
@@ -293,7 +262,7 @@
@Test
fun isExpandToQsEnabled_respondsToDozingUpdates() =
testScope.runTest {
- whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ deviceProvisioningRepository.setDeviceProvisioned(true)
keyguardRepository.setIsDozing(false)
disableFlagsRepository.disableFlags.value =
DisableFlagsModel(
@@ -321,7 +290,7 @@
@Test
fun isExpandToQsEnabled_respondsToDisableUpdates() =
testScope.runTest {
- whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ deviceProvisioningRepository.setDeviceProvisioned(true)
keyguardRepository.setIsDozing(false)
disableFlagsRepository.disableFlags.value =
DisableFlagsModel(
@@ -353,7 +322,7 @@
@Test
fun isExpandToQsEnabled_respondsToUserUpdates() =
testScope.runTest {
- whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ deviceProvisioningRepository.setDeviceProvisioned(true)
keyguardRepository.setIsDozing(false)
disableFlagsRepository.disableFlags.value =
DisableFlagsModel(
@@ -1129,4 +1098,168 @@
// THEN interacting is false
assertThat(interacting).isFalse()
}
+
+ @Test
+ fun isShadeTouchable_isFalse_whenFrpIsActive() =
+ testScope.runTest {
+ deviceProvisioningRepository.setFactoryResetProtectionActive(true)
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ val isShadeTouchable by collectLastValue(underTest.isShadeTouchable)
+ runCurrent()
+ assertThat(isShadeTouchable).isFalse()
+ }
+
+ @Test
+ fun isShadeTouchable_isFalse_whenDeviceAsleepAndNotPulsing() =
+ testScope.runTest {
+ powerRepository.updateWakefulness(
+ rawState = WakefulnessState.ASLEEP,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.OTHER,
+ )
+ // goingToSleep == false
+ // TODO: remove?
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.GONE,
+ to = KeyguardState.LOCKSCREEN,
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ keyguardRepository.setDozeTransitionModel(
+ DozeTransitionModel(
+ to = DozeStateModel.DOZE_AOD,
+ )
+ )
+ val isShadeTouchable by collectLastValue(underTest.isShadeTouchable)
+ runCurrent()
+ assertThat(isShadeTouchable).isFalse()
+ }
+
+ @Test
+ fun isShadeTouchable_isTrue_whenDeviceAsleepAndPulsing() =
+ testScope.runTest {
+ powerRepository.updateWakefulness(
+ rawState = WakefulnessState.ASLEEP,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.OTHER,
+ )
+ // goingToSleep == false
+ // TODO: remove?
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.GONE,
+ to = KeyguardState.LOCKSCREEN,
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ keyguardRepository.setDozeTransitionModel(
+ DozeTransitionModel(
+ to = DozeStateModel.DOZE_PULSING,
+ )
+ )
+ val isShadeTouchable by collectLastValue(underTest.isShadeTouchable)
+ runCurrent()
+ assertThat(isShadeTouchable).isTrue()
+ }
+
+ @Test
+ fun isShadeTouchable_isFalse_whenStartingToSleepAndNotControlScreenOff() =
+ testScope.runTest {
+ powerRepository.updateWakefulness(
+ rawState = WakefulnessState.STARTING_TO_SLEEP,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.OTHER,
+ )
+ // goingToSleep == true
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ whenever(dozeParameters.shouldControlScreenOff()).thenReturn(false)
+ val isShadeTouchable by collectLastValue(underTest.isShadeTouchable)
+ runCurrent()
+ assertThat(isShadeTouchable).isFalse()
+ }
+
+ @Test
+ fun isShadeTouchable_isTrue_whenStartingToSleepAndControlScreenOff() =
+ testScope.runTest {
+ powerRepository.updateWakefulness(
+ rawState = WakefulnessState.STARTING_TO_SLEEP,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.OTHER,
+ )
+ // goingToSleep == true
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ whenever(dozeParameters.shouldControlScreenOff()).thenReturn(true)
+ val isShadeTouchable by collectLastValue(underTest.isShadeTouchable)
+ runCurrent()
+ assertThat(isShadeTouchable).isTrue()
+ }
+
+ @Test
+ fun isShadeTouchable_isTrue_whenNotAsleep() =
+ testScope.runTest {
+ powerRepository.updateWakefulness(
+ rawState = WakefulnessState.AWAKE,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.OTHER,
+ )
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ val isShadeTouchable by collectLastValue(underTest.isShadeTouchable)
+ runCurrent()
+ assertThat(isShadeTouchable).isTrue()
+ }
+
+ @SysUISingleton
+ @Component(
+ modules =
+ [
+ SysUITestModule::class,
+ UserDomainLayerModule::class,
+ ]
+ )
+ interface TestComponent {
+
+ val underTest: ShadeInteractor
+
+ val configurationRepository: FakeConfigurationRepository
+ val deviceProvisioningRepository: FakeDeviceProvisioningRepository
+ val disableFlagsRepository: FakeDisableFlagsRepository
+ val keyguardRepository: FakeKeyguardRepository
+ val keygaurdTransitionRepository: FakeKeyguardTransitionRepository
+ val powerRepository: FakePowerRepository
+ val sceneInteractor: SceneInteractor
+ val shadeRepository: FakeShadeRepository
+ val testScope: TestScope
+ val userRepository: FakeUserRepository
+ val userSetupRepository: FakeUserSetupRepository
+
+ @Component.Factory
+ interface Factory {
+ fun create(
+ @BindsInstance test: SysuiTestCase,
+ featureFlags: FakeFeatureFlagsClassicModule,
+ mocks: TestMocksModule,
+ ): TestComponent
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
index 7463e65..6eeafefd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
@@ -18,7 +18,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.shade.ShadeExpansionChangeEvent
import com.android.systemui.shade.ShadeExpansionStateManager
@@ -43,7 +42,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class ShadeRepositoryImplTest : SysuiTestCase() {
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 607cdab..df38f93 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
@@ -4,6 +4,8 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.flags.Flags
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.model.ObservableTransitionState
import com.android.systemui.scene.shared.model.SceneKey
@@ -31,6 +33,7 @@
private val sceneInteractor = utils.sceneInteractor()
private val mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
+ private val flags = FakeFeatureFlagsClassic().also { it.set(Flags.NEW_NETWORK_SLICE_UI, false) }
private var mobileIconsViewModel: MobileIconsViewModel =
MobileIconsViewModel(
@@ -43,6 +46,7 @@
FakeConnectivityRepository(),
),
constants = mock(),
+ flags,
scope = testScope.backgroundScope,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
index c423782..3064f4b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
@@ -21,6 +21,8 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.flags.Flags
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
@@ -55,6 +57,7 @@
)
private val mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
+ private val flags = FakeFeatureFlagsClassic().also { it.set(Flags.NEW_NETWORK_SLICE_UI, false) }
private var mobileIconsViewModel: MobileIconsViewModel =
MobileIconsViewModel(
@@ -67,6 +70,7 @@
FakeConnectivityRepository(),
),
constants = mock(),
+ flags,
scope = testScope.backgroundScope,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index e714736..ae659f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -131,11 +131,10 @@
fun addClock(
id: ClockId,
- name: String,
create: (ClockId) -> ClockController = ::failFactory,
getThumbnail: (ClockId) -> Drawable? = ::failThumbnail
): FakeClockPlugin {
- metadata.add(ClockMetadata(id, name))
+ metadata.add(ClockMetadata(id))
createCallbacks[id] = create
thumbnailCallbacks[id] = getThumbnail
return this
@@ -149,7 +148,7 @@
scope = TestScope(dispatcher)
fakeDefaultProvider = FakeClockPlugin()
- .addClock(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME, { mockDefaultClock }, { mockThumbnail })
+ .addClock(DEFAULT_CLOCK_ID, { mockDefaultClock }, { mockThumbnail })
whenever(mockContext.contentResolver).thenReturn(mockContentResolver)
val captor = argumentCaptor<PluginListener<ClockProviderPlugin>>()
@@ -183,13 +182,13 @@
@Test
fun pluginRegistration_CorrectState() {
val plugin1 = FakeClockPlugin()
- .addClock("clock_1", "clock 1")
- .addClock("clock_2", "clock 2")
+ .addClock("clock_1")
+ .addClock("clock_2")
val lifecycle1 = FakeLifecycle("1", plugin1)
val plugin2 = FakeClockPlugin()
- .addClock("clock_3", "clock 3")
- .addClock("clock_4", "clock 4")
+ .addClock("clock_3")
+ .addClock("clock_4")
val lifecycle2 = FakeLifecycle("2", plugin2)
pluginListener.onPluginLoaded(plugin1, mockContext, lifecycle1)
@@ -198,11 +197,11 @@
assertEquals(
list.toSet(),
setOf(
- ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME),
- ClockMetadata("clock_1", "clock 1"),
- ClockMetadata("clock_2", "clock 2"),
- ClockMetadata("clock_3", "clock 3"),
- ClockMetadata("clock_4", "clock 4")
+ ClockMetadata(DEFAULT_CLOCK_ID),
+ ClockMetadata("clock_1"),
+ ClockMetadata("clock_2"),
+ ClockMetadata("clock_3"),
+ ClockMetadata("clock_4")
)
)
}
@@ -216,13 +215,13 @@
@Test
fun clockIdConflict_ErrorWithoutCrash_unloadDuplicate() {
val plugin1 = FakeClockPlugin()
- .addClock("clock_1", "clock 1", { mockClock }, { mockThumbnail })
- .addClock("clock_2", "clock 2", { mockClock }, { mockThumbnail })
+ .addClock("clock_1", { mockClock }, { mockThumbnail })
+ .addClock("clock_2", { mockClock }, { mockThumbnail })
val lifecycle1 = spy(FakeLifecycle("1", plugin1))
val plugin2 = FakeClockPlugin()
- .addClock("clock_1", "clock 1")
- .addClock("clock_2", "clock 2")
+ .addClock("clock_1")
+ .addClock("clock_2")
val lifecycle2 = spy(FakeLifecycle("2", plugin2))
pluginListener.onPluginLoaded(plugin1, mockContext, lifecycle1)
@@ -231,9 +230,9 @@
assertEquals(
list.toSet(),
setOf(
- ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME),
- ClockMetadata("clock_1", "clock 1"),
- ClockMetadata("clock_2", "clock 2")
+ ClockMetadata(DEFAULT_CLOCK_ID),
+ ClockMetadata("clock_1"),
+ ClockMetadata("clock_2")
)
)
@@ -248,13 +247,13 @@
@Test
fun createCurrentClock_pluginConnected() {
val plugin1 = FakeClockPlugin()
- .addClock("clock_1", "clock 1")
- .addClock("clock_2", "clock 2")
+ .addClock("clock_1")
+ .addClock("clock_2")
val lifecycle1 = spy(FakeLifecycle("1", plugin1))
val plugin2 = FakeClockPlugin()
- .addClock("clock_3", "clock 3", { mockClock })
- .addClock("clock_4", "clock 4")
+ .addClock("clock_3", { mockClock })
+ .addClock("clock_4")
val lifecycle2 = spy(FakeLifecycle("2", plugin2))
registry.applySettings(ClockSettings("clock_3", null))
@@ -268,13 +267,13 @@
@Test
fun activeClockId_changeAfterPluginConnected() {
val plugin1 = FakeClockPlugin()
- .addClock("clock_1", "clock 1")
- .addClock("clock_2", "clock 2")
+ .addClock("clock_1")
+ .addClock("clock_2")
val lifecycle1 = spy(FakeLifecycle("1", plugin1))
val plugin2 = FakeClockPlugin()
- .addClock("clock_3", "clock 3", { mockClock })
- .addClock("clock_4", "clock 4")
+ .addClock("clock_3", { mockClock })
+ .addClock("clock_4")
val lifecycle2 = spy(FakeLifecycle("2", plugin2))
registry.applySettings(ClockSettings("clock_3", null))
@@ -289,13 +288,13 @@
@Test
fun createDefaultClock_pluginDisconnected() {
val plugin1 = FakeClockPlugin()
- .addClock("clock_1", "clock 1")
- .addClock("clock_2", "clock 2")
+ .addClock("clock_1")
+ .addClock("clock_2")
val lifecycle1 = spy(FakeLifecycle("1", plugin1))
val plugin2 = FakeClockPlugin()
- .addClock("clock_3", "clock 3")
- .addClock("clock_4", "clock 4")
+ .addClock("clock_3")
+ .addClock("clock_4")
val lifecycle2 = spy(FakeLifecycle("2", plugin2))
registry.applySettings(ClockSettings("clock_3", null))
@@ -310,13 +309,13 @@
@Test
fun pluginRemoved_clockAndListChanged() {
val plugin1 = FakeClockPlugin()
- .addClock("clock_1", "clock 1")
- .addClock("clock_2", "clock 2")
+ .addClock("clock_1")
+ .addClock("clock_2")
val lifecycle1 = spy(FakeLifecycle("1", plugin1))
val plugin2 = FakeClockPlugin()
- .addClock("clock_3", "clock 3", { mockClock })
- .addClock("clock_4", "clock 4")
+ .addClock("clock_3", { mockClock })
+ .addClock("clock_4")
val lifecycle2 = spy(FakeLifecycle("2", plugin2))
var changeCallCount = 0
@@ -415,13 +414,13 @@
@Test
fun pluginAddRemove_concurrentModification() {
- val plugin1 = FakeClockPlugin().addClock("clock_1", "clock 1")
+ val plugin1 = FakeClockPlugin().addClock("clock_1")
val lifecycle1 = FakeLifecycle("1", plugin1)
- val plugin2 = FakeClockPlugin().addClock("clock_2", "clock 2")
+ val plugin2 = FakeClockPlugin().addClock("clock_2")
val lifecycle2 = FakeLifecycle("2", plugin2)
- val plugin3 = FakeClockPlugin().addClock("clock_3", "clock 3")
+ val plugin3 = FakeClockPlugin().addClock("clock_3")
val lifecycle3 = FakeLifecycle("3", plugin3)
- val plugin4 = FakeClockPlugin().addClock("clock_4", "clock 4")
+ val plugin4 = FakeClockPlugin().addClock("clock_4")
val lifecycle4 = FakeLifecycle("4", plugin4)
// Set the current clock to the final clock to load
@@ -450,10 +449,10 @@
// Verify all plugins were correctly loaded into the registry
assertEquals(registry.getClocks().toSet(), setOf(
- ClockMetadata("DEFAULT", "Default Clock"),
- ClockMetadata("clock_2", "clock 2"),
- ClockMetadata("clock_3", "clock 3"),
- ClockMetadata("clock_4", "clock 4")
+ ClockMetadata("DEFAULT"),
+ ClockMetadata("clock_2"),
+ ClockMetadata("clock_3"),
+ ClockMetadata("clock_4")
))
}
@@ -527,8 +526,8 @@
featureFlags.set(TRANSIT_CLOCK, flag)
registry.isTransitClockEnabled = featureFlags.isEnabled(TRANSIT_CLOCK)
val plugin = FakeClockPlugin()
- .addClock("clock_1", "clock 1")
- .addClock("DIGITAL_CLOCK_METRO", "metro clock")
+ .addClock("clock_1")
+ .addClock("DIGITAL_CLOCK_METRO")
val lifecycle = FakeLifecycle("metro", plugin)
pluginListener.onPluginLoaded(plugin, mockContext, lifecycle)
@@ -536,17 +535,17 @@
if (flag) {
assertEquals(
setOf(
- ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME),
- ClockMetadata("clock_1", "clock 1"),
- ClockMetadata("DIGITAL_CLOCK_METRO", "metro clock")
+ ClockMetadata(DEFAULT_CLOCK_ID),
+ ClockMetadata("clock_1"),
+ ClockMetadata("DIGITAL_CLOCK_METRO")
),
list.toSet()
)
} else {
assertEquals(
setOf(
- ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME),
- ClockMetadata("clock_1", "clock 1")
+ ClockMetadata(DEFAULT_CLOCK_ID),
+ ClockMetadata("clock_1")
),
list.toSet()
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt
index 7a2d122..b8fe2f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/smartspace/BcSmartspaceConfigProviderTest.kt
@@ -18,7 +18,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
@@ -33,7 +32,6 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class BcSmartspaceConfigProviderTest : SysuiTestCase() {
@Mock private lateinit var featureFlags: FeatureFlags
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
index f1c181f..e093859 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
@@ -26,7 +26,6 @@
import android.widget.FrameLayout
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dreams.smartspace.DreamSmartspaceController
import com.android.systemui.plugins.BcSmartspaceConfigPlugin
@@ -53,7 +52,6 @@
import org.mockito.Spy
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class DreamSmartspaceControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt
index 7af29ba..886c61a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt
@@ -25,7 +25,6 @@
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.settings.UserTracker
import com.android.systemui.smartspace.filters.LockscreenAndDreamTargetFilter
@@ -52,7 +51,6 @@
@SmallTest
@TestableLooper.RunWithLooper
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class LockscreenAndDreamTargetFilterTest : SysuiTestCase() {
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt
index a7c223d..0b5aea7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt
@@ -19,7 +19,6 @@
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.smartspace.preconditions.LockscreenPrecondition
import com.android.systemui.statusbar.policy.DeviceProvisionedController
@@ -36,7 +35,6 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class LockscreenPreconditionTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 14e58e5..e8923a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -5,41 +5,32 @@
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
+import com.android.SysUITestModule
+import com.android.TestMocksModule
import com.android.systemui.ExpandHelper
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
-import com.android.systemui.classifier.FalsingCollector
-import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.keyguard.WakefulnessLifecycle
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FakeFeatureFlagsClassicModule
+import com.android.systemui.flags.Flags
import com.android.systemui.media.controls.ui.MediaHierarchyManager
-import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.qs.QS
-import com.android.systemui.power.domain.interactor.PowerInteractorFactory
-import com.android.systemui.scene.SceneTestUtils
-import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
+import com.android.systemui.res.R
import com.android.systemui.shade.ShadeViewController
-import com.android.systemui.shade.data.repository.FakeShadeRepository
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.NotificationTestHelper
-import com.android.systemui.statusbar.notification.stack.AmbientState
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
-import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.phone.KeyguardBypassController
-import com.android.systemui.statusbar.phone.LSShadeTransitionLogger
import com.android.systemui.statusbar.phone.ScrimController
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository
import com.android.systemui.statusbar.policy.FakeConfigurationController
-import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.user.domain.UserDomainLayerModule
+import dagger.BindsInstance
+import dagger.Component
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import org.junit.After
import org.junit.Assert.assertFalse
@@ -60,9 +51,8 @@
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
-import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.junit.MockitoJUnit
import org.mockito.Mockito.`when` as whenever
+import org.mockito.junit.MockitoJUnit
private fun <T> anyObject(): T {
return Mockito.anyObject<T>()
@@ -73,68 +63,38 @@
@RunWith(AndroidTestingRunner::class)
@OptIn(ExperimentalCoroutinesApi::class)
class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
- private val utils = SceneTestUtils(this)
- private val testScope = utils.testScope
- lateinit var transitionController: LockscreenShadeTransitionController
+ private lateinit var testComponent: TestComponent
+
+ private val transitionController
+ get() = testComponent.transitionController
+ private val configurationController
+ get() = testComponent.configurationController
+ private val disableFlagsRepository
+ get() = testComponent.disableFlagsRepository
+ private val testScope
+ get() = testComponent.testScope
+
lateinit var row: ExpandableNotificationRow
- @Mock lateinit var statusbarStateController: SysuiStatusBarStateController
- @Mock lateinit var logger: LSShadeTransitionLogger
- @Mock lateinit var dumpManager: DumpManager
+
+ @Mock lateinit var centralSurfaces: CentralSurfaces
+ @Mock lateinit var depthController: NotificationShadeDepthController
+ @Mock lateinit var expandHelperCallback: ExpandHelper.Callback
@Mock lateinit var keyguardBypassController: KeyguardBypassController
@Mock lateinit var lockScreenUserManager: NotificationLockscreenUserManager
- @Mock lateinit var falsingCollector: FalsingCollector
- @Mock lateinit var ambientState: AmbientState
- @Mock lateinit var wakefulnessLifecycle: WakefulnessLifecycle
@Mock lateinit var mediaHierarchyManager: MediaHierarchyManager
- @Mock lateinit var scrimController: ScrimController
- @Mock lateinit var falsingManager: FalsingManager
- @Mock lateinit var shadeViewController: ShadeViewController
@Mock lateinit var nsslController: NotificationStackScrollLayoutController
- @Mock lateinit var depthController: NotificationShadeDepthController
- @Mock lateinit var stackscroller: NotificationStackScrollLayout
- @Mock lateinit var expandHelperCallback: ExpandHelper.Callback
- @Mock lateinit var mCentralSurfaces: CentralSurfaces
@Mock lateinit var qS: QS
- @Mock lateinit var singleShadeOverScroller: SingleShadeLockScreenOverScroller
- @Mock lateinit var splitShadeOverScroller: SplitShadeLockScreenOverScroller
- @Mock lateinit var qsTransitionController: LockscreenShadeQsTransitionController
- @Mock lateinit var activityStarter: ActivityStarter
+ @Mock lateinit var scrimController: ScrimController
+ @Mock lateinit var shadeViewController: ShadeViewController
+ @Mock lateinit var stackscroller: NotificationStackScrollLayout
+ @Mock lateinit var statusbarStateController: SysuiStatusBarStateController
@Mock lateinit var transitionControllerCallback: LockscreenShadeTransitionController.Callback
- private val sceneContainerFlags = FakeSceneContainerFlags()
- private val sceneInteractor = utils.sceneInteractor()
- private val disableFlagsRepository = FakeDisableFlagsRepository()
- private val keyguardRepository = FakeKeyguardRepository()
- private val configurationRepository = FakeConfigurationRepository()
- private val sharedNotificationContainerInteractor = SharedNotificationContainerInteractor(
- configurationRepository,
- mContext,
- ResourcesSplitShadeStateController()
- )
- private val shadeInteractor =
- ShadeInteractor(
- testScope.backgroundScope,
- disableFlagsRepository,
- sceneContainerFlags,
- { sceneInteractor },
- keyguardRepository,
- userSetupRepository = FakeUserSetupRepository(),
- deviceProvisionedController = mock(),
- userInteractor = mock(),
- sharedNotificationContainerInteractor,
- repository = FakeShadeRepository(),
- )
- private val powerInteractor = PowerInteractorFactory.create().powerInteractor
- @JvmField @Rule val mockito = MockitoJUnit.rule()
- private val configurationController = FakeConfigurationController()
+ @JvmField @Rule val mockito = MockitoJUnit.rule()
@Before
fun setup() {
- // By default, have the shade enabled
- disableFlagsRepository.disableFlags.value = DisableFlagsModel()
- testScope.runCurrent()
-
val helper = NotificationTestHelper(mContext, mDependency, TestableLooper.get(this))
row = helper.createRow()
context
@@ -143,55 +103,9 @@
context
.getOrCreateTestableResources()
.addOverride(R.dimen.lockscreen_shade_depth_controller_transition_distance, 100)
- transitionController =
- LockscreenShadeTransitionController(
- statusBarStateController = statusbarStateController,
- logger = logger,
- keyguardBypassController = keyguardBypassController,
- lockScreenUserManager = lockScreenUserManager,
- falsingCollector = falsingCollector,
- ambientState = ambientState,
- mediaHierarchyManager = mediaHierarchyManager,
- depthController = depthController,
- wakefulnessLifecycle = wakefulnessLifecycle,
- context = context,
- configurationController = configurationController,
- falsingManager = falsingManager,
- dumpManager = dumpManager,
- splitShadeOverScrollerFactory = { _, _ -> splitShadeOverScroller },
- singleShadeOverScrollerFactory = { singleShadeOverScroller },
- scrimTransitionController =
- LockscreenShadeScrimTransitionController(
- scrimController,
- context,
- configurationController,
- dumpManager,
- ResourcesSplitShadeStateController()
- ),
- keyguardTransitionControllerFactory = { notificationPanelController ->
- LockscreenShadeKeyguardTransitionController(
- mediaHierarchyManager,
- notificationPanelController,
- context,
- configurationController,
- dumpManager,
- ResourcesSplitShadeStateController()
- )
- },
- qsTransitionControllerFactory = { qsTransitionController },
- activityStarter = activityStarter,
- shadeRepository = FakeShadeRepository(),
- shadeInteractor = shadeInteractor,
- powerInteractor = powerInteractor,
- splitShadeStateController = ResourcesSplitShadeStateController()
- )
- transitionController.addCallback(transitionControllerCallback)
+
whenever(nsslController.view).thenReturn(stackscroller)
whenever(nsslController.expandHelperCallback).thenReturn(expandHelperCallback)
- transitionController.shadeViewController = shadeViewController
- transitionController.centralSurfaces = mCentralSurfaces
- transitionController.qS = qS
- transitionController.setStackScroller(nsslController)
whenever(statusbarStateController.state).thenReturn(StatusBarState.KEYGUARD)
whenever(nsslController.isInLockedDownShade).thenReturn(false)
whenever(qS.isFullyCollapsed).thenReturn(true)
@@ -199,9 +113,36 @@
.thenReturn(true)
whenever(lockScreenUserManager.shouldShowLockscreenNotifications()).thenReturn(true)
whenever(lockScreenUserManager.isLockscreenPublicMode(anyInt())).thenReturn(true)
- whenever(falsingCollector.shouldEnforceBouncer()).thenReturn(false)
whenever(keyguardBypassController.bypassEnabled).thenReturn(false)
- clearInvocations(mCentralSurfaces)
+
+ testComponent =
+ DaggerLockscreenShadeTransitionControllerTest_TestComponent.factory()
+ .create(
+ test = this,
+ featureFlags =
+ FakeFeatureFlagsClassicModule {
+ set(Flags.FULL_SCREEN_USER_SWITCHER, false)
+ },
+ mocks =
+ TestMocksModule(
+ notificationShadeDepthController = depthController,
+ keyguardBypassController = keyguardBypassController,
+ mediaHierarchyManager = mediaHierarchyManager,
+ notificationLockscreenUserManager = lockScreenUserManager,
+ notificationStackScrollLayoutController = nsslController,
+ scrimController = scrimController,
+ statusBarStateController = statusbarStateController,
+ )
+ )
+
+ transitionController.addCallback(transitionControllerCallback)
+ transitionController.shadeViewController = shadeViewController
+ transitionController.centralSurfaces = centralSurfaces
+ transitionController.qS = qS
+ transitionController.setStackScroller(nsslController)
+ clearInvocations(centralSurfaces)
+
+ testScope.runCurrent()
}
@After
@@ -282,7 +223,7 @@
transitionController.goToLockedShade(null)
verify(statusbarStateController, never()).setState(anyInt())
verify(statusbarStateController).setLeaveOpenOnKeyguardHide(true)
- verify(mCentralSurfaces).showBouncerWithDimissAndCancelIfKeyguard(anyObject(), anyObject())
+ verify(centralSurfaces).showBouncerWithDimissAndCancelIfKeyguard(anyObject(), anyObject())
}
@Test
@@ -318,7 +259,7 @@
verify(scrimController, never()).setTransitionToFullShadeProgress(anyFloat(), anyFloat())
verify(transitionControllerCallback, never())
.setTransitionToFullShadeAmount(anyFloat(), anyBoolean(), anyLong())
- verify(qsTransitionController, never()).dragDownAmount = anyFloat()
+ verify(qS, never()).setTransitionToFullShadeProgress(anyBoolean(), anyFloat(), anyFloat())
}
@Test
@@ -329,7 +270,7 @@
verify(scrimController).setTransitionToFullShadeProgress(anyFloat(), anyFloat())
verify(transitionControllerCallback)
.setTransitionToFullShadeAmount(anyFloat(), anyBoolean(), anyLong())
- verify(qsTransitionController).dragDownAmount = 10f
+ verify(qS).setTransitionToFullShadeProgress(eq(true), anyFloat(), anyFloat())
verify(depthController).transitionToFullShadeProgress = anyFloat()
}
@@ -532,8 +473,8 @@
transitionController.dragDownAmount = 10f
- verify(singleShadeOverScroller).expansionDragDownAmount = 10f
- verifyZeroInteractions(splitShadeOverScroller)
+ verify(nsslController).setOverScrollAmount(0)
+ verify(scrimController, never()).setNotificationsOverScrollAmount(anyInt())
}
@Test
@@ -542,8 +483,8 @@
transitionController.dragDownAmount = 10f
- verify(splitShadeOverScroller).expansionDragDownAmount = 10f
- verifyZeroInteractions(singleShadeOverScroller)
+ verify(nsslController).setOverScrollAmount(0)
+ verify(scrimController).setNotificationsOverScrollAmount(0)
}
@Test
@@ -591,6 +532,32 @@
progress: Float,
lockScreenNotificationsProgress: Float
) {
- scrimController.setTransitionToFullShadeProgress(progress, lockScreenNotificationsProgress)
+ setTransitionToFullShadeProgress(progress, lockScreenNotificationsProgress)
+ }
+
+ @SysUISingleton
+ @Component(
+ modules =
+ [
+ SysUITestModule::class,
+ UserDomainLayerModule::class,
+ ]
+ )
+ interface TestComponent {
+
+ val transitionController: LockscreenShadeTransitionController
+
+ val configurationController: FakeConfigurationController
+ val disableFlagsRepository: FakeDisableFlagsRepository
+ val testScope: TestScope
+
+ @Component.Factory
+ interface Factory {
+ fun create(
+ @BindsInstance test: SysuiTestCase,
+ featureFlags: FakeFeatureFlagsClassicModule,
+ mocks: TestMocksModule,
+ ): TestComponent
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java
new file mode 100644
index 0000000..3a9c24a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java
@@ -0,0 +1,597 @@
+/*
+ * 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;
+
+import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
+import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS;
+import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS;
+import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS;
+import static android.provider.Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.clearInvocations;
+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.KeyguardManager;
+import android.app.Notification;
+import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.UserInfo;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.systemui.Dependency;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FakeFeatureFlagsClassic;
+import com.android.systemui.flags.Flags;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.NotificationStateChangedListener;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
+import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.settings.FakeSettings;
+
+import com.google.android.collect.Lists;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.concurrent.Executor;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class NotificationLockscreenUserManagerMainThreadTest extends SysuiTestCase {
+ @Mock
+ private NotificationPresenter mPresenter;
+ @Mock
+ private UserManager mUserManager;
+ @Mock
+ private UserTracker mUserTracker;
+
+ // Dependency mocks:
+ @Mock
+ private NotificationVisibilityProvider mVisibilityProvider;
+ @Mock
+ private CommonNotifCollection mNotifCollection;
+ @Mock
+ private DevicePolicyManager mDevicePolicyManager;
+ @Mock
+ private NotificationClickNotifier mClickNotifier;
+ @Mock
+ private OverviewProxyService mOverviewProxyService;
+ @Mock
+ private KeyguardManager mKeyguardManager;
+ @Mock
+ private DeviceProvisionedController mDeviceProvisionedController;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
+ @Mock
+ private BroadcastDispatcher mBroadcastDispatcher;
+ @Mock
+ private KeyguardStateController mKeyguardStateController;
+
+ private UserInfo mCurrentUser;
+ private UserInfo mSecondaryUser;
+ private UserInfo mWorkUser;
+ private FakeSettings mSettings;
+ private TestNotificationLockscreenUserManager mLockscreenUserManager;
+ private NotificationEntry mCurrentUserNotif;
+ private NotificationEntry mSecondaryUserNotif;
+ private NotificationEntry mWorkProfileNotif;
+ private final FakeFeatureFlagsClassic mFakeFeatureFlags = new FakeFeatureFlagsClassic();
+ private Executor mBackgroundExecutor = Runnable::run; // Direct executor
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mFakeFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, false);
+
+ int currentUserId = ActivityManager.getCurrentUser();
+ when(mUserTracker.getUserId()).thenReturn(currentUserId);
+ mSettings = new FakeSettings();
+ mSettings.setUserId(ActivityManager.getCurrentUser());
+ mCurrentUser = new UserInfo(currentUserId, "", 0);
+ mSecondaryUser = new UserInfo(currentUserId + 1, "", 0);
+ mWorkUser = new UserInfo(currentUserId + 2, "" /* name */, null /* iconPath */, 0,
+ UserManager.USER_TYPE_PROFILE_MANAGED);
+
+ when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(true);
+ when(mUserManager.getProfiles(currentUserId)).thenReturn(Lists.newArrayList(
+ mCurrentUser, mWorkUser));
+ when(mUserManager.getProfiles(mSecondaryUser.id)).thenReturn(Lists.newArrayList(
+ mSecondaryUser));
+ mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
+ Handler.createAsync(Looper.myLooper()));
+
+ Notification notifWithPrivateVisibility = new Notification();
+ notifWithPrivateVisibility.visibility = Notification.VISIBILITY_PRIVATE;
+ mCurrentUserNotif = new NotificationEntryBuilder()
+ .setNotification(notifWithPrivateVisibility)
+ .setUser(new UserHandle(mCurrentUser.id))
+ .build();
+ mSecondaryUserNotif = new NotificationEntryBuilder()
+ .setNotification(notifWithPrivateVisibility)
+ .setUser(new UserHandle(mSecondaryUser.id))
+ .build();
+ mWorkProfileNotif = new NotificationEntryBuilder()
+ .setNotification(notifWithPrivateVisibility)
+ .setUser(new UserHandle(mWorkUser.id))
+ .build();
+
+ mLockscreenUserManager = new TestNotificationLockscreenUserManager(mContext);
+ mLockscreenUserManager.setUpWithPresenter(mPresenter);
+ }
+
+ private void changeSetting(String setting) {
+ final Collection<Uri> lockScreenUris = new ArrayList<>();
+ lockScreenUris.add(Settings.Secure.getUriFor(setting));
+ mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false,
+ lockScreenUris, 0);
+ }
+
+ @Test
+ public void testLockScreenShowNotificationsFalse() {
+ mSettings.putInt(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
+ assertFalse(mLockscreenUserManager.shouldShowLockscreenNotifications());
+ }
+
+ @Test
+ public void testLockScreenShowNotificationsTrue() {
+ mSettings.putInt(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
+ assertTrue(mLockscreenUserManager.shouldShowLockscreenNotifications());
+ }
+
+ @Test
+ public void testLockScreenAllowPrivateNotificationsTrue() {
+ mSettings.putInt(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+ assertTrue(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUser.id));
+ }
+
+ @Test
+ public void testLockScreenAllowPrivateNotificationsFalse() {
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+ mCurrentUser.id);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+ assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUser.id));
+ }
+
+ @Test
+ public void testLockScreenAllowsWorkPrivateNotificationsFalse() {
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+ mWorkUser.id);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+ assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mWorkUser.id));
+ }
+
+ @Test
+ public void testLockScreenAllowsWorkPrivateNotificationsTrue() {
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+ mWorkUser.id);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+ assertTrue(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mWorkUser.id));
+ }
+
+ @Test
+ public void testCurrentUserPrivateNotificationsNotRedacted() {
+ // GIVEN current user doesn't allow private notifications to show
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+ mCurrentUser.id);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+
+ // THEN current user's notification is redacted
+ assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
+ }
+
+ @Test
+ public void testCurrentUserPrivateNotificationsRedacted() {
+ // GIVEN current user allows private notifications to show
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+ mCurrentUser.id);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+
+ // THEN current user's notification isn't redacted
+ assertFalse(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
+ }
+
+ @Test
+ public void testWorkPrivateNotificationsRedacted() {
+ // GIVEN work profile doesn't private notifications to show
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+ mWorkUser.id);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+
+ // THEN work profile notification is redacted
+ assertTrue(mLockscreenUserManager.needsRedaction(mWorkProfileNotif));
+ assertFalse(mLockscreenUserManager.allowsManagedPrivateNotificationsInPublic());
+ }
+
+ @Test
+ public void testWorkPrivateNotificationsNotRedacted() {
+ // GIVEN work profile allows private notifications to show
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+ mWorkUser.id);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+
+ // THEN work profile notification isn't redacted
+ assertFalse(mLockscreenUserManager.needsRedaction(mWorkProfileNotif));
+ assertTrue(mLockscreenUserManager.allowsManagedPrivateNotificationsInPublic());
+ }
+
+ @Test
+ public void testWorkPrivateNotificationsNotRedacted_otherUsersRedacted() {
+ // GIVEN work profile allows private notifications to show but the other users don't
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+ mWorkUser.id);
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+ mCurrentUser.id);
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+ mSecondaryUser.id);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+
+ // THEN the work profile notification doesn't need to be redacted
+ assertFalse(mLockscreenUserManager.needsRedaction(mWorkProfileNotif));
+
+ // THEN the current user and secondary user notifications do need to be redacted
+ assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
+ assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
+ }
+
+ @Test
+ public void testWorkProfileRedacted_otherUsersNotRedacted() {
+ // GIVEN work profile doesn't allow private notifications to show but the other users do
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+ mWorkUser.id);
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+ mCurrentUser.id);
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+ mSecondaryUser.id);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+
+ // THEN the work profile notification needs to be redacted
+ assertTrue(mLockscreenUserManager.needsRedaction(mWorkProfileNotif));
+
+ // THEN the current user and secondary user notifications don't need to be redacted
+ assertFalse(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
+ assertFalse(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
+ }
+
+ @Test
+ public void testSecondaryUserNotRedacted_currentUserRedacted() {
+ // GIVEN secondary profile allows private notifications to show but the current user
+ // doesn't allow private notifications to show
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+ mCurrentUser.id);
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+ mSecondaryUser.id);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+
+ // THEN the secondary profile notification still needs to be redacted because the current
+ // user's setting takes precedence
+ assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
+ }
+
+ @Test
+ public void testUserSwitchedCallsOnUserSwitching() {
+ mLockscreenUserManager.getUserTrackerCallbackForTest().onUserChanging(mSecondaryUser.id,
+ mContext);
+ verify(mPresenter, times(1)).onUserSwitched(mSecondaryUser.id);
+ }
+
+ @Test
+ public void testIsLockscreenPublicMode() {
+ assertFalse(mLockscreenUserManager.isLockscreenPublicMode(mCurrentUser.id));
+ mLockscreenUserManager.setLockscreenPublicMode(true, mCurrentUser.id);
+ assertTrue(mLockscreenUserManager.isLockscreenPublicMode(mCurrentUser.id));
+ }
+
+ @Test
+ public void testUpdateIsPublicMode() {
+ when(mKeyguardStateController.isMethodSecure()).thenReturn(true);
+
+ NotificationStateChangedListener listener = mock(NotificationStateChangedListener.class);
+ mLockscreenUserManager.addNotificationStateChangedListener(listener);
+ mLockscreenUserManager.mCurrentProfiles.append(0, mock(UserInfo.class));
+
+ // first call explicitly sets user 0 to not public; notifies
+ mLockscreenUserManager.updatePublicMode();
+ TestableLooper.get(this).processAllMessages();
+ assertFalse(mLockscreenUserManager.isLockscreenPublicMode(0));
+ verify(listener).onNotificationStateChanged();
+ clearInvocations(listener);
+
+ // calling again has no changes; does not notify
+ mLockscreenUserManager.updatePublicMode();
+ TestableLooper.get(this).processAllMessages();
+ assertFalse(mLockscreenUserManager.isLockscreenPublicMode(0));
+ verify(listener, never()).onNotificationStateChanged();
+
+ // Calling again with keyguard now showing makes user 0 public; notifies
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ mLockscreenUserManager.updatePublicMode();
+ TestableLooper.get(this).processAllMessages();
+ assertTrue(mLockscreenUserManager.isLockscreenPublicMode(0));
+ verify(listener).onNotificationStateChanged();
+ clearInvocations(listener);
+
+ // calling again has no changes; does not notify
+ mLockscreenUserManager.updatePublicMode();
+ TestableLooper.get(this).processAllMessages();
+ assertTrue(mLockscreenUserManager.isLockscreenPublicMode(0));
+ verify(listener, never()).onNotificationStateChanged();
+ }
+
+ @Test
+ public void testDevicePolicyDoesNotAllowNotifications() {
+ // User allows them
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
+
+ // DevicePolicy hides notifs on lockscreen
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
+ .thenReturn(KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
+
+ BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
+ 0, null, null, 0, true, false, null, mCurrentUser.id, 0);
+ mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
+ mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
+ new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
+
+ assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
+ }
+
+ @Test
+ public void testDevicePolicyDoesNotAllowNotifications_secondary() {
+ Mockito.clearInvocations(mDevicePolicyManager);
+ // User allows notifications
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mSecondaryUser.id);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
+
+ // DevicePolicy hides notifications
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mSecondaryUser.id))
+ .thenReturn(KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
+
+ BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
+ 0, null, null, 0, true, false, null, mSecondaryUser.id, 0);
+ mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
+ mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
+ new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
+
+ assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id));
+ }
+
+ @Test
+ public void testDevicePolicy_noPrivateNotifications() {
+ Mockito.clearInvocations(mDevicePolicyManager);
+ // User allows notifications
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
+
+ // DevicePolicy hides sensitive content
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
+ .thenReturn(KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
+
+ BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
+ 0, null, null, 0, true, false, null, mCurrentUser.id, 0);
+ mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
+ mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
+ new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
+
+ assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
+ }
+
+ @Test
+ public void testDevicePolicy_noPrivateNotifications_userAll() {
+ // User allows notifications
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
+
+ // DevicePolicy hides sensitive content
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
+ .thenReturn(KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
+
+ BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
+ 0, null, null, 0, true, false, null, mCurrentUser.id, 0);
+ mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
+ mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
+ new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
+
+ assertTrue(mLockscreenUserManager.needsRedaction(new NotificationEntryBuilder()
+ .setNotification(new Notification())
+ .setUser(UserHandle.ALL)
+ .build()));
+ }
+
+ @Test
+ public void testDevicePolicyPrivateNotifications_secondary() {
+ Mockito.clearInvocations(mDevicePolicyManager);
+ // User allows notifications
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mSecondaryUser.id);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
+
+ // DevicePolicy hides sensitive content
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mSecondaryUser.id))
+ .thenReturn(KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
+
+ BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
+ 0, null, null, 0, true, false, null, mSecondaryUser.id, 0);
+ mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
+ mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
+ new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
+
+ mLockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext);
+ assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
+ }
+
+ @Test
+ public void testHideNotifications_primary() {
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mCurrentUser.id);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
+
+ assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
+ }
+
+ @Test
+ public void testHideNotifications_secondary() {
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mSecondaryUser.id);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
+
+ assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id));
+ }
+
+ @Test
+ public void testHideNotifications_secondary_userSwitch() {
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mSecondaryUser.id);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
+
+ mLockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext);
+
+ assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id));
+ }
+
+ @Test
+ public void testShowNotifications_secondary_userSwitch() {
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mSecondaryUser.id);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
+
+ mLockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext);
+
+ assertTrue(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id));
+ }
+
+ @Test
+ public void testUserAllowsNotificationsInPublic_keyguardManagerNoPrivateNotifications() {
+ // DevicePolicy allows notifications
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
+ .thenReturn(0);
+ BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
+ 0, null, null, 0, true, false, null, mCurrentUser.id, 0);
+ mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
+ mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
+ new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
+
+ // KeyguardManager does not allow notifications
+ when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(false);
+
+ // User allows notifications
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
+ // We shouldn't need to call this method, but getPrivateNotificationsAllowed has no
+ // callback, so it's only updated when the setting is
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
+
+ assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
+ }
+
+ @Test
+ public void testUserAllowsNotificationsInPublic_settingsChange() {
+ // User allows notifications
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
+
+ assertTrue(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
+
+ // User disables
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mCurrentUser.id);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
+
+ assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
+ }
+
+ private class TestNotificationLockscreenUserManager
+ extends NotificationLockscreenUserManagerImpl {
+ public TestNotificationLockscreenUserManager(Context context) {
+ super(
+ context,
+ mBroadcastDispatcher,
+ mDevicePolicyManager,
+ mUserManager,
+ mUserTracker,
+ (() -> mVisibilityProvider),
+ (() -> mNotifCollection),
+ mClickNotifier,
+ (() -> mOverviewProxyService),
+ NotificationLockscreenUserManagerMainThreadTest.this.mKeyguardManager,
+ mStatusBarStateController,
+ Handler.createAsync(Looper.myLooper()),
+ Handler.createAsync(Looper.myLooper()),
+ mBackgroundExecutor,
+ mDeviceProvisionedController,
+ mKeyguardStateController,
+ mSettings,
+ mock(DumpManager.class),
+ mock(LockPatternUtils.class),
+ mFakeFeatureFlags);
+ }
+
+ public BroadcastReceiver getBaseBroadcastReceiverForTest() {
+ return mBaseBroadcastReceiver;
+ }
+
+ public UserTracker.Callback getUserTrackerCallbackForTest() {
+ return mUserChangedCallback;
+ }
+
+ public ContentObserver getLockscreenSettingsObserverForTest() {
+ return mLockscreenSettingsObserver;
+ }
+
+ public ContentObserver getSettingsObserverForTest() {
+ return mSettingsObserver;
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 19863ec..a5f5fc7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -16,17 +16,23 @@
package com.android.systemui.statusbar;
+import static android.app.Notification.VISIBILITY_PRIVATE;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.VISIBILITY_NO_OVERRIDE;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS;
import static android.os.UserHandle.USER_ALL;
+import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS;
import static android.provider.Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS;
import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atMost;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
@@ -38,12 +44,15 @@
import android.app.ActivityManager;
import android.app.KeyguardManager;
import android.app.Notification;
+import android.app.NotificationChannel;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
@@ -59,6 +68,8 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FakeFeatureFlagsClassic;
+import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.settings.UserTracker;
@@ -74,12 +85,16 @@
import com.google.android.collect.Lists;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.concurrent.Executor;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -121,11 +136,15 @@
private NotificationEntry mCurrentUserNotif;
private NotificationEntry mSecondaryUserNotif;
private NotificationEntry mWorkProfileNotif;
+ private final FakeFeatureFlagsClassic mFakeFeatureFlags = new FakeFeatureFlagsClassic();
+ private Executor mBackgroundExecutor = Runnable::run; // Direct executor
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mFakeFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true);
+
int currentUserId = ActivityManager.getCurrentUser();
when(mUserTracker.getUserId()).thenReturn(currentUserId);
mSettings = new FakeSettings();
@@ -138,103 +157,144 @@
when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(true);
when(mUserManager.getProfiles(currentUserId)).thenReturn(Lists.newArrayList(
mCurrentUser, mWorkUser));
+ when(mUserManager.getUsers()).thenReturn(Lists.newArrayList(
+ mCurrentUser, mWorkUser, mSecondaryUser));
when(mUserManager.getProfiles(mSecondaryUser.id)).thenReturn(Lists.newArrayList(
mSecondaryUser));
mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
Handler.createAsync(Looper.myLooper()));
Notification notifWithPrivateVisibility = new Notification();
- notifWithPrivateVisibility.visibility = Notification.VISIBILITY_PRIVATE;
+ notifWithPrivateVisibility.visibility = VISIBILITY_PRIVATE;
mCurrentUserNotif = new NotificationEntryBuilder()
.setNotification(notifWithPrivateVisibility)
.setUser(new UserHandle(mCurrentUser.id))
.build();
+ NotificationChannel channel = new NotificationChannel("1", "1", IMPORTANCE_HIGH);
+ channel.setLockscreenVisibility(VISIBILITY_NO_OVERRIDE);
+ mCurrentUserNotif.setRanking(new RankingBuilder(mCurrentUserNotif.getRanking())
+ .setChannel(channel)
+ .setVisibilityOverride(VISIBILITY_NO_OVERRIDE).build());
+ when(mNotifCollection.getEntry(mCurrentUserNotif.getKey())).thenReturn(mCurrentUserNotif);
mSecondaryUserNotif = new NotificationEntryBuilder()
.setNotification(notifWithPrivateVisibility)
.setUser(new UserHandle(mSecondaryUser.id))
.build();
+ mSecondaryUserNotif.setRanking(new RankingBuilder(mSecondaryUserNotif.getRanking())
+ .setChannel(channel)
+ .setVisibilityOverride(VISIBILITY_NO_OVERRIDE).build());
+ when(mNotifCollection.getEntry(
+ mSecondaryUserNotif.getKey())).thenReturn(mSecondaryUserNotif);
mWorkProfileNotif = new NotificationEntryBuilder()
.setNotification(notifWithPrivateVisibility)
.setUser(new UserHandle(mWorkUser.id))
.build();
+ mWorkProfileNotif.setRanking(new RankingBuilder(mWorkProfileNotif.getRanking())
+ .setChannel(channel)
+ .setVisibilityOverride(VISIBILITY_NO_OVERRIDE).build());
+ when(mNotifCollection.getEntry(mWorkProfileNotif.getKey())).thenReturn(mWorkProfileNotif);
mLockscreenUserManager = new TestNotificationLockscreenUserManager(mContext);
mLockscreenUserManager.setUpWithPresenter(mPresenter);
}
+ private void changeSetting(String setting) {
+ final Collection<Uri> lockScreenUris = new ArrayList<>();
+ lockScreenUris.add(Settings.Secure.getUriFor(setting));
+ mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false,
+ lockScreenUris, 0);
+ }
+
@Test
public void testLockScreenShowNotificationsFalse() {
mSettings.putInt(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
assertFalse(mLockscreenUserManager.shouldShowLockscreenNotifications());
}
@Test
public void testLockScreenShowNotificationsTrue() {
mSettings.putInt(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
assertTrue(mLockscreenUserManager.shouldShowLockscreenNotifications());
}
@Test
public void testLockScreenAllowPrivateNotificationsTrue() {
- mSettings.putInt(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1);
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+ mSettings.putInt(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
assertTrue(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUser.id));
}
@Test
public void testLockScreenAllowPrivateNotificationsFalse() {
- mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
mCurrentUser.id);
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUser.id));
}
@Test
public void testLockScreenAllowsWorkPrivateNotificationsFalse() {
- mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
mWorkUser.id);
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mWorkUser.id));
}
@Test
public void testLockScreenAllowsWorkPrivateNotificationsTrue() {
- mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
mWorkUser.id);
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
assertTrue(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mWorkUser.id));
}
@Test
- public void testCurrentUserPrivateNotificationsNotRedacted() {
+ public void testCurrentUserPrivateNotificationsRedacted() {
// GIVEN current user doesn't allow private notifications to show
- mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
mCurrentUser.id);
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
// THEN current user's notification is redacted
assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
}
@Test
- public void testCurrentUserPrivateNotificationsRedacted() {
+ public void testCurrentUserPrivateNotificationsNotRedacted() {
// GIVEN current user allows private notifications to show
- mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
mCurrentUser.id);
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
// THEN current user's notification isn't redacted
assertFalse(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
}
@Test
+ public void testCurrentUserPrivateNotificationsRedactedChannel() {
+ // GIVEN current user allows private notifications to show
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+ mCurrentUser.id);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+
+ // but doesn't allow it at the channel level
+ NotificationChannel channel = new NotificationChannel("1", "1", IMPORTANCE_HIGH);
+ channel.setLockscreenVisibility(VISIBILITY_PRIVATE);
+ mCurrentUserNotif.setRanking(new RankingBuilder(mCurrentUserNotif.getRanking())
+ .setChannel(channel)
+ .setVisibilityOverride(VISIBILITY_NO_OVERRIDE).build());
+ // THEN the notification is redacted
+ assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
+ }
+
+ @Test
public void testWorkPrivateNotificationsRedacted() {
// GIVEN work profile doesn't private notifications to show
- mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
mWorkUser.id);
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
// THEN work profile notification is redacted
assertTrue(mLockscreenUserManager.needsRedaction(mWorkProfileNotif));
@@ -244,9 +304,9 @@
@Test
public void testWorkPrivateNotificationsNotRedacted() {
// GIVEN work profile allows private notifications to show
- mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
mWorkUser.id);
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
// THEN work profile notification isn't redacted
assertFalse(mLockscreenUserManager.needsRedaction(mWorkProfileNotif));
@@ -256,13 +316,15 @@
@Test
public void testWorkPrivateNotificationsNotRedacted_otherUsersRedacted() {
// GIVEN work profile allows private notifications to show but the other users don't
- mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
mWorkUser.id);
- mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
mCurrentUser.id);
- mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
mSecondaryUser.id);
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
// THEN the work profile notification doesn't need to be redacted
assertFalse(mLockscreenUserManager.needsRedaction(mWorkProfileNotif));
@@ -275,13 +337,15 @@
@Test
public void testWorkProfileRedacted_otherUsersNotRedacted() {
// GIVEN work profile doesn't allow private notifications to show but the other users do
- mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
mWorkUser.id);
- mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
mCurrentUser.id);
- mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
mSecondaryUser.id);
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
// THEN the work profile notification needs to be redacted
assertTrue(mLockscreenUserManager.needsRedaction(mWorkProfileNotif));
@@ -295,11 +359,12 @@
public void testSecondaryUserNotRedacted_currentUserRedacted() {
// GIVEN secondary profile allows private notifications to show but the current user
// doesn't allow private notifications to show
- mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
mCurrentUser.id);
- mSettings.putIntForUser(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
mSecondaryUser.id);
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+ changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
// THEN the secondary profile notification still needs to be redacted because the current
// user's setting takes precedence
@@ -323,6 +388,7 @@
@Test
public void testUpdateIsPublicMode() {
when(mKeyguardStateController.isMethodSecure()).thenReturn(true);
+ when(mKeyguardStateController.isShowing()).thenReturn(false);
NotificationStateChangedListener listener = mock(NotificationStateChangedListener.class);
mLockscreenUserManager.addNotificationStateChangedListener(listener);
@@ -330,24 +396,28 @@
// first call explicitly sets user 0 to not public; notifies
mLockscreenUserManager.updatePublicMode();
+ TestableLooper.get(this).processAllMessages();
assertFalse(mLockscreenUserManager.isLockscreenPublicMode(0));
verify(listener).onNotificationStateChanged();
clearInvocations(listener);
// calling again has no changes; does not notify
mLockscreenUserManager.updatePublicMode();
+ TestableLooper.get(this).processAllMessages();
assertFalse(mLockscreenUserManager.isLockscreenPublicMode(0));
verify(listener, never()).onNotificationStateChanged();
// Calling again with keyguard now showing makes user 0 public; notifies
when(mKeyguardStateController.isShowing()).thenReturn(true);
mLockscreenUserManager.updatePublicMode();
+ TestableLooper.get(this).processAllMessages();
assertTrue(mLockscreenUserManager.isLockscreenPublicMode(0));
verify(listener).onNotificationStateChanged();
clearInvocations(listener);
// calling again has no changes; does not notify
mLockscreenUserManager.updatePublicMode();
+ TestableLooper.get(this).processAllMessages();
assertTrue(mLockscreenUserManager.isLockscreenPublicMode(0));
verify(listener, never()).onNotificationStateChanged();
}
@@ -356,14 +426,14 @@
public void testDevicePolicyDoesNotAllowNotifications() {
// User allows them
mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
// DevicePolicy hides notifs on lockscreen
when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
.thenReturn(KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
- BroadcastReceiver.PendingResult pr = mock(BroadcastReceiver.PendingResult.class);
- when(pr.getSendingUserId()).thenReturn(mCurrentUser.id);
+ BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
+ 0, null, null, 0, true, false, null, mCurrentUser.id, 0);
mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
@@ -371,19 +441,18 @@
assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
}
- @Ignore("b/286230167")
@Test
public void testDevicePolicyDoesNotAllowNotifications_userAll() {
// User allows them
mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
// DevicePolicy hides notifications
when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
.thenReturn(KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
- BroadcastReceiver.PendingResult pr = mock(BroadcastReceiver.PendingResult.class);
- when(pr.getSendingUserId()).thenReturn(mCurrentUser.id);
+ BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
+ 0, null, null, 0, true, false, null, mCurrentUser.id, 0);
mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
@@ -392,98 +461,105 @@
}
@Test
- @Ignore("b/286230167")
public void testDevicePolicyDoesNotAllowNotifications_secondary() {
+ Mockito.clearInvocations(mDevicePolicyManager);
// User allows notifications
mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mSecondaryUser.id);
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
// DevicePolicy hides notifications
when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mSecondaryUser.id))
.thenReturn(KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
- BroadcastReceiver.PendingResult pr = mock(BroadcastReceiver.PendingResult.class);
- when(pr.getSendingUserId()).thenReturn(mSecondaryUser.id);
+ BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
+ 0, null, null, 0, true, false, null, mSecondaryUser.id, 0);
mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id));
- // TODO (b/286230167): enable assertion
verify(mDevicePolicyManager, atMost(1)).getKeyguardDisabledFeatures(any(), anyInt());
}
@Test
public void testDevicePolicy_noPrivateNotifications() {
+ Mockito.clearInvocations(mDevicePolicyManager);
// User allows notifications
mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
// DevicePolicy hides sensitive content
when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
.thenReturn(KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
- BroadcastReceiver.PendingResult pr = mock(BroadcastReceiver.PendingResult.class);
- when(pr.getSendingUserId()).thenReturn(mCurrentUser.id);
+ BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
+ 0, null, null, 0, true, false, null, mCurrentUser.id, 0);
mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
- // TODO (b/286230167): enable assertion. It's currently called 4 times.
- //verify(mDevicePolicyManager, atMost(1)).getKeyguardDisabledFeatures(any(), anyInt());
+ verify(mDevicePolicyManager, atMost(1)).getKeyguardDisabledFeatures(any(), anyInt());
}
@Test
public void testDevicePolicy_noPrivateNotifications_userAll() {
+ NotificationChannel channel = new NotificationChannel("1", "1", IMPORTANCE_HIGH);
+ channel.setLockscreenVisibility(VISIBILITY_NO_OVERRIDE);
+ NotificationEntry notifEntry = new NotificationEntryBuilder()
+ .setNotification(new Notification())
+ .setUser(UserHandle.ALL)
+ .build();
+ notifEntry.setRanking(new RankingBuilder(notifEntry.getRanking())
+ .setChannel(channel)
+ .setVisibilityOverride(VISIBILITY_NO_OVERRIDE).build());
+ when(mNotifCollection.getEntry(notifEntry.getKey())).thenReturn(notifEntry);
// User allows notifications
mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
// DevicePolicy hides sensitive content
when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
.thenReturn(KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
- BroadcastReceiver.PendingResult pr = mock(BroadcastReceiver.PendingResult.class);
- when(pr.getSendingUserId()).thenReturn(mCurrentUser.id);
+ BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
+ 0, null, null, 0, true, false, null, mCurrentUser.id, 0);
mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
- assertTrue(mLockscreenUserManager.needsRedaction(new NotificationEntryBuilder()
- .setNotification(new Notification())
- .setUser(UserHandle.ALL)
- .build()));
+ assertTrue(mLockscreenUserManager.needsRedaction(notifEntry));
}
@Test
public void testDevicePolicyPrivateNotifications_secondary() {
+ Mockito.clearInvocations(mDevicePolicyManager);
// User allows notifications
mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mSecondaryUser.id);
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
// DevicePolicy hides sensitive content
when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mSecondaryUser.id))
.thenReturn(KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
- BroadcastReceiver.PendingResult pr = mock(BroadcastReceiver.PendingResult.class);
- when(pr.getSendingUserId()).thenReturn(mSecondaryUser.id);
+ BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
+ 0, null, null, 0, true, false, null, mSecondaryUser.id, 0);
mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
+ mLockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext);
assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
- // TODO (b/286230167): enable assertion. It's currently called 5 times.
- //verify(mDevicePolicyManager, atMost(1)).getKeyguardDisabledFeatures(any(), anyInt());
+ verify(mDevicePolicyManager, atMost(1)).getKeyguardDisabledFeatures(any(), anyInt());
}
@Test
public void testHideNotifications_primary() {
mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mCurrentUser.id);
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
}
@@ -491,16 +567,17 @@
@Test
public void testHideNotifications_secondary() {
mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mSecondaryUser.id);
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id));
}
- @Ignore("b/286230167")
@Test
public void testHideNotifications_workProfile() {
- mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mWorkUser.id);
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mCurrentUser.id);
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mWorkUser.id);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mWorkUser.id));
}
@@ -508,67 +585,62 @@
@Test
public void testHideNotifications_secondary_userSwitch() {
mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mSecondaryUser.id);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
- TestNotificationLockscreenUserManager lockscreenUserManager
- = new TestNotificationLockscreenUserManager(mContext);
- lockscreenUserManager.setUpWithPresenter(mPresenter);
+ mLockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext);
- lockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext);
-
- assertFalse(lockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id));
+ assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id));
}
@Test
public void testShowNotifications_secondary_userSwitch() {
mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mSecondaryUser.id);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
- TestNotificationLockscreenUserManager lockscreenUserManager
- = new TestNotificationLockscreenUserManager(mContext);
- lockscreenUserManager.setUpWithPresenter(mPresenter);
+ mLockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext);
- lockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext);
-
- assertTrue(lockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id));
+ assertTrue(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id));
}
- @Ignore("b/286230167")
@Test
public void testShouldShowLockscreenNotifications_keyguardManagerNoPrivateNotifications() {
+ // KeyguardManager does not allow notifications
+ when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(false);
// User allows notifications
mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
// DevicePolicy allows notifications
when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
.thenReturn(0);
- BroadcastReceiver.PendingResult pr = mock(BroadcastReceiver.PendingResult.class);
- when(pr.getSendingUserId()).thenReturn(mCurrentUser.id);
+ BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
+ 0, null, null, 0, true, false, null, mCurrentUser.id, 0);
mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
- // KeyguardManager does not
- when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(false);
-
assertFalse(mLockscreenUserManager.shouldShowLockscreenNotifications());
}
@Test
public void testUserAllowsNotificationsInPublic_keyguardManagerNoPrivateNotifications() {
- // User allows notifications
- mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
// DevicePolicy allows notifications
when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
.thenReturn(0);
- BroadcastReceiver.PendingResult pr = mock(BroadcastReceiver.PendingResult.class);
- when(pr.getSendingUserId()).thenReturn(mCurrentUser.id);
+ BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
+ 0, null, null, 0, true, false, null, mCurrentUser.id, 0);
mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
- // KeyguardManager does not
+ // KeyguardManager does not allow notifications
when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(false);
+ // User allows notifications
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
+ // We shouldn't need to call this method, but getPrivateNotificationsAllowed has no
+ // callback, so it's only updated when the setting is
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
+
assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
}
@@ -576,31 +648,30 @@
public void testUserAllowsNotificationsInPublic_settingsChange() {
// User allows notifications
mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
assertTrue(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
// User disables
mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mCurrentUser.id);
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
}
- @Ignore("b/286230167")
@Test
public void testUserAllowsNotificationsInPublic_devicePolicyChange() {
// User allows notifications
mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
assertTrue(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
// DevicePolicy disables notifications
when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
.thenReturn(KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
- BroadcastReceiver.PendingResult pr = mock(BroadcastReceiver.PendingResult.class);
- when(pr.getSendingUserId()).thenReturn(mCurrentUser.id);
+ BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
+ 0, null, null, 0, true, false, null, mCurrentUser.id, 0);
mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
@@ -608,6 +679,28 @@
assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
}
+ @Test
+ public void testNewUserAdded() {
+ int newUserId = 14;
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, newUserId);
+ mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, newUserId);
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, newUserId))
+ .thenReturn(KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
+
+ BroadcastReceiver broadcastReceiver =
+ mLockscreenUserManager.getBaseBroadcastReceiverForTest();
+ final Bundle extras = new Bundle();
+ final Intent intent = new Intent(Intent.ACTION_USER_ADDED);
+ intent.putExtras(extras);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, newUserId);
+ broadcastReceiver.onReceive(mContext, intent);
+
+ verify(mDevicePolicyManager, atMost(1)).getKeyguardDisabledFeatures(any(), eq(newUserId));
+
+ assertTrue(mLockscreenUserManager.userAllowsNotificationsInPublic(newUserId));
+ assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(newUserId));
+ }
+
private class TestNotificationLockscreenUserManager
extends NotificationLockscreenUserManagerImpl {
public TestNotificationLockscreenUserManager(Context context) {
@@ -623,12 +716,17 @@
(() -> mOverviewProxyService),
NotificationLockscreenUserManagerTest.this.mKeyguardManager,
mStatusBarStateController,
- Handler.createAsync(Looper.myLooper()),
+ Handler.createAsync(TestableLooper.get(
+ NotificationLockscreenUserManagerTest.this).getLooper()),
+ Handler.createAsync(TestableLooper.get(
+ NotificationLockscreenUserManagerTest.this).getLooper()),
+ mBackgroundExecutor,
mDeviceProvisionedController,
mKeyguardStateController,
mSettings,
mock(DumpManager.class),
- mock(LockPatternUtils.class));
+ mock(LockPatternUtils.class),
+ mFakeFeatureFlags);
}
public BroadcastReceiver getBaseBroadcastReceiverForTest() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt
index fbb8ebf..20e5c43 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt
@@ -29,9 +29,9 @@
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
import com.android.systemui.statusbar.notification.row.ExpandableView
import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.util.mockito.mock
import org.junit.Before
import org.junit.Test
@@ -52,7 +52,7 @@
private val collapsedHeight = 300
private val wakeUpCoordinator: NotificationWakeUpCoordinator = mock()
private val bypassController: KeyguardBypassController = mock()
- private val headsUpManager: HeadsUpManagerPhone = mock()
+ private val headsUpManager: HeadsUpManager = mock()
private val roundnessManager: NotificationRoundnessManager = mock()
private val configurationController: ConfigurationController = mock()
private val statusBarStateController: StatusBarStateController = mock()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
index 3327e42..1b26e19 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -23,9 +23,33 @@
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.SysuiTestCase
+import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
+import com.android.systemui.classifier.FalsingCollectorFake
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
+import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.keyguard.data.repository.FakeCommandQueue
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.shade.ShadeExpansionStateManager
+import com.android.systemui.power.data.repository.FakePowerRepository
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository
+import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
+import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.util.mockito.mock
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -40,17 +64,22 @@
import org.mockito.Mockito
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
class StatusBarStateControllerImplTest : SysuiTestCase() {
+ private val utils = SceneTestUtils(this)
+ private val testScope = utils.testScope
+ private lateinit var shadeInteractor: ShadeInteractor
+ private lateinit var fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor
+ private lateinit var fromPrimaryBouncerTransitionInteractor:
+ FromPrimaryBouncerTransitionInteractor
@Mock lateinit var interactionJankMonitor: InteractionJankMonitor
- @Mock private lateinit var mockDarkAnimator: ObjectAnimator
- @Mock private lateinit var shadeExpansionStateManager: ShadeExpansionStateManager
+ @Mock lateinit var mockDarkAnimator: ObjectAnimator
private lateinit var controller: StatusBarStateControllerImpl
private lateinit var uiEventLogger: UiEventLoggerFake
@@ -65,10 +94,74 @@
controller = object : StatusBarStateControllerImpl(
uiEventLogger,
mock(DumpManager::class.java),
- interactionJankMonitor, shadeExpansionStateManager
+ interactionJankMonitor,
+ { shadeInteractor }
) {
override fun createDarkAnimator(): ObjectAnimator { return mockDarkAnimator }
}
+
+ val powerInteractor = PowerInteractor(
+ FakePowerRepository(),
+ FalsingCollectorFake(),
+ mock(),
+ controller)
+ val keyguardRepository = FakeKeyguardRepository()
+ val keyguardTransitionRepository = FakeKeyguardTransitionRepository()
+ val featureFlags = FakeFeatureFlagsClassic()
+ val shadeRepository = FakeShadeRepository()
+ val sceneContainerFlags = FakeSceneContainerFlags()
+ val configurationRepository = FakeConfigurationRepository()
+ val keyguardInteractor = KeyguardInteractor(
+ keyguardRepository,
+ FakeCommandQueue(),
+ powerInteractor,
+ featureFlags,
+ sceneContainerFlags,
+ FakeDeviceEntryRepository(),
+ FakeKeyguardBouncerRepository(),
+ configurationRepository,
+ shadeRepository,
+ utils::sceneInteractor)
+ val keyguardTransitionInteractor = KeyguardTransitionInteractor(
+ testScope.backgroundScope,
+ keyguardTransitionRepository,
+ { keyguardInteractor },
+ { fromLockscreenTransitionInteractor },
+ { fromPrimaryBouncerTransitionInteractor })
+ fromLockscreenTransitionInteractor = FromLockscreenTransitionInteractor(
+ keyguardTransitionRepository,
+ keyguardTransitionInteractor,
+ testScope.backgroundScope,
+ keyguardInteractor,
+ featureFlags,
+ shadeRepository,
+ powerInteractor)
+ fromPrimaryBouncerTransitionInteractor = FromPrimaryBouncerTransitionInteractor(
+ keyguardTransitionRepository,
+ keyguardTransitionInteractor,
+ testScope.backgroundScope,
+ keyguardInteractor,
+ featureFlags,
+ mock(),
+ powerInteractor)
+ shadeInteractor = ShadeInteractor(
+ testScope.backgroundScope,
+ FakeDeviceProvisioningRepository(),
+ FakeDisableFlagsRepository(),
+ mock(),
+ sceneContainerFlags,
+ utils::sceneInteractor,
+ keyguardRepository,
+ keyguardTransitionInteractor,
+ powerInteractor,
+ FakeUserSetupRepository(),
+ FakeUserRepository(),
+ SharedNotificationContainerInteractor(
+ configurationRepository,
+ mContext,
+ ResourcesSplitShadeStateController()),
+ shadeRepository,
+ )
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt
index d86f8bb..235ac5c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt
@@ -8,15 +8,15 @@
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.res.R
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.NotificationTestHelper
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
+import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.statusbar.policy.HeadsUpUtil
-import com.android.systemui.res.R
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import kotlinx.coroutines.test.TestScope
@@ -37,7 +37,7 @@
@RunWithLooper
class NotificationLaunchAnimatorControllerTest : SysuiTestCase() {
@Mock lateinit var notificationListContainer: NotificationListContainer
- @Mock lateinit var headsUpManager: HeadsUpManagerPhone
+ @Mock lateinit var headsUpManager: HeadsUpManager
@Mock lateinit var jankMonitor: InteractionJankMonitor
@Mock lateinit var onFinishAnimationCallback: Runnable
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
index 257cc5b..4f1581c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
@@ -43,8 +43,8 @@
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderWrapper.FullScreenIntentDecisionImpl
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider
import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
import com.android.systemui.statusbar.phone.NotificationGroupTestHelper
-import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
@@ -87,7 +87,7 @@
private val notifPipeline: NotifPipeline = mock()
private val logger = HeadsUpCoordinatorLogger(logcatLogBuffer(), verbose = true)
- private val headsUpManager: HeadsUpManager = mock()
+ private val headsUpManager: HeadsUpManagerPhone = mock()
private val headsUpViewBinder: HeadsUpViewBinder = mock()
private val visualInterruptionDecisionProvider: VisualInterruptionDecisionProvider = mock()
private val remoteInputManager: NotificationRemoteInputManager = mock()
@@ -435,7 +435,7 @@
private fun addHUN(entry: NotificationEntry) {
huns.add(entry)
- whenever(headsUpManager.topEntry).thenReturn(entry)
+ whenever(headsUpManager.getTopEntry()).thenReturn(entry)
onHeadsUpChangedListener.onHeadsUpStateChanged(entry, true)
notifLifetimeExtender.cancelLifetimeExtension(entry)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
index fbd61f4..546abd4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
@@ -23,7 +23,6 @@
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.advanceTimeBy
import com.android.systemui.dump.DumpManager
import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
@@ -40,10 +39,10 @@
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider
-import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProvider
-import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderImpl
import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.stack.data.repository.NotificationListRepository
+import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationListInteractor
import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
import com.android.systemui.util.mockito.any
@@ -247,7 +246,7 @@
unseenFilter.onCleanup()
// THEN: The SeenNotificationProvider has been updated to reflect the suppression
- assertThat(seenNotificationsProvider.hasFilteredOutSeenNotifications).isTrue()
+ assertThat(notificationListInteractor.hasFilteredOutSeenNotifications.value).isTrue()
}
}
@@ -598,7 +597,7 @@
FakeSettings().apply {
putInt(Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS, 1)
}
- val seenNotificationsProvider = SeenNotificationsProviderImpl()
+ val notificationListInteractor = NotificationListInteractor(NotificationListRepository())
val keyguardCoordinator =
KeyguardCoordinator(
testDispatcher,
@@ -611,7 +610,7 @@
testScope.backgroundScope,
sectionHeaderVisibilityProvider,
fakeSettings,
- seenNotificationsProvider,
+ notificationListInteractor,
statusBarStateController,
)
keyguardCoordinator.attach(notifPipeline)
@@ -619,7 +618,7 @@
KeyguardCoordinatorTestScope(
keyguardCoordinator,
testScope,
- seenNotificationsProvider,
+ notificationListInteractor,
fakeSettings,
)
.testBlock()
@@ -629,7 +628,7 @@
private inner class KeyguardCoordinatorTestScope(
private val keyguardCoordinator: KeyguardCoordinator,
private val scope: TestScope,
- val seenNotificationsProvider: SeenNotificationsProvider,
+ val notificationListInteractor: NotificationListInteractor,
private val fakeSettings: FakeSettings,
) : CoroutineScope by scope {
val testScheduler: TestCoroutineScheduler
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt
new file mode 100644
index 0000000..2f8f3bb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ */
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.statusbar.notification.data.repository
+
+import androidx.test.filters.SmallTest
+import com.android.SysUITestModule
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.mockito.withArgCaptor
+import com.google.common.truth.Truth.assertThat
+import dagger.BindsInstance
+import dagger.Component
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.mockito.Mockito.verify
+
+@SmallTest
+class NotificationsKeyguardViewStateRepositoryTest : SysuiTestCase() {
+
+ private val testComponent: TestComponent =
+ DaggerNotificationsKeyguardViewStateRepositoryTest_TestComponent.factory()
+ .create(test = this)
+
+ @Test
+ fun areNotifsFullyHidden_reflectsWakeUpCoordinator() =
+ with(testComponent) {
+ testScope.runTest {
+ whenever(mockWakeUpCoordinator.notificationsFullyHidden).thenReturn(false)
+ val notifsFullyHidden by collectLastValue(underTest.areNotificationsFullyHidden)
+ runCurrent()
+
+ assertThat(notifsFullyHidden).isFalse()
+
+ withArgCaptor { verify(mockWakeUpCoordinator).addListener(capture()) }
+ .onFullyHiddenChanged(true)
+ runCurrent()
+
+ assertThat(notifsFullyHidden).isTrue()
+ }
+ }
+
+ @Test
+ fun isPulseExpanding_reflectsWakeUpCoordinator() =
+ with(testComponent) {
+ testScope.runTest {
+ whenever(mockWakeUpCoordinator.isPulseExpanding()).thenReturn(false)
+ val isPulseExpanding by collectLastValue(underTest.isPulseExpanding)
+ runCurrent()
+
+ assertThat(isPulseExpanding).isFalse()
+
+ withArgCaptor { verify(mockWakeUpCoordinator).addListener(capture()) }
+ .onPulseExpansionChanged(true)
+ runCurrent()
+
+ assertThat(isPulseExpanding).isTrue()
+ }
+ }
+
+ @SysUISingleton
+ @Component(
+ modules =
+ [
+ SysUITestModule::class,
+ ]
+ )
+ interface TestComponent {
+
+ val underTest: NotificationsKeyguardViewStateRepositoryImpl
+
+ val mockWakeUpCoordinator: NotificationWakeUpCoordinator
+ val testScope: TestScope
+
+ @Component.Factory
+ interface Factory {
+ fun create(
+ @BindsInstance test: SysuiTestCase,
+ ): TestComponent
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt
new file mode 100644
index 0000000..705a5a3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt
@@ -0,0 +1,89 @@
+/*
+ * 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.domain.interactor
+
+import androidx.test.filters.SmallTest
+import com.android.SysUITestModule
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.data.repository.FakeNotificationsKeyguardViewStateRepository
+import com.google.common.truth.Truth.assertThat
+import dagger.BindsInstance
+import dagger.Component
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+
+@SmallTest
+class NotificationsKeyguardInteractorTest : SysuiTestCase() {
+
+ private val testComponent: TestComponent =
+ DaggerNotificationsKeyguardInteractorTest_TestComponent.factory().create(test = this)
+
+ @Test
+ fun areNotifsFullyHidden_reflectsRepository() =
+ with(testComponent) {
+ testScope.runTest {
+ repository.setNotificationsFullyHidden(false)
+ val notifsFullyHidden by collectLastValue(underTest.areNotificationsFullyHidden)
+ runCurrent()
+
+ assertThat(notifsFullyHidden).isFalse()
+
+ repository.setNotificationsFullyHidden(true)
+ runCurrent()
+
+ assertThat(notifsFullyHidden).isTrue()
+ }
+ }
+
+ @Test
+ fun isPulseExpanding_reflectsRepository() =
+ with(testComponent) {
+ testScope.runTest {
+ repository.setPulseExpanding(false)
+ val isPulseExpanding by collectLastValue(underTest.isPulseExpanding)
+ runCurrent()
+
+ assertThat(isPulseExpanding).isFalse()
+
+ repository.setPulseExpanding(true)
+ runCurrent()
+
+ assertThat(isPulseExpanding).isTrue()
+ }
+ }
+
+ @SysUISingleton
+ @Component(
+ modules =
+ [
+ SysUITestModule::class,
+ ]
+ )
+ interface TestComponent {
+
+ val underTest: NotificationsKeyguardInteractor
+
+ val repository: FakeNotificationsKeyguardViewStateRepository
+ val testScope: TestScope
+
+ @Component.Factory
+ interface Factory {
+ fun create(@BindsInstance test: SysuiTestCase): TestComponent
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java
index b120c47..f72142f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java
@@ -11,10 +11,10 @@
* 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
+ * limitations under the License.
*/
-package com.android.systemui.statusbar.notification.row;
+package com.android.systemui.statusbar.notification.footer.ui.view;
import static com.google.common.truth.Truth.assertThat;
@@ -31,8 +31,8 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.res.R;
import org.junit.Before;
import org.junit.Test;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImplTest.kt
index 7caa5ccc..e57986d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImplTest.kt
@@ -26,9 +26,7 @@
import com.android.systemui.flags.FakeFeatureFlagsClassicModule
import com.android.systemui.flags.Flags
import com.android.systemui.statusbar.phone.DozeParameters
-import com.android.systemui.statusbar.phone.NotificationIconContainer
import com.android.systemui.user.domain.UserDomainLayerModule
-import com.android.systemui.util.mockito.whenever
import dagger.BindsInstance
import dagger.Component
import org.junit.Assert.assertFalse
@@ -37,7 +35,6 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@SmallTest
@@ -46,7 +43,6 @@
class NotificationIconAreaControllerViewBinderWrapperImplTest : SysuiTestCase() {
@Mock private lateinit var dozeParams: DozeParameters
- @Mock private lateinit var aodIcons: NotificationIconContainer
private lateinit var testComponent: TestComponent
private val underTest
@@ -85,15 +81,6 @@
assertTrue(underTest.shouldShowLowPriorityIcons())
}
- @Test
- fun testAppearResetsTranslation() {
- underTest.setupAodIcons(aodIcons)
- whenever(dozeParams.shouldControlScreenOff()).thenReturn(false)
- underTest.appearAodIcons()
- verify(aodIcons).translationY = 0f
- verify(aodIcons).alpha = 1.0f
- }
-
@SysUISingleton
@Component(
modules =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
new file mode 100644
index 0000000..31efebb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
@@ -0,0 +1,493 @@
+/*
+ * 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.
+ */
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.statusbar.notification.icon.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.SysUITestModule
+import com.android.TestMocksModule
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
+import com.android.systemui.flags.FakeFeatureFlagsClassicModule
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.DozeStateModel
+import com.android.systemui.keyguard.shared.model.DozeTransitionModel
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.power.data.repository.FakePowerRepository
+import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.power.shared.model.WakefulnessState
+import com.android.systemui.statusbar.notification.data.repository.FakeNotificationsKeyguardViewStateRepository
+import com.android.systemui.statusbar.phone.DozeParameters
+import com.android.systemui.statusbar.phone.ScreenOffAnimationController
+import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository
+import com.android.systemui.user.domain.UserDomainLayerModule
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.ui.AnimatedValue
+import com.google.common.truth.Truth.assertThat
+import dagger.BindsInstance
+import dagger.Component
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NotificationIconContainerAlwaysOnDisplayViewModelTest : SysuiTestCase() {
+
+ @Mock private lateinit var dozeParams: DozeParameters
+ @Mock private lateinit var screenOffAnimController: ScreenOffAnimationController
+
+ private lateinit var testComponent: TestComponent
+ private val underTest: NotificationIconContainerAlwaysOnDisplayViewModel
+ get() = testComponent.underTest
+ private val deviceEntryRepository: FakeDeviceEntryRepository
+ get() = testComponent.deviceEntryRepository
+ private val deviceProvisioningRepository: FakeDeviceProvisioningRepository
+ get() = testComponent.deviceProvisioningRepository
+ private val keyguardRepository: FakeKeyguardRepository
+ get() = testComponent.keyguardRepository
+ private val keyguardTransitionRepository: FakeKeyguardTransitionRepository
+ get() = testComponent.keyguardTransitionRepository
+ private val notifsKeyguardRepository: FakeNotificationsKeyguardViewStateRepository
+ get() = testComponent.notifsKeyguardRepository
+ private val powerRepository: FakePowerRepository
+ get() = testComponent.powerRepository
+ private val scope: TestScope
+ get() = testComponent.scope
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+
+ testComponent =
+ DaggerNotificationIconContainerAlwaysOnDisplayViewModelTest_TestComponent.factory()
+ .create(
+ test = this,
+ featureFlags =
+ FakeFeatureFlagsClassicModule {
+ setDefault(Flags.FACE_AUTH_REFACTOR)
+ set(Flags.FULL_SCREEN_USER_SWITCHER, value = false)
+ setDefault(Flags.NEW_AOD_TRANSITION)
+ },
+ mocks =
+ TestMocksModule(
+ dozeParameters = dozeParams,
+ screenOffAnimationController = screenOffAnimController,
+ ),
+ )
+
+ keyguardRepository.setKeyguardShowing(true)
+ keyguardRepository.setKeyguardOccluded(false)
+ deviceProvisioningRepository.setFactoryResetProtectionActive(false)
+ powerRepository.updateWakefulness(
+ rawState = WakefulnessState.AWAKE,
+ lastWakeReason = WakeSleepReason.OTHER,
+ lastSleepReason = WakeSleepReason.OTHER,
+ )
+ }
+
+ @Test
+ fun animationsEnabled_isFalse_whenFrpIsActive() =
+ scope.runTest {
+ deviceProvisioningRepository.setFactoryResetProtectionActive(true)
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+ runCurrent()
+ assertThat(animationsEnabled).isFalse()
+ }
+
+ @Test
+ fun animationsEnabled_isFalse_whenDeviceAsleepAndNotPulsing() =
+ scope.runTest {
+ powerRepository.updateWakefulness(
+ rawState = WakefulnessState.ASLEEP,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.OTHER,
+ )
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ keyguardRepository.setDozeTransitionModel(
+ DozeTransitionModel(
+ to = DozeStateModel.DOZE_AOD,
+ )
+ )
+ val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+ runCurrent()
+ assertThat(animationsEnabled).isFalse()
+ }
+
+ @Test
+ fun animationsEnabled_isTrue_whenDeviceAsleepAndPulsing() =
+ scope.runTest {
+ powerRepository.updateWakefulness(
+ rawState = WakefulnessState.ASLEEP,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.OTHER,
+ )
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ keyguardRepository.setDozeTransitionModel(
+ DozeTransitionModel(
+ to = DozeStateModel.DOZE_PULSING,
+ )
+ )
+ val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+ runCurrent()
+ assertThat(animationsEnabled).isTrue()
+ }
+
+ @Test
+ fun animationsEnabled_isFalse_whenStartingToSleepAndNotControlScreenOff() =
+ scope.runTest {
+ powerRepository.updateWakefulness(
+ rawState = WakefulnessState.STARTING_TO_SLEEP,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.OTHER,
+ )
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ whenever(dozeParams.shouldControlScreenOff()).thenReturn(false)
+ val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+ runCurrent()
+ assertThat(animationsEnabled).isFalse()
+ }
+
+ @Test
+ fun animationsEnabled_isTrue_whenStartingToSleepAndControlScreenOff() =
+ scope.runTest {
+ powerRepository.updateWakefulness(
+ rawState = WakefulnessState.STARTING_TO_SLEEP,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.OTHER,
+ )
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ whenever(dozeParams.shouldControlScreenOff()).thenReturn(true)
+ val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+ runCurrent()
+ assertThat(animationsEnabled).isTrue()
+ }
+
+ @Test
+ fun animationsEnabled_isTrue_whenNotAsleep() =
+ scope.runTest {
+ powerRepository.updateWakefulness(
+ rawState = WakefulnessState.AWAKE,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.OTHER,
+ )
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+ runCurrent()
+ assertThat(animationsEnabled).isTrue()
+ }
+
+ @Test
+ fun animationsEnabled_isTrue_whenKeyguardIsShowing() =
+ scope.runTest {
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+
+ keyguardRepository.setKeyguardShowing(true)
+ keyguardRepository.setKeyguardOccluded(false)
+ runCurrent()
+
+ assertThat(animationsEnabled).isTrue()
+
+ keyguardRepository.setKeyguardOccluded(true)
+ runCurrent()
+
+ assertThat(animationsEnabled).isFalse()
+
+ keyguardRepository.setKeyguardShowing(false)
+ keyguardRepository.setKeyguardOccluded(true)
+ runCurrent()
+
+ assertThat(animationsEnabled).isFalse()
+ }
+
+ @Test
+ fun isDozing_startAodTransition() =
+ scope.runTest {
+ val isDozing by collectLastValue(underTest.isDozing)
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ runCurrent()
+ assertThat(isDozing).isEqualTo(AnimatedValue(true, isAnimating = true))
+ }
+
+ @Test
+ fun isDozing_startDozeTransition() =
+ scope.runTest {
+ val isDozing by collectLastValue(underTest.isDozing)
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.GONE,
+ to = KeyguardState.DOZING,
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ runCurrent()
+ assertThat(isDozing).isEqualTo(AnimatedValue(true, isAnimating = false))
+ }
+
+ @Test
+ fun isDozing_startDozeToAodTransition() =
+ scope.runTest {
+ val isDozing by collectLastValue(underTest.isDozing)
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.DOZING,
+ to = KeyguardState.AOD,
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ runCurrent()
+ assertThat(isDozing).isEqualTo(AnimatedValue(true, isAnimating = true))
+ }
+
+ @Test
+ fun isNotDozing_startAodToGoneTransition() =
+ scope.runTest {
+ val isDozing by collectLastValue(underTest.isDozing)
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.AOD,
+ to = KeyguardState.GONE,
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ runCurrent()
+ assertThat(isDozing).isEqualTo(AnimatedValue(false, isAnimating = true))
+ }
+
+ @Test
+ fun isDozing_stopAnimation() =
+ scope.runTest {
+ val isDozing by collectLastValue(underTest.isDozing)
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.AOD,
+ to = KeyguardState.GONE,
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ runCurrent()
+
+ underTest.completeDozeAnimation()
+ runCurrent()
+
+ assertThat(isDozing?.isAnimating).isEqualTo(false)
+ }
+
+ @Test
+ fun isNotVisible_pulseExpanding() =
+ scope.runTest {
+ val isVisible by collectLastValue(underTest.isVisible)
+ notifsKeyguardRepository.setPulseExpanding(true)
+ runCurrent()
+
+ assertThat(isVisible?.value).isFalse()
+ }
+
+ @Test
+ fun isNotVisible_notOnKeyguard_dontShowAodIconsWhenShade() =
+ scope.runTest {
+ val isVisible by collectLastValue(underTest.isVisible)
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ to = KeyguardState.GONE,
+ transitionState = TransitionState.FINISHED,
+ )
+ )
+ whenever(screenOffAnimController.shouldShowAodIconsWhenShade()).thenReturn(false)
+ runCurrent()
+
+ assertThat(isVisible).isEqualTo(AnimatedValue(false, isAnimating = false))
+ }
+
+ @Test
+ fun isVisible_bypassEnabled() =
+ scope.runTest {
+ val isVisible by collectLastValue(underTest.isVisible)
+ deviceEntryRepository.setBypassEnabled(true)
+ runCurrent()
+
+ assertThat(isVisible?.value).isTrue()
+ }
+
+ @Test
+ fun isNotVisible_pulseExpanding_notBypassing() =
+ scope.runTest {
+ val isVisible by collectLastValue(underTest.isVisible)
+ notifsKeyguardRepository.setPulseExpanding(true)
+ deviceEntryRepository.setBypassEnabled(false)
+ runCurrent()
+
+ assertThat(isVisible?.value).isEqualTo(false)
+ }
+
+ @Test
+ fun isVisible_notifsFullyHidden_bypassEnabled() =
+ scope.runTest {
+ val isVisible by collectLastValue(underTest.isVisible)
+ runCurrent()
+ notifsKeyguardRepository.setPulseExpanding(false)
+ deviceEntryRepository.setBypassEnabled(true)
+ notifsKeyguardRepository.setNotificationsFullyHidden(true)
+ runCurrent()
+
+ assertThat(isVisible).isEqualTo(AnimatedValue(true, isAnimating = true))
+ }
+
+ @Test
+ fun isVisible_notifsFullyHidden_bypassDisabled_aodDisabled() =
+ scope.runTest {
+ val isVisible by collectLastValue(underTest.isVisible)
+ notifsKeyguardRepository.setPulseExpanding(false)
+ deviceEntryRepository.setBypassEnabled(false)
+ whenever(dozeParams.alwaysOn).thenReturn(false)
+ notifsKeyguardRepository.setNotificationsFullyHidden(true)
+ runCurrent()
+
+ assertThat(isVisible).isEqualTo(AnimatedValue(true, isAnimating = false))
+ }
+
+ @Test
+ fun isVisible_notifsFullyHidden_bypassDisabled_displayNeedsBlanking() =
+ scope.runTest {
+ val isVisible by collectLastValue(underTest.isVisible)
+ notifsKeyguardRepository.setPulseExpanding(false)
+ deviceEntryRepository.setBypassEnabled(false)
+ whenever(dozeParams.alwaysOn).thenReturn(true)
+ whenever(dozeParams.displayNeedsBlanking).thenReturn(true)
+ notifsKeyguardRepository.setNotificationsFullyHidden(true)
+ runCurrent()
+
+ assertThat(isVisible).isEqualTo(AnimatedValue(true, isAnimating = false))
+ }
+
+ @Test
+ fun isVisible_notifsFullyHidden_bypassDisabled() =
+ scope.runTest {
+ val isVisible by collectLastValue(underTest.isVisible)
+ runCurrent()
+ notifsKeyguardRepository.setPulseExpanding(false)
+ deviceEntryRepository.setBypassEnabled(false)
+ whenever(dozeParams.alwaysOn).thenReturn(true)
+ whenever(dozeParams.displayNeedsBlanking).thenReturn(false)
+ notifsKeyguardRepository.setNotificationsFullyHidden(true)
+ runCurrent()
+
+ assertThat(isVisible).isEqualTo(AnimatedValue(true, isAnimating = true))
+ }
+
+ @Test
+ fun isVisible_stopAnimation() =
+ scope.runTest {
+ val isVisible by collectLastValue(underTest.isVisible)
+ notifsKeyguardRepository.setPulseExpanding(false)
+ deviceEntryRepository.setBypassEnabled(false)
+ whenever(dozeParams.alwaysOn).thenReturn(true)
+ whenever(dozeParams.displayNeedsBlanking).thenReturn(false)
+ notifsKeyguardRepository.setNotificationsFullyHidden(true)
+ runCurrent()
+
+ underTest.completeVisibilityAnimation()
+ runCurrent()
+
+ assertThat(isVisible?.isAnimating).isEqualTo(false)
+ }
+
+ @SysUISingleton
+ @Component(
+ modules =
+ [
+ SysUITestModule::class,
+ BiometricsDomainLayerModule::class,
+ UserDomainLayerModule::class,
+ ]
+ )
+ interface TestComponent {
+
+ val underTest: NotificationIconContainerAlwaysOnDisplayViewModel
+
+ val deviceEntryRepository: FakeDeviceEntryRepository
+ val deviceProvisioningRepository: FakeDeviceProvisioningRepository
+ val keyguardRepository: FakeKeyguardRepository
+ val keyguardTransitionRepository: FakeKeyguardTransitionRepository
+ val notifsKeyguardRepository: FakeNotificationsKeyguardViewStateRepository
+ val powerRepository: FakePowerRepository
+ val scope: TestScope
+
+ @Component.Factory
+ interface Factory {
+ fun create(
+ @BindsInstance test: SysuiTestCase,
+ mocks: TestMocksModule,
+ featureFlags: FakeFeatureFlagsClassicModule,
+ ): TestComponent
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
new file mode 100644
index 0000000..e1e7f92
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
@@ -0,0 +1,276 @@
+/*
+ * 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.
+ */
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.statusbar.notification.icon.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.SysUITestModule
+import com.android.TestMocksModule
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FakeFeatureFlagsClassicModule
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.DozeStateModel
+import com.android.systemui.keyguard.shared.model.DozeTransitionModel
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.power.data.repository.FakePowerRepository
+import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.power.shared.model.WakefulnessState
+import com.android.systemui.statusbar.phone.DozeParameters
+import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository
+import com.android.systemui.user.domain.UserDomainLayerModule
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import dagger.BindsInstance
+import dagger.Component
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NotificationIconContainerStatusBarViewModelTest : SysuiTestCase() {
+
+ @Mock lateinit var dozeParams: DozeParameters
+
+ private lateinit var testComponent: TestComponent
+ private val underTest: NotificationIconContainerStatusBarViewModel
+ get() = testComponent.underTest
+ private val deviceProvisioningRepository
+ get() = testComponent.deviceProvisioningRepository
+ private val keyguardTransitionRepository
+ get() = testComponent.keyguardTransitionRepository
+ private val keyguardRepository
+ get() = testComponent.keyguardRepository
+ private val powerRepository
+ get() = testComponent.powerRepository
+ private val scope
+ get() = testComponent.scope
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+
+ testComponent =
+ DaggerNotificationIconContainerStatusBarViewModelTest_TestComponent.factory()
+ .create(
+ test = this,
+ // Configurable bindings
+ featureFlags =
+ FakeFeatureFlagsClassicModule {
+ set(Flags.FACE_AUTH_REFACTOR, value = false)
+ set(Flags.FULL_SCREEN_USER_SWITCHER, value = false)
+ },
+ mocks =
+ TestMocksModule(
+ dozeParameters = dozeParams,
+ ),
+ )
+
+ keyguardRepository.setKeyguardShowing(false)
+ deviceProvisioningRepository.setFactoryResetProtectionActive(false)
+ powerRepository.updateWakefulness(
+ rawState = WakefulnessState.AWAKE,
+ lastWakeReason = WakeSleepReason.OTHER,
+ lastSleepReason = WakeSleepReason.OTHER,
+ )
+ }
+
+ @Test
+ fun animationsEnabled_isFalse_whenFrpIsActive() =
+ scope.runTest {
+ deviceProvisioningRepository.setFactoryResetProtectionActive(true)
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+ runCurrent()
+ assertThat(animationsEnabled).isFalse()
+ }
+
+ @Test
+ fun animationsEnabled_isFalse_whenDeviceAsleepAndNotPulsing() =
+ scope.runTest {
+ powerRepository.updateWakefulness(
+ rawState = WakefulnessState.ASLEEP,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.OTHER,
+ )
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ keyguardRepository.setDozeTransitionModel(
+ DozeTransitionModel(
+ to = DozeStateModel.DOZE_AOD,
+ )
+ )
+ val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+ runCurrent()
+ assertThat(animationsEnabled).isFalse()
+ }
+
+ @Test
+ fun animationsEnabled_isTrue_whenDeviceAsleepAndPulsing() =
+ scope.runTest {
+ powerRepository.updateWakefulness(
+ rawState = WakefulnessState.ASLEEP,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.OTHER,
+ )
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ keyguardRepository.setDozeTransitionModel(
+ DozeTransitionModel(
+ to = DozeStateModel.DOZE_PULSING,
+ )
+ )
+ val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+ runCurrent()
+ assertThat(animationsEnabled).isTrue()
+ }
+
+ @Test
+ fun animationsEnabled_isFalse_whenStartingToSleepAndNotControlScreenOff() =
+ scope.runTest {
+ powerRepository.updateWakefulness(
+ rawState = WakefulnessState.STARTING_TO_SLEEP,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.OTHER,
+ )
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ whenever(dozeParams.shouldControlScreenOff()).thenReturn(false)
+ val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+ runCurrent()
+ assertThat(animationsEnabled).isFalse()
+ }
+
+ @Test
+ fun animationsEnabled_isTrue_whenStartingToSleepAndControlScreenOff() =
+ scope.runTest {
+ powerRepository.updateWakefulness(
+ rawState = WakefulnessState.STARTING_TO_SLEEP,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.OTHER,
+ )
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ whenever(dozeParams.shouldControlScreenOff()).thenReturn(true)
+ val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+ runCurrent()
+ assertThat(animationsEnabled).isTrue()
+ }
+
+ @Test
+ fun animationsEnabled_isTrue_whenNotAsleep() =
+ scope.runTest {
+ powerRepository.updateWakefulness(
+ rawState = WakefulnessState.AWAKE,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.OTHER,
+ )
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+ runCurrent()
+ assertThat(animationsEnabled).isTrue()
+ }
+
+ @Test
+ fun animationsEnabled_isTrue_whenKeyguardIsNotShowing() =
+ scope.runTest {
+ val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ keyguardRepository.setKeyguardShowing(true)
+ runCurrent()
+
+ assertThat(animationsEnabled).isFalse()
+
+ keyguardRepository.setKeyguardShowing(false)
+ runCurrent()
+
+ assertThat(animationsEnabled).isTrue()
+ }
+
+ @SysUISingleton
+ @Component(
+ modules =
+ [
+ SysUITestModule::class,
+ // Real impls
+ BiometricsDomainLayerModule::class,
+ UserDomainLayerModule::class,
+ ]
+ )
+ interface TestComponent {
+
+ val underTest: NotificationIconContainerStatusBarViewModel
+
+ val deviceProvisioningRepository: FakeDeviceProvisioningRepository
+ val keyguardTransitionRepository: FakeKeyguardTransitionRepository
+ val keyguardRepository: FakeKeyguardRepository
+ val powerRepository: FakePowerRepository
+ val scope: TestScope
+
+ @Component.Factory
+ interface Factory {
+ fun create(
+ @BindsInstance test: SysuiTestCase,
+ mocks: TestMocksModule,
+ featureFlags: FakeFeatureFlagsClassicModule,
+ ): TestComponent
+ }
+ }
+}
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..b922ab3 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
@@ -22,6 +22,7 @@
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MIN;
+import static android.app.NotificationManager.VISIBILITY_NO_OVERRIDE;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
@@ -36,6 +37,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.NotificationChannel;
import android.content.Context;
import android.os.Handler;
import android.os.UserHandle;
@@ -51,6 +53,9 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FakeFeatureFlagsClassic;
+import com.android.systemui.flags.FeatureFlagsClassic;
+import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -62,11 +67,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 +86,6 @@
import java.util.Map;
import java.util.function.Consumer;
-import dagger.BindsInstance;
-import dagger.Component;
-
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -93,7 +99,9 @@
@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 final FakeFeatureFlagsClassic mFeatureFlags = new FakeFeatureFlagsClassic();
private KeyguardNotificationVisibilityProvider mKeyguardNotificationVisibilityProvider;
private NotificationEntry mEntry;
@@ -113,8 +121,9 @@
mHighPriorityProvider,
mStatusBarStateController,
mUserTracker,
- mFakeSettings,
- mFakeSettings);
+ mSecureSettings,
+ mGlobalSettings,
+ mFeatureFlags);
mKeyguardNotificationVisibilityProvider = component.getProvider();
for (CoreStartable startable : component.getCoreStartables().values()) {
startable.start();
@@ -223,7 +232,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 +243,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 +251,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 +273,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 +286,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 +309,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 +328,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 +347,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 +357,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 +369,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 +380,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());
}
@@ -421,6 +431,7 @@
@Test
public void publicMode_settingsDisallow() {
+ mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true);
// GIVEN an 'unfiltered-keyguard-showing' state
setupUnfilteredState(mEntry);
@@ -430,12 +441,59 @@
when(mLockscreenUserManager.userAllowsNotificationsInPublic(NOTIF_USER_ID))
.thenReturn(false);
+ mEntry.setRanking(new RankingBuilder()
+ .setChannel(new NotificationChannel("1", "1", 4))
+ .setVisibilityOverride(VISIBILITY_NO_OVERRIDE)
+ .setKey(mEntry.getKey()).build());
+
+ // THEN filter out the entry
+ assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry));
+ }
+
+ @Test
+ public void publicMode_settingsDisallow_mainThread() {
+ mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, false);
+ // GIVEN an 'unfiltered-keyguard-showing' state
+ setupUnfilteredState(mEntry);
+
+ // WHEN the notification's user is in public mode and settings are configured to disallow
+ // notifications in public mode
+ when(mLockscreenUserManager.isLockscreenPublicMode(NOTIF_USER_ID)).thenReturn(true);
+ when(mLockscreenUserManager.userAllowsNotificationsInPublic(NOTIF_USER_ID))
+ .thenReturn(false);
+
+ mEntry.setRanking(new RankingBuilder()
+ .setChannel(new NotificationChannel("1", "1", 4))
+ .setVisibilityOverride(VISIBILITY_NO_OVERRIDE)
+ .setKey(mEntry.getKey()).build());
+
// THEN filter out the entry
assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry));
}
@Test
public void publicMode_notifDisallowed() {
+ mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true);
+ NotificationChannel channel = new NotificationChannel("1", "1", IMPORTANCE_HIGH);
+ channel.setLockscreenVisibility(VISIBILITY_SECRET);
+ // GIVEN an 'unfiltered-keyguard-showing' state
+ setupUnfilteredState(mEntry);
+
+ // WHEN the notification's user is in public mode and settings are configured to disallow
+ // notifications in public mode
+ when(mLockscreenUserManager.isLockscreenPublicMode(CURR_USER_ID)).thenReturn(true);
+ mEntry.setRanking(new RankingBuilder()
+ .setKey(mEntry.getKey())
+ .setChannel(channel)
+ .setVisibilityOverride(VISIBILITY_SECRET).build());
+
+ // THEN filter out the entry
+ assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry));
+ }
+
+ @Test
+ public void publicMode_notifDisallowed_mainThread() {
+ mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, false);
// GIVEN an 'unfiltered-keyguard-showing' state
setupUnfilteredState(mEntry);
@@ -470,8 +528,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
@@ -503,6 +561,54 @@
}
@Test
+ public void notificationChannelVisibilityNoOverride() {
+ mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true);
+ // GIVEN a VISIBILITY_PRIVATE notification
+ NotificationEntryBuilder entryBuilder = new NotificationEntryBuilder()
+ .setUser(new UserHandle(NOTIF_USER_ID));
+ entryBuilder.modifyNotification(mContext)
+ .setVisibility(VISIBILITY_PRIVATE);
+ mEntry = entryBuilder.build();
+ // ranking says secret because of DPC or Setting
+ mEntry.setRanking(new RankingBuilder()
+ .setKey(mEntry.getKey())
+ .setVisibilityOverride(VISIBILITY_SECRET)
+ .setImportance(IMPORTANCE_HIGH)
+ .build());
+
+ // WHEN we're in an 'unfiltered-keyguard-showing' state
+ setupUnfilteredState(mEntry);
+
+ // THEN don't hide the entry based on visibility. (Redaction is handled elsewhere.)
+ assertFalse(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry));
+ }
+
+ @Test
+ public void notificationChannelVisibilitySecret() {
+ mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true);
+ // GIVEN a VISIBILITY_PRIVATE notification
+ NotificationEntryBuilder entryBuilder = new NotificationEntryBuilder()
+ .setUser(new UserHandle(NOTIF_USER_ID));
+ entryBuilder.modifyNotification(mContext)
+ .setVisibility(VISIBILITY_PRIVATE);
+ // And a VISIBILITY_SECRET NotificationChannel
+ NotificationChannel channel = new NotificationChannel("id", "name", IMPORTANCE_HIGH);
+ channel.setLockscreenVisibility(VISIBILITY_SECRET);
+ mEntry = entryBuilder.build();
+ // WHEN we're in an 'unfiltered-keyguard-showing' state
+ setupUnfilteredState(mEntry);
+ when(mLockscreenUserManager.isLockscreenPublicMode(CURR_USER_ID)).thenReturn(true);
+ when(mLockscreenUserManager.isLockscreenPublicMode(NOTIF_USER_ID)).thenReturn(true);
+
+ mEntry.setRanking(new RankingBuilder(mEntry.getRanking())
+ .setChannel(channel)
+ .build());
+
+ // THEN hide the entry based on visibility.
+ assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry));
+ }
+
+ @Test
public void notificationVisibilityPrivate() {
// GIVEN a VISIBILITY_PRIVATE notification
NotificationEntryBuilder entryBuilder = new NotificationEntryBuilder()
@@ -557,7 +663,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
@@ -632,7 +738,8 @@
@BindsInstance SysuiStatusBarStateController statusBarStateController,
@BindsInstance UserTracker userTracker,
@BindsInstance SecureSettings secureSettings,
- @BindsInstance GlobalSettings globalSettings
+ @BindsInstance GlobalSettings globalSettings,
+ @BindsInstance FeatureFlagsClassic featureFlags
);
}
}
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/FeedbackInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
index c9b77c5..9c20e54 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
@@ -54,9 +54,9 @@
import android.widget.TextView;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
@@ -132,7 +132,8 @@
public void testBindNotification_SetsTextApplicationName() {
when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(),
- mMockNotificationRow, mAssistantFeedbackController);
+ mMockNotificationRow, mAssistantFeedbackController, mStatusBarService,
+ mNotificationGutsManager);
final TextView textView = mFeedbackInfo.findViewById(R.id.pkg_name);
assertTrue(textView.getText().toString().contains("App Name"));
}
@@ -143,7 +144,8 @@
when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class)))
.thenReturn(iconDrawable);
mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(),
- mMockNotificationRow, mAssistantFeedbackController);
+ mMockNotificationRow, mAssistantFeedbackController, mStatusBarService,
+ mNotificationGutsManager);
final ImageView iconView = mFeedbackInfo.findViewById(R.id.pkg_icon);
assertEquals(iconDrawable, iconView.getDrawable());
}
@@ -153,7 +155,7 @@
when(mAssistantFeedbackController.getFeedbackStatus(any(NotificationEntry.class)))
.thenReturn(STATUS_SILENCED);
mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow,
- mAssistantFeedbackController);
+ mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager);
TextView prompt = mFeedbackInfo.findViewById(R.id.prompt);
assertEquals("This notification was automatically demoted to Silent by the system. "
+ "Let the developer know your feedback. Was this correct?",
@@ -165,7 +167,7 @@
when(mAssistantFeedbackController.getFeedbackStatus(any(NotificationEntry.class)))
.thenReturn(STATUS_PROMOTED);
mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow,
- mAssistantFeedbackController);
+ mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager);
TextView prompt = mFeedbackInfo.findViewById(R.id.prompt);
assertEquals("This notification was automatically ranked higher in your shade. "
+ "Let the developer know your feedback. Was this correct?",
@@ -177,7 +179,7 @@
when(mAssistantFeedbackController.getFeedbackStatus(any(NotificationEntry.class)))
.thenReturn(STATUS_ALERTED);
mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow,
- mAssistantFeedbackController);
+ mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager);
TextView prompt = mFeedbackInfo.findViewById(R.id.prompt);
assertEquals("This notification was automatically promoted to Default by the system. "
+ "Let the developer know your feedback. Was this correct?",
@@ -189,7 +191,7 @@
when(mAssistantFeedbackController.getFeedbackStatus(any(NotificationEntry.class)))
.thenReturn(STATUS_DEMOTED);
mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow,
- mAssistantFeedbackController);
+ mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager);
TextView prompt = mFeedbackInfo.findViewById(R.id.prompt);
assertEquals("This notification was automatically ranked lower in your shade. "
+ "Let the developer know your feedback. Was this correct?",
@@ -199,7 +201,7 @@
@Test
public void testPositiveFeedback() {
mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow,
- mAssistantFeedbackController);
+ mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager);
final View yes = mFeedbackInfo.findViewById(R.id.yes);
yes.performClick();
@@ -216,7 +218,7 @@
.thenReturn(true);
mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow,
- mAssistantFeedbackController);
+ mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager);
final View no = mFeedbackInfo.findViewById(R.id.no);
no.performClick();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 9f2afdf..8a730cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -33,7 +33,6 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anySet;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
@@ -89,8 +88,8 @@
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.time.FakeSystemClock;
@@ -152,7 +151,7 @@
@Mock private AssistantFeedbackController mAssistantFeedbackController;
@Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
@Mock private StatusBarStateController mStatusBarStateController;
- @Mock private HeadsUpManagerPhone mHeadsUpManagerPhone;
+ @Mock private HeadsUpManager mHeadsUpManager;
@Mock private ActivityStarter mActivityStarter;
@Mock private UserManager mUserManager;
@@ -171,7 +170,7 @@
mTestScope.getBackgroundScope(),
new WindowRootViewVisibilityRepository(mBarService, mExecutor),
new FakeKeyguardRepository(),
- mHeadsUpManagerPhone,
+ mHeadsUpManager,
PowerInteractorFactory.create().getPowerInteractor());
mGutsManager = new NotificationGutsManager(
@@ -196,9 +195,10 @@
mWindowRootViewVisibilityInteractor,
mNotificationLockscreenUserManager,
mStatusBarStateController,
+ mBarService,
mDeviceProvisionedController,
mMetricsLogger,
- mHeadsUpManagerPhone,
+ mHeadsUpManager,
mActivityStarter);
mGutsManager.setUpWithPresenter(mPresenter, mNotificationListContainer,
mOnSettingsClickListener);
@@ -239,7 +239,7 @@
anyInt(),
anyBoolean(),
any(Runnable.class));
- verify(mHeadsUpManagerPhone).setGutsShown(realRow.getEntry(), true);
+ verify(mHeadsUpManager).setGutsShown(realRow.getEntry(), true);
assertEquals(View.VISIBLE, guts.getVisibility());
mGutsManager.closeAndSaveGuts(false, false, true, 0, 0, false);
@@ -247,7 +247,7 @@
verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean());
verify(row, times(1)).setGutsView(any());
mTestableLooper.processAllMessages();
- verify(mHeadsUpManagerPhone).setGutsShown(realRow.getEntry(), false);
+ verify(mHeadsUpManager).setGutsShown(realRow.getEntry(), false);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index ac680e6..cb73108 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -61,6 +61,7 @@
import com.android.systemui.media.controls.util.MediaFeatureFlag;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -81,14 +82,13 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder;
import com.android.systemui.statusbar.policy.SmartReplyConstants;
import com.android.systemui.statusbar.policy.SmartReplyStateInflater;
import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent;
-import com.android.systemui.res.R;
import com.android.systemui.wmshell.BubblesManager;
import com.android.systemui.wmshell.BubblesTestActivity;
@@ -123,7 +123,7 @@
private final GroupMembershipManager mGroupMembershipManager;
private final GroupExpansionManager mGroupExpansionManager;
private ExpandableNotificationRow mRow;
- private final HeadsUpManagerPhone mHeadsUpManager;
+ private final HeadsUpManager mHeadsUpManager;
private final NotifBindPipeline mBindPipeline;
private final NotifCollectionListener mBindPipelineEntryListener;
private final RowContentBindStage mBindStage;
@@ -161,7 +161,7 @@
mKeyguardBypassController = mock(KeyguardBypassController.class);
mGroupMembershipManager = mock(GroupMembershipManager.class);
mGroupExpansionManager = mock(GroupExpansionManager.class);
- mHeadsUpManager = mock(HeadsUpManagerPhone.class);
+ mHeadsUpManager = mock(HeadsUpManager.class);
mIconManager = new IconManager(
mock(CommonNotifCollection.class),
mock(LauncherApps.class),
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/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index ffe312b..20197e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -77,7 +77,6 @@
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider;
-import com.android.systemui.statusbar.notification.collection.provider.SeenNotificationsProviderImpl;
import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.NotifStats;
@@ -88,13 +87,15 @@
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController.NotificationPanelEvent;
import com.android.systemui.statusbar.notification.stack.NotificationSwipeHelper.NotificationCallback;
+import com.android.systemui.statusbar.notification.stack.data.repository.NotificationListRepository;
+import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationListInteractor;
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.tuner.TunerService;
@@ -124,7 +125,7 @@
@Mock private NotificationGutsManager mNotificationGutsManager;
@Mock private NotificationsController mNotificationsController;
@Mock private NotificationVisibilityProvider mVisibilityProvider;
- @Mock private HeadsUpManagerPhone mHeadsUpManager;
+ @Mock private HeadsUpManager mHeadsUpManager;
@Mock private NotificationRoundnessManager mNotificationRoundnessManager;
@Mock private TunerService mTunerService;
@Mock private DeviceProvisionedController mDeviceProvisionedController;
@@ -170,8 +171,8 @@
@Captor
private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerArgumentCaptor;
- private final SeenNotificationsProviderImpl mSeenNotificationsProvider =
- new SeenNotificationsProviderImpl();
+ private final NotificationListInteractor mNotificationListInteractor =
+ new NotificationListInteractor(new NotificationListRepository());
private NotificationStackScrollLayoutController mController;
@@ -503,7 +504,7 @@
@Test
public void testSetNotifStats_updatesHasFilteredOutSeenNotifications() {
initController(/* viewIsAttached= */ true);
- mSeenNotificationsProvider.setHasFilteredOutSeenNotifications(true);
+ mNotificationListInteractor.setHasFilteredOutSeenNotifications(true);
mController.getNotifStackController().setNotifStats(NotifStats.getEmpty());
verify(mNotificationStackScrollLayout).setHasFilteredOutSeenNotifications(true);
verify(mNotificationStackScrollLayout).updateFooter();
@@ -703,7 +704,7 @@
mUiEventLogger,
mRemoteInputManager,
mVisibilityLocationProviderDelegator,
- mSeenNotificationsProvider,
+ mNotificationListInteractor,
mShadeController,
mJankMonitor,
mStackLogger,
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 236bcb4..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
@@ -65,12 +65,12 @@
import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.systemui.ExpandHelper;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.res.R;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.EmptyShadeView;
@@ -81,9 +81,9 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
+import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.FooterView;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -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/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 93faa77..49906dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -5,18 +5,18 @@
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ShadeInterpolation.getContentAlpha
import com.android.systemui.dump.DumpManager
+import com.android.systemui.res.R
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
import com.android.systemui.statusbar.EmptyShadeView
import com.android.systemui.statusbar.NotificationShelf
import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
+import com.android.systemui.statusbar.notification.footer.ui.view.FooterView.FooterViewState
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
-import com.android.systemui.statusbar.notification.row.FooterView
-import com.android.systemui.statusbar.notification.row.FooterView.FooterViewState
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Expect
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index e254dd0..ac11ff2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -19,36 +19,35 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
+import com.android.SysUITestModule
+import com.android.TestMocksModule
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FakeFeatureFlagsClassicModule
+import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.scene.SceneTestUtils
-import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
+import com.android.systemui.res.R
import com.android.systemui.shade.data.repository.FakeShadeRepository
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository
-import com.android.systemui.statusbar.policy.DeviceProvisionedController
-import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
-import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.user.domain.UserDomainLayerModule
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 dagger.BindsInstance
+import dagger.Component
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -60,29 +59,27 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class SharedNotificationContainerViewModelTest : SysuiTestCase() {
- private val utils = SceneTestUtils(this)
- private val testScope = utils.testScope
+ private lateinit var testComponent: TestComponent
- private val disableFlagsRepository = FakeDisableFlagsRepository()
- private val userSetupRepository = FakeUserSetupRepository()
- private val shadeRepository = FakeShadeRepository()
- private val keyguardRepository = FakeKeyguardRepository()
- private val sceneContainerFlags = FakeSceneContainerFlags()
- private val sceneInteractor = utils.sceneInteractor()
-
- private lateinit var configurationRepository: FakeConfigurationRepository
- private lateinit var sharedNotificationContainerInteractor:
- SharedNotificationContainerInteractor
- private lateinit var underTest: SharedNotificationContainerViewModel
- private lateinit var keyguardInteractor: KeyguardInteractor
- private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
- private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository
- private lateinit var shadeInteractor: ShadeInteractor
+ private val shadeRepository
+ get() = testComponent.shadeRepository
+ private val keyguardRepository
+ get() = testComponent.keyguardRepository
+ private val configurationRepository
+ get() = testComponent.configurationRepository
+ private val sharedNotificationContainerInteractor: SharedNotificationContainerInteractor
+ get() = testComponent.sharedNotificationContainerInteractor
+ private val underTest: SharedNotificationContainerViewModel
+ get() = testComponent.underTest
+ private val keyguardInteractor: KeyguardInteractor
+ get() = testComponent.keyguardInteractor
+ private val keyguardTransitionRepository
+ get() = testComponent.keyguardTransitionRepository
+ private val testScope
+ get() = testComponent.testScope
@Mock private lateinit var notificationStackSizeCalculator: NotificationStackSizeCalculator
- @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
- @Mock private lateinit var userInteractor: UserInteractor
@Mock
private lateinit var notificationStackScrollLayoutController:
NotificationStackScrollLayoutController
@@ -94,43 +91,21 @@
whenever(notificationStackScrollLayoutController.getView()).thenReturn(mock())
whenever(notificationStackScrollLayoutController.getShelfHeight()).thenReturn(0)
- configurationRepository = FakeConfigurationRepository()
- KeyguardTransitionInteractorFactory.create(
- scope = testScope.backgroundScope,
- )
- .also {
- keyguardInteractor = it.keyguardInteractor
- keyguardTransitionInteractor = it.keyguardTransitionInteractor
- keyguardTransitionRepository = it.repository
- }
- sharedNotificationContainerInteractor =
- SharedNotificationContainerInteractor(
- configurationRepository,
- mContext,
- ResourcesSplitShadeStateController()
- )
- shadeInteractor =
- ShadeInteractor(
- testScope.backgroundScope,
- disableFlagsRepository,
- sceneContainerFlags,
- { sceneInteractor },
- keyguardRepository,
- userSetupRepository,
- deviceProvisionedController,
- userInteractor,
- sharedNotificationContainerInteractor,
- shadeRepository,
- )
- underTest =
- SharedNotificationContainerViewModel(
- sharedNotificationContainerInteractor,
- keyguardInteractor,
- keyguardTransitionInteractor,
- notificationStackSizeCalculator,
- notificationStackScrollLayoutController,
- shadeInteractor
- )
+ testComponent =
+ DaggerSharedNotificationContainerViewModelTest_TestComponent.factory()
+ .create(
+ test = this,
+ featureFlags =
+ FakeFeatureFlagsClassicModule {
+ set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+ },
+ mocks =
+ TestMocksModule(
+ notificationStackSizeCalculator = notificationStackSizeCalculator,
+ notificationStackScrollLayoutController =
+ notificationStackScrollLayoutController,
+ )
+ )
}
@Test
@@ -404,4 +379,34 @@
)
)
}
+
+ @SysUISingleton
+ @Component(
+ modules =
+ [
+ SysUITestModule::class,
+ UserDomainLayerModule::class,
+ ]
+ )
+ interface TestComponent {
+
+ val underTest: SharedNotificationContainerViewModel
+
+ val configurationRepository: FakeConfigurationRepository
+ val keyguardRepository: FakeKeyguardRepository
+ val keyguardInteractor: KeyguardInteractor
+ val keyguardTransitionRepository: FakeKeyguardTransitionRepository
+ val shadeRepository: FakeShadeRepository
+ val sharedNotificationContainerInteractor: SharedNotificationContainerInteractor
+ val testScope: TestScope
+
+ @Component.Factory
+ interface Factory {
+ fun create(
+ @BindsInstance test: SysuiTestCase,
+ featureFlags: FakeFeatureFlagsClassicModule,
+ mocks: TestMocksModule,
+ ): TestComponent
+ }
+ }
}
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/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
index a5d3484..e7dad6a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
@@ -57,6 +57,7 @@
import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
@@ -85,7 +86,7 @@
private final MetricsLogger mMetricsLogger = new FakeMetricsLogger();
@Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock private KeyguardStateController mKeyguardStateController;
- @Mock private HeadsUpManagerPhone mHeadsUpManager;
+ @Mock private HeadsUpManager mHeadsUpManager;
@Mock private WakefulnessLifecycle mWakefulnessLifecycle;
@Mock private DeviceProvisionedController mDeviceProvisionedController;
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
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 e33fa22..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;
@@ -85,7 +87,6 @@
import com.android.keyguard.TestScopeProvider;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.InitController;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
import com.android.systemui.animation.ActivityLaunchAnimator;
@@ -117,6 +118,7 @@
import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.domain.interactor.PowerInteractor;
+import com.android.systemui.res.R;
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.settings.brightness.BrightnessSliderController;
@@ -219,7 +221,7 @@
@Mock private KeyguardIndicationController mKeyguardIndicationController;
@Mock private NotificationStackScrollLayout mStackScroller;
@Mock private NotificationStackScrollLayoutController mStackScrollerController;
- @Mock private HeadsUpManagerPhone mHeadsUpManager;
+ @Mock private HeadsUpManager mHeadsUpManager;
@Mock private NotificationPanelViewController mNotificationPanelViewController;
@Mock private ShadeLogger mShadeLogger;
@Mock private NotificationPanelView mNotificationPanelView;
@@ -336,6 +338,7 @@
mFeatureFlags.set(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI, false);
// Set default value to avoid IllegalStateException.
mFeatureFlags.set(Flags.SHORTCUT_LIST_SEARCH_LAYOUT, false);
+ mFeatureFlags.setDefault(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR);
// For the Shade to respond to Back gesture, we must enable the event routing
mFeatureFlags.set(Flags.WM_SHADE_ALLOW_BACK_GESTURE, true);
// For the Shade to animate during the Back gesture, we must enable the animation flag.
@@ -343,6 +346,7 @@
mFeatureFlags.set(Flags.LIGHT_REVEAL_MIGRATION, true);
// Turn AOD on and toggle feature flag for jank fixes
mFeatureFlags.set(Flags.ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD, true);
+ mFeatureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, false);
when(mDozeParameters.getAlwaysOn()).thenReturn(true);
IThermalService thermalService = mock(IThermalService.class);
@@ -1108,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/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
index ff6f40d5..593c587 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
@@ -53,6 +53,7 @@
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import org.junit.Before;
import org.junit.Test;
@@ -72,7 +73,7 @@
private DozeServiceHost mDozeServiceHost;
- @Mock private HeadsUpManagerPhone mHeadsUpManager;
+ @Mock private HeadsUpManager mHeadsUpManager;
@Mock private ScrimController mScrimController;
@Mock private DozeScrimController mDozeScrimController;
@Mock private StatusBarStateControllerImpl mStatusBarStateController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index ec6286b..d84bb72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -47,6 +47,7 @@
import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.policy.Clock;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Assert;
@@ -72,7 +73,7 @@
private ExpandableNotificationRow mRow;
private NotificationEntry mEntry;
private HeadsUpStatusBarView mHeadsUpStatusBarView;
- private HeadsUpManagerPhone mHeadsUpManager;
+ private HeadsUpManager mHeadsUpManager;
private View mOperatorNameView;
private StatusBarStateController mStatusbarStateController;
private PhoneStatusBarTransitions mPhoneStatusBarTransitions;
@@ -93,7 +94,7 @@
mEntry = mRow.getEntry();
mHeadsUpStatusBarView = new HeadsUpStatusBarView(mContext, mock(View.class),
mock(TextView.class));
- mHeadsUpManager = mock(HeadsUpManagerPhone.class);
+ mHeadsUpManager = mock(HeadsUpManager.class);
mOperatorNameView = new View(mContext);
mStatusbarStateController = mock(StatusBarStateController.class);
mPhoneStatusBarTransitions = mock(PhoneStatusBarTransitions.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index 1bc522d..48b95d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -32,9 +32,9 @@
import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.res.R;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.res.R;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.AlertingNotificationManagerTest;
import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -43,7 +43,9 @@
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
+import com.android.systemui.util.kotlin.JavaAdapter;
import org.junit.After;
import org.junit.Before;
@@ -55,6 +57,8 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import kotlinx.coroutines.flow.StateFlowKt;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -69,9 +73,9 @@
@Mock private KeyguardBypassController mBypassController;
@Mock private ConfigurationControllerImpl mConfigurationController;
@Mock private AccessibilityManagerWrapper mAccessibilityManagerWrapper;
- @Mock private ShadeExpansionStateManager mShadeExpansionStateManager;
@Mock private UiEventLogger mUiEventLogger;
- private boolean mLivesPastNormalTime;
+ @Mock private JavaAdapter mJavaAdapter;
+ @Mock private ShadeInteractor mShadeInteractor;
private static final class TestableHeadsUpManagerPhone extends HeadsUpManagerPhone {
TestableHeadsUpManagerPhone(
@@ -85,7 +89,8 @@
Handler handler,
AccessibilityManagerWrapper accessibilityManagerWrapper,
UiEventLogger uiEventLogger,
- ShadeExpansionStateManager shadeExpansionStateManager
+ JavaAdapter javaAdapter,
+ ShadeInteractor shadeInteractor
) {
super(
context,
@@ -98,7 +103,8 @@
handler,
accessibilityManagerWrapper,
uiEventLogger,
- shadeExpansionStateManager
+ javaAdapter,
+ shadeInteractor
);
mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME;
@@ -117,7 +123,8 @@
mTestHandler,
mAccessibilityManagerWrapper,
mUiEventLogger,
- mShadeExpansionStateManager
+ mJavaAdapter,
+ mShadeInteractor
);
}
@@ -129,6 +136,7 @@
@Before
@Override
public void setUp() {
+ when(mShadeInteractor.isAnyExpanded()).thenReturn(StateFlowKt.MutableStateFlow(false));
final AccessibilityManagerWrapper accessibilityMgr =
mDependency.injectMockDependency(AccessibilityManagerWrapper.class);
when(accessibilityMgr.getRecommendedTimeoutMillis(anyInt(), anyInt()))
@@ -149,7 +157,7 @@
@Test
public void testSnooze() {
- final HeadsUpManagerPhone hmp = createHeadsUpManagerPhone();
+ final HeadsUpManager hmp = createHeadsUpManagerPhone();
final NotificationEntry entry = createEntry(/* id = */ 0);
hmp.showNotification(entry);
@@ -160,7 +168,7 @@
@Test
public void testSwipedOutNotification() {
- final HeadsUpManagerPhone hmp = createHeadsUpManagerPhone();
+ final HeadsUpManager hmp = createHeadsUpManagerPhone();
final NotificationEntry entry = createEntry(/* id = */ 0);
hmp.showNotification(entry);
@@ -176,7 +184,7 @@
@Test
public void testCanRemoveImmediately_swipedOut() {
- final HeadsUpManagerPhone hmp = createHeadsUpManagerPhone();
+ final HeadsUpManager hmp = createHeadsUpManagerPhone();
final NotificationEntry entry = createEntry(/* id = */ 0);
hmp.showNotification(entry);
@@ -189,7 +197,7 @@
@Ignore("b/141538055")
@Test
public void testCanRemoveImmediately_notTopEntry() {
- final HeadsUpManagerPhone hmp = createHeadsUpManagerPhone();
+ final HeadsUpManager hmp = createHeadsUpManagerPhone();
final NotificationEntry earlierEntry = createEntry(/* id = */ 0);
final NotificationEntry laterEntry = createEntry(/* id = */ 1);
laterEntry.setRow(mRow);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index 03f5f00..3556703 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -30,9 +30,11 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.doze.util.BurnInHelperKt;
+import com.android.systemui.log.LogBuffer;
+import com.android.systemui.log.core.FakeLogBuffer;
+import com.android.systemui.res.R;
import org.junit.After;
import org.junit.Before;
@@ -51,8 +53,7 @@
private static final float OPAQUE = 1.f;
private static final float TRANSPARENT = 0.f;
- @Mock
- private Resources mResources;
+ @Mock private Resources mResources;
private KeyguardClockPositionAlgorithm mClockPositionAlgorithm;
private KeyguardClockPositionAlgorithm.Result mClockPosition;
@@ -80,7 +81,8 @@
.mockStatic(BurnInHelperKt.class)
.startMocking();
- mClockPositionAlgorithm = new KeyguardClockPositionAlgorithm();
+ LogBuffer logBuffer = FakeLogBuffer.Factory.Companion.create();
+ mClockPositionAlgorithm = new KeyguardClockPositionAlgorithm(logBuffer);
when(mResources.getDimensionPixelSize(anyInt())).thenReturn(0);
mClockPositionAlgorithm.loadDimens(mResources);
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 bac8579..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
@@ -18,6 +18,7 @@
import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN;
import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -33,6 +34,7 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+
import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher;
import android.service.trust.TrustAgentService;
@@ -173,8 +175,8 @@
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);
when(mNotificationShadeWindowController.getWindowRootView())
.thenReturn(mNotificationShadeWindowView);
@@ -761,6 +763,30 @@
}
@Test
+ public void handleDispatchTouchEvent_alternateBouncerViewFlagEnabled() {
+ mStatusBarKeyguardViewManager.addCallback(mCallback);
+
+ // GIVEN alternate bouncer view flag enabled & the alternate bouncer is visible
+ mFeatureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, true);
+ when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
+
+ // THEN the touch is not acted upon
+ verify(mCallback, never()).onTouch(any());
+ }
+
+ @Test
+ public void onInterceptTouch_alternateBouncerViewFlagEnabled() {
+ // GIVEN alternate bouncer view flag enabled & the alternate bouncer is visible
+ mFeatureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, true);
+ when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
+
+ // THEN the touch is not intercepted
+ assertFalse(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(
+ MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
+ ));
+ }
+
+ @Test
public void handleDispatchTouchEvent_alternateBouncerNotVisible() {
mStatusBarKeyguardViewManager.addCallback(mCallback);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 8013e5e..beac995 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -92,6 +92,7 @@
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -218,7 +219,7 @@
mScreenOffAnimationController,
mStatusBarStateController).getPowerInteractor();
- HeadsUpManagerPhone headsUpManager = mock(HeadsUpManagerPhone.class);
+ HeadsUpManager headsUpManager = mock(HeadsUpManager.class);
NotificationLaunchAnimatorControllerProvider notificationAnimationProvider =
new NotificationLaunchAnimatorControllerProvider(
new NotificationExpansionRepository(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index 233f407..ee4f208 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -15,6 +15,7 @@
package com.android.systemui.statusbar.phone;
import static android.view.Display.DEFAULT_DISPLAY;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
@@ -59,6 +60,7 @@
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
@@ -106,7 +108,7 @@
mContext,
shadeViewController,
mock(QuickSettingsController.class),
- mock(HeadsUpManagerPhone.class),
+ mock(HeadsUpManager.class),
notificationShadeWindowView,
mock(ActivityStarter.class),
stackScrollLayoutController,
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/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
index 812e91b..610fede 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
@@ -60,6 +60,8 @@
override val isAllowedDuringAirplaneMode = MutableStateFlow(false)
+ override val hasPrioritizedNetworkCapabilities = MutableStateFlow(false)
+
fun setDataEnabled(enabled: Boolean) {
_dataEnabled.value = enabled
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt
index 57f97ec..1f8cc54 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt
@@ -123,6 +123,7 @@
carrierNetworkChange = testCase.carrierNetworkChange,
roaming = testCase.roaming,
name = "demo name",
+ slice = testCase.slice,
)
fakeNetworkEventFlow.value = networkModel
@@ -142,6 +143,7 @@
launch { conn.carrierName.collect {} }
launch { conn.isEmergencyOnly.collect {} }
launch { conn.dataConnectionState.collect {} }
+ launch { conn.hasPrioritizedNetworkCapabilities.collect {} }
}
return job
}
@@ -165,6 +167,7 @@
.isEqualTo(NetworkNameModel.IntentDerived(model.name))
assertThat(conn.carrierName.value)
.isEqualTo(NetworkNameModel.SubscriptionDerived("${model.name} ${model.subId}"))
+ assertThat(conn.hasPrioritizedNetworkCapabilities.value).isEqualTo(model.slice)
// TODO(b/261029387): check these once we start handling them
assertThat(conn.isEmergencyOnly.value).isFalse()
@@ -190,6 +193,7 @@
val carrierNetworkChange: Boolean,
val roaming: Boolean,
val name: String,
+ val slice: Boolean,
) {
override fun toString(): String {
return "INPUT(level=$level, " +
@@ -200,7 +204,8 @@
"activity=$activity, " +
"carrierNetworkChange=$carrierNetworkChange, " +
"roaming=$roaming, " +
- "name=$name)"
+ "name=$name," +
+ "slice=$slice)"
}
// Convenience for iterating test data and creating new cases
@@ -214,6 +219,7 @@
carrierNetworkChange: Boolean? = null,
roaming: Boolean? = null,
name: String? = null,
+ slice: Boolean? = null,
): TestCase =
TestCase(
level = level ?: this.level,
@@ -225,6 +231,7 @@
carrierNetworkChange = carrierNetworkChange ?: this.carrierNetworkChange,
roaming = roaming ?: this.roaming,
name = name ?: this.name,
+ slice = slice ?: this.slice,
)
}
@@ -254,6 +261,7 @@
// false first so the base case doesn't have roaming set (more common)
private val roaming = listOf(false, true)
private val names = listOf("name 1", "name 2")
+ private val slice = listOf(false, true)
@Parameters(name = "{0}") @JvmStatic fun data() = testData()
@@ -291,6 +299,7 @@
carrierNetworkChange.first(),
roaming.first(),
names.first(),
+ slice.first(),
)
val tail =
@@ -303,6 +312,7 @@
carrierNetworkChange.map { baseCase.modifiedBy(carrierNetworkChange = it) },
roaming.map { baseCase.modifiedBy(roaming = it) },
names.map { baseCase.modifiedBy(name = it) },
+ slice.map { baseCase.modifiedBy(slice = it) }
)
.flatten()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
index 2712b70..d918fa8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
@@ -549,6 +549,7 @@
launch { conn.carrierName.collect {} }
launch { conn.isEmergencyOnly.collect {} }
launch { conn.dataConnectionState.collect {} }
+ launch { conn.hasPrioritizedNetworkCapabilities.collect {} }
}
return job
}
@@ -574,6 +575,7 @@
.isEqualTo(NetworkNameModel.IntentDerived(model.name))
assertThat(conn.carrierName.value)
.isEqualTo(NetworkNameModel.SubscriptionDerived("${model.name} ${model.subId}"))
+ assertThat(conn.hasPrioritizedNetworkCapabilities.value).isEqualTo(model.slice)
// TODO(b/261029387) check these once we start handling them
assertThat(conn.isEmergencyOnly.value).isFalse()
@@ -599,6 +601,7 @@
assertThat(conn.isEmergencyOnly.value).isFalse()
assertThat(conn.isGsm.value).isFalse()
assertThat(conn.dataConnectionState.value).isEqualTo(DataConnectionState.Connected)
+ assertThat(conn.hasPrioritizedNetworkCapabilities.value).isFalse()
job.cancel()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
index ede02d1..1c21ebe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod
+import android.net.ConnectivityManager
import android.telephony.ServiceState
import android.telephony.SignalStrength
import android.telephony.TelephonyCallback
@@ -80,6 +81,7 @@
)
private val mobileFactory = mock<MobileConnectionRepositoryImpl.Factory>()
private val carrierMergedFactory = mock<CarrierMergedConnectionRepository.Factory>()
+ private val connectivityManager = mock<ConnectivityManager>()
private val subscriptionModel =
MutableStateFlow(
@@ -678,6 +680,7 @@
subscriptionModel,
DEFAULT_NAME_MODEL,
SEP,
+ connectivityManager,
telephonyManager,
systemUiCarrierConfig = mock(),
fakeBroadcastDispatcher,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
index 8ef82c9..ba64265 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
@@ -19,6 +19,8 @@
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
+import android.net.ConnectivityManager
+import android.net.ConnectivityManager.NetworkCallback
import android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WLAN
import android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN
import android.telephony.CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL
@@ -85,7 +87,9 @@
import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
@@ -107,6 +111,7 @@
private lateinit var underTest: MobileConnectionRepositoryImpl
private lateinit var connectionsRepo: FakeMobileConnectionsRepository
+ @Mock private lateinit var connectivityManager: ConnectivityManager
@Mock private lateinit var telephonyManager: TelephonyManager
@Mock private lateinit var logger: MobileInputLogger
@Mock private lateinit var tableLogger: TableLogBuffer
@@ -144,6 +149,7 @@
subscriptionModel,
DEFAULT_NAME_MODEL,
SEP,
+ connectivityManager,
telephonyManager,
systemUiCarrierConfig,
fakeBroadcastDispatcher,
@@ -904,6 +910,45 @@
assertThat(latest).isFalse()
}
+ @Test
+ fun hasPrioritizedCaps_defaultFalse() {
+ assertThat(underTest.hasPrioritizedNetworkCapabilities.value).isFalse()
+ }
+
+ @Test
+ fun hasPrioritizedCaps_trueWhenAvailable() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.hasPrioritizedNetworkCapabilities)
+
+ val callback: NetworkCallback =
+ withArgCaptor<NetworkCallback> {
+ verify(connectivityManager).registerNetworkCallback(any(), capture())
+ }
+
+ callback.onAvailable(mock())
+
+ assertThat(latest).isTrue()
+ }
+
+ @Test
+ fun hasPrioritizedCaps_becomesFalseWhenNetworkLost() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.hasPrioritizedNetworkCapabilities)
+
+ val callback: NetworkCallback =
+ withArgCaptor<NetworkCallback> {
+ verify(connectivityManager).registerNetworkCallback(any(), capture())
+ }
+
+ callback.onAvailable(mock())
+
+ assertThat(latest).isTrue()
+
+ callback.onLost(mock())
+
+ assertThat(latest).isFalse()
+ }
+
private inline fun <reified T> getTelephonyCallbackForType(): T {
return MobileTelephonyHelpers.getTelephonyCallbackForType(telephonyManager)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
index 852ed20..889f60a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod
+import android.net.ConnectivityManager
import android.telephony.ServiceState
import android.telephony.TelephonyCallback
import android.telephony.TelephonyCallback.CarrierNetworkListener
@@ -96,6 +97,7 @@
private lateinit var underTest: MobileConnectionRepositoryImpl
private lateinit var connectionsRepo: FakeMobileConnectionsRepository
+ @Mock private lateinit var connectivityManager: ConnectivityManager
@Mock private lateinit var telephonyManager: TelephonyManager
@Mock private lateinit var logger: MobileInputLogger
@Mock private lateinit var tableLogger: TableLogBuffer
@@ -129,6 +131,7 @@
subscriptionModel,
DEFAULT_NAME,
SEP,
+ connectivityManager,
telephonyManager,
systemUiCarrierConfig,
fakeBroadcastDispatcher,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index 9148c75..18ba6c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -180,6 +180,7 @@
MobileConnectionRepositoryImpl.Factory(
context,
fakeBroadcastDispatcher,
+ connectivityManager,
telephonyManager = telephonyManager,
bgDispatcher = dispatcher,
logger = logger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
index de2b6a8..5f4d7bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
@@ -48,6 +48,8 @@
NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G)
)
+ override val showSliceAttribution = MutableStateFlow(false)
+
override val networkName = MutableStateFlow(NetworkNameModel.IntentDerived("demo mode"))
override val carrierName = MutableStateFlow("demo mode")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt
index 218fd33..3936bed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt
@@ -22,13 +22,15 @@
import android.testing.TestableLooper.RunWithLooper
import android.testing.ViewUtils
import android.view.View
+import android.widget.FrameLayout
import android.widget.ImageView
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.flags.Flags
import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.res.R
import com.android.systemui.statusbar.StatusBarIconView
-import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconInteractor
@@ -58,8 +60,8 @@
private lateinit var testableLooper: TestableLooper
private val testDispatcher = UnconfinedTestDispatcher()
private val testScope = TestScope(testDispatcher)
+ private val flags = FakeFeatureFlagsClassic().also { it.set(Flags.NEW_NETWORK_SLICE_UI, false) }
- @Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
@Mock private lateinit var tableLogBuffer: TableLogBuffer
@Mock private lateinit var viewLogger: MobileViewLogger
@Mock private lateinit var constants: ConnectivityConstants
@@ -246,7 +248,8 @@
testableLooper.processAllMessages()
val color = 0x12345678
- view.onDarkChanged(arrayListOf(), 1.0f, color)
+ val contrast = 0x12344321
+ view.onDarkChangedWithContrast(arrayListOf(), color, contrast)
testableLooper.processAllMessages()
assertThat(view.getIconView().imageTintList).isEqualTo(ColorStateList.valueOf(color))
@@ -267,7 +270,8 @@
testableLooper.processAllMessages()
val color = 0x23456789
- view.setStaticDrawableColor(color)
+ val contrast = 0x12344321
+ view.setStaticDrawableColor(color, contrast)
testableLooper.processAllMessages()
assertThat(view.getIconView().imageTintList).isEqualTo(ColorStateList.valueOf(color))
@@ -275,6 +279,35 @@
ViewUtils.detachView(view)
}
+ @Test
+ fun colorChange_layersUpdateWithContrast() {
+ // Allow the slice, and set it to visible. This cause us to use special color logic
+ flags.set(Flags.NEW_NETWORK_SLICE_UI, true)
+ interactor.showSliceAttribution.value = true
+ createViewModel()
+
+ val view =
+ ModernStatusBarMobileView.constructAndBind(
+ context,
+ viewLogger,
+ SLOT_NAME,
+ viewModel,
+ )
+ ViewUtils.attachView(view)
+ testableLooper.processAllMessages()
+
+ val color = 0x23456789
+ val contrast = 0x12344321
+ view.setStaticDrawableColor(color, contrast)
+
+ testableLooper.processAllMessages()
+
+ assertThat(view.getNetTypeContainer().backgroundTintList).isEqualTo(color.colorState())
+ assertThat(view.getNetTypeView().imageTintList).isEqualTo(contrast.colorState())
+
+ ViewUtils.detachView(view)
+ }
+
private fun View.getGroupView(): View {
return this.requireViewById(R.id.mobile_group)
}
@@ -283,10 +316,20 @@
return this.requireViewById(R.id.mobile_signal)
}
+ private fun View.getNetTypeContainer(): FrameLayout {
+ return this.requireViewById(R.id.mobile_type_container)
+ }
+
+ private fun View.getNetTypeView(): ImageView {
+ return this.requireViewById(R.id.mobile_type)
+ }
+
private fun View.getDotView(): View {
return this.requireViewById(R.id.status_bar_dot)
}
+ private fun Int.colorState() = ColorStateList.valueOf(this)
+
private fun createViewModel() {
viewModelCommon =
MobileIconViewModel(
@@ -294,6 +337,7 @@
interactor,
airplaneModeInteractor,
constants,
+ flags,
testScope.backgroundScope,
)
viewModel = QsMobileIconViewModel(viewModelCommon)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
index 1878329..1d5487f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
@@ -16,8 +16,11 @@
package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.flags.Flags
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.statusbar.connectivity.MobileIconCarrierIdOverridesFake
import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
@@ -47,12 +50,14 @@
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
+@RunWith(AndroidJUnit4::class)
class LocationBasedMobileIconViewModelTest : SysuiTestCase() {
private lateinit var commonImpl: MobileIconViewModelCommon
private lateinit var homeIcon: HomeMobileIconViewModel
@@ -65,6 +70,7 @@
private lateinit var airplaneModeInteractor: AirplaneModeInteractor
private val connectivityRepository = FakeConnectivityRepository()
+ private val flags = FakeFeatureFlagsClassic().also { it.set(Flags.NEW_NETWORK_SLICE_UI, false) }
@Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
@Mock private lateinit var constants: ConnectivityConstants
@@ -133,6 +139,7 @@
interactor,
airplaneModeInteractor,
constants,
+ flags,
testScope.backgroundScope,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
index 796d5ec..c831e62 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH
import com.android.settingslib.AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH_NONE
@@ -26,7 +27,12 @@
import com.android.systemui.SysuiTestCase
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.flags.FakeFeatureFlagsClassic
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.Flags.NEW_NETWORK_SLICE_UI
import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.res.R
import com.android.systemui.statusbar.connectivity.MobileIconCarrierIdOverridesFake
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
@@ -54,12 +60,14 @@
import kotlinx.coroutines.yield
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
+@RunWith(AndroidJUnit4::class)
class MobileIconViewModelTest : SysuiTestCase() {
private var connectivityRepository = FakeConnectivityRepository()
@@ -74,6 +82,7 @@
@Mock private lateinit var tableLogBuffer: TableLogBuffer
@Mock private lateinit var carrierConfigTracker: CarrierConfigTracker
+ private val flags = FakeFeatureFlagsClassic().also { it.set(Flags.NEW_NETWORK_SLICE_UI, false) }
private val testDispatcher = UnconfinedTestDispatcher()
private val testScope = TestScope(testDispatcher)
@@ -595,6 +604,46 @@
containerJob.cancel()
}
+ @Test
+ fun netTypeBackground_flagOff_isNull() =
+ testScope.runTest {
+ flags.set(NEW_NETWORK_SLICE_UI, false)
+ createAndSetViewModel()
+
+ val latest by collectLastValue(underTest.networkTypeBackground)
+
+ repository.hasPrioritizedNetworkCapabilities.value = true
+
+ assertThat(latest).isNull()
+ }
+
+ @Test
+ fun netTypeBackground_flagOn_nullWhenNoPrioritizedCapabilities() =
+ testScope.runTest {
+ flags.set(NEW_NETWORK_SLICE_UI, true)
+ createAndSetViewModel()
+
+ val latest by collectLastValue(underTest.networkTypeBackground)
+
+ repository.hasPrioritizedNetworkCapabilities.value = false
+
+ assertThat(latest).isNull()
+ }
+
+ @Test
+ fun netTypeBackground_flagOn_notNullWhenPrioritizedCapabilities() =
+ testScope.runTest {
+ flags.set(NEW_NETWORK_SLICE_UI, true)
+ createAndSetViewModel()
+
+ val latest by collectLastValue(underTest.networkTypeBackground)
+
+ repository.hasPrioritizedNetworkCapabilities.value = true
+
+ assertThat(latest)
+ .isEqualTo(Icon.Resource(R.drawable.mobile_network_type_background, null))
+ }
+
private fun createAndSetViewModel() {
underTest =
MobileIconViewModel(
@@ -602,6 +651,7 @@
interactor,
airplaneModeInteractor,
constants,
+ flags,
testScope.backgroundScope,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
index eb6f2f8..f3e334e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
@@ -16,9 +16,12 @@
package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.mobile.TelephonyIcons
import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.flags.Flags
import com.android.systemui.statusbar.phone.StatusBarLocation
import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
@@ -41,15 +44,18 @@
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
+@RunWith(AndroidJUnit4::class)
class MobileIconsViewModelTest : SysuiTestCase() {
private lateinit var underTest: MobileIconsViewModel
private val interactor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
+ private val flags = FakeFeatureFlagsClassic().also { it.set(Flags.NEW_NETWORK_SLICE_UI, false) }
private lateinit var airplaneModeInteractor: AirplaneModeInteractor
@Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
@@ -77,6 +83,7 @@
interactor,
airplaneModeInteractor,
constants,
+ flags,
testScope.backgroundScope,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt
index e44ff8e..28d632d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt
@@ -41,6 +41,7 @@
* setting mobile connected && validated, since the default state is disconnected && not
* validated
*/
+ @JvmOverloads
fun setMobileConnected(
default: Boolean = true,
validated: Boolean = true,
@@ -53,6 +54,7 @@
}
/** Similar convenience method for ethernet */
+ @JvmOverloads
fun setEthernetConnected(
default: Boolean = true,
validated: Boolean = true,
@@ -64,6 +66,7 @@
)
}
+ @JvmOverloads
fun setWifiConnected(
default: Boolean = true,
validated: Boolean = true,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarViewTest.kt
index b4039d9..028a58c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarViewTest.kt
@@ -77,9 +77,10 @@
fun onDarkChanged_bindingReceivesIconAndDecorTint() {
val view = createAndInitView()
- view.onDarkChanged(arrayListOf(), 1.0f, 0x12345678)
+ view.onDarkChangedWithContrast(arrayListOf(), 0x12345678, 0x12344321)
assertThat(binding.iconTint).isEqualTo(0x12345678)
+ assertThat(binding.contrastTint).isEqualTo(0x12344321)
assertThat(binding.decorTint).isEqualTo(0x12345678)
}
@@ -87,9 +88,10 @@
fun setStaticDrawableColor_bindingReceivesIconTint() {
val view = createAndInitView()
- view.setStaticDrawableColor(0x12345678)
+ view.setStaticDrawableColor(0x12345678, 0x12344321)
assertThat(binding.iconTint).isEqualTo(0x12345678)
+ assertThat(binding.contrastTint).isEqualTo(0x12344321)
}
@Test
@@ -144,13 +146,15 @@
inner class TestBinding : ModernStatusBarViewBinding {
var iconTint: Int? = null
+ var contrastTint: Int? = null
var decorTint: Int? = null
var onVisibilityStateChangedCalled: Boolean = false
var shouldIconBeVisibleInternal: Boolean = true
- override fun onIconTintChanged(newTint: Int) {
+ override fun onIconTintChanged(newTint: Int, contrastTint: Int) {
iconTint = newTint
+ this.contrastTint = contrastTint
}
override fun onDecorTintChanged(newTint: Int) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
index dbaa29b..d06a6e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
@@ -20,7 +20,6 @@
import android.net.wifi.WifiManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.demomode.DemoMode
import com.android.systemui.demomode.DemoModeController
@@ -53,7 +52,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class WifiRepositorySwitcherTest : SysuiTestCase() {
private lateinit var underTest: WifiRepositorySwitcher
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt
index 206ac1d..ce00250 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt
@@ -18,7 +18,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
@@ -28,7 +27,6 @@
import org.junit.runner.RunWith
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class DisabledWifiRepositoryTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
index c2e75aa..cf20ba8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
@@ -35,7 +35,6 @@
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.log.table.TableLogBuffer
@@ -73,7 +72,6 @@
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class WifiRepositoryImplTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
index 1db8065..7fbbfc7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
@@ -19,7 +19,6 @@
import android.net.wifi.WifiManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
@@ -43,7 +42,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class WifiInteractorImplTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
index a27f899..d75a452 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
@@ -25,9 +25,9 @@
import android.view.ViewGroup
import android.widget.ImageView
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.res.R
import com.android.systemui.statusbar.StatusBarIconView.STATE_DOT
import com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN
import com.android.systemui.statusbar.StatusBarIconView.STATE_ICON
@@ -229,7 +229,8 @@
testableLooper.processAllMessages()
val color = 0x12345678
- view.onDarkChanged(arrayListOf(), 1.0f, color)
+ val contrast = 0x12344321
+ view.onDarkChangedWithContrast(arrayListOf(), color, contrast)
testableLooper.processAllMessages()
assertThat(view.getIconView().imageTintList).isEqualTo(ColorStateList.valueOf(color))
@@ -244,7 +245,8 @@
testableLooper.processAllMessages()
val color = 0x23456789
- view.setStaticDrawableColor(color)
+ val contrast = 0x12344321
+ view.setStaticDrawableColor(color, contrast)
testableLooper.processAllMessages()
assertThat(view.getIconView().imageTintList).isEqualTo(ColorStateList.valueOf(color))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
index 49a2648..2d1a27f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
@@ -19,7 +19,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.AccessibilityContentDescriptions.WIFI_OTHER_DEVICE_CONNECTION
-import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
import com.android.systemui.coroutines.collectLastValue
@@ -53,7 +52,6 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RoboPilotTest
@RunWith(AndroidJUnit4::class)
class WifiViewModelTest : SysuiTestCase() {
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/HeadsUpManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
index 64ebcd9..4f3f564 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
@@ -41,10 +41,13 @@
import android.app.Person;
import android.content.Context;
import android.content.Intent;
+import android.graphics.Region;
import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
@@ -73,7 +76,7 @@
private final HeadsUpManagerLogger mLogger = spy(new HeadsUpManagerLogger(logcatLogBuffer()));
@Mock private AccessibilityManagerWrapper mAccessibilityMgr;
- private final class TestableHeadsUpManager extends HeadsUpManager {
+ private final class TestableHeadsUpManager extends BaseHeadsUpManager {
TestableHeadsUpManager(Context context,
HeadsUpManagerLogger logger,
Handler handler,
@@ -85,9 +88,78 @@
mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME;
mStickyDisplayTime = TEST_STICKY_AUTO_DISMISS_TIME;
}
+
+ // The following are only implemented by HeadsUpManagerPhone. If you need them, use that.
+ @Override
+ public void addHeadsUpPhoneListener(@NonNull OnHeadsUpPhoneListenerChange listener) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void addSwipedOutNotification(@NonNull String key) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void extendHeadsUp() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Nullable
+ @Override
+ public Region getTouchableRegion() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isHeadsUpGoingAway() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void onExpandingFinished() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean removeNotification(@NonNull String key, boolean releaseImmediately,
+ boolean animate) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setAnimationStateHandler(@NonNull AnimationStateHandler handler) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setGutsShown(@NonNull NotificationEntry entry, boolean gutsShown) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setHeadsUpGoingAway(boolean headsUpGoingAway) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setRemoteInputActive(@NonNull NotificationEntry entry,
+ boolean remoteInputActive) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setTrackingHeadsUp(boolean tracking) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean shouldSwallowClick(@NonNull String key) {
+ throw new UnsupportedOperationException();
+ }
}
- private HeadsUpManager createHeadsUpManager() {
+ private BaseHeadsUpManager createHeadsUpManager() {
return new TestableHeadsUpManager(mContext, mLogger, mTestHandler, mAccessibilityMgr,
mUiEventLoggerFake);
}
@@ -165,9 +237,10 @@
@Test
public void testHunRemovedLogging() {
- final HeadsUpManager hum = createHeadsUpManager();
+ final BaseHeadsUpManager hum = createHeadsUpManager();
final NotificationEntry notifEntry = createEntry(/* id = */ 0);
- final HeadsUpManager.HeadsUpEntry headsUpEntry = mock(HeadsUpManager.HeadsUpEntry.class);
+ final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = mock(
+ BaseHeadsUpManager.HeadsUpEntry.class);
headsUpEntry.mEntry = notifEntry;
hum.onAlertEntryRemoved(headsUpEntry);
@@ -177,35 +250,37 @@
@Test
public void testShouldHeadsUpBecomePinned_hasFSI_notUnpinned_true() {
- final HeadsUpManager hum = createHeadsUpManager();
+ final BaseHeadsUpManager hum = createHeadsUpManager();
final NotificationEntry notifEntry = createFullScreenIntentEntry(/* id = */ 0);
// Add notifEntry to ANM mAlertEntries map and make it NOT unpinned
hum.showNotification(notifEntry);
- final HeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(notifEntry.getKey());
- headsUpEntry.wasUnpinned = false;
+ final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(
+ notifEntry.getKey());
+ headsUpEntry.mWasUnpinned = false;
assertTrue(hum.shouldHeadsUpBecomePinned(notifEntry));
}
@Test
public void testShouldHeadsUpBecomePinned_wasUnpinned_false() {
- final HeadsUpManager hum = createHeadsUpManager();
+ final BaseHeadsUpManager hum = createHeadsUpManager();
final NotificationEntry notifEntry = createFullScreenIntentEntry(/* id = */ 0);
// Add notifEntry to ANM mAlertEntries map and make it unpinned
hum.showNotification(notifEntry);
- final HeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(notifEntry.getKey());
- headsUpEntry.wasUnpinned = true;
+ final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(
+ notifEntry.getKey());
+ headsUpEntry.mWasUnpinned = true;
assertFalse(hum.shouldHeadsUpBecomePinned(notifEntry));
}
@Test
public void testShouldHeadsUpBecomePinned_noFSI_false() {
- final HeadsUpManager hum = createHeadsUpManager();
+ final BaseHeadsUpManager hum = createHeadsUpManager();
final NotificationEntry entry = createEntry(/* id = */ 0);
assertFalse(hum.shouldHeadsUpBecomePinned(entry));
@@ -214,7 +289,7 @@
@Test
public void testShowNotification_autoDismissesIncludingTouchAcceptanceDelay() {
- final HeadsUpManager hum = createHeadsUpManager();
+ final BaseHeadsUpManager hum = createHeadsUpManager();
final NotificationEntry entry = createEntry(/* id = */ 0);
useAccessibilityTimeout(false);
@@ -228,7 +303,7 @@
@Test
public void testShowNotification_autoDismissesWithDefaultTimeout() {
- final HeadsUpManager hum = createHeadsUpManager();
+ final BaseHeadsUpManager hum = createHeadsUpManager();
final NotificationEntry entry = createEntry(/* id = */ 0);
useAccessibilityTimeout(false);
@@ -242,7 +317,7 @@
@Test
public void testShowNotification_stickyForSomeTime_autoDismissesWithStickyTimeout() {
- final HeadsUpManager hum = createHeadsUpManager();
+ final BaseHeadsUpManager hum = createHeadsUpManager();
final NotificationEntry entry = createStickyForSomeTimeEntry(/* id = */ 0);
useAccessibilityTimeout(false);
@@ -256,7 +331,7 @@
@Test
public void testShowNotification_sticky_neverAutoDismisses() {
- final HeadsUpManager hum = createHeadsUpManager();
+ final BaseHeadsUpManager hum = createHeadsUpManager();
final NotificationEntry entry = createStickyEntry(/* id = */ 0);
useAccessibilityTimeout(false);
@@ -278,7 +353,7 @@
@Test
public void testShowNotification_autoDismissesWithAccessibilityTimeout() {
- final HeadsUpManager hum = createHeadsUpManager();
+ final BaseHeadsUpManager hum = createHeadsUpManager();
final NotificationEntry entry = createEntry(/* id = */ 0);
useAccessibilityTimeout(true);
@@ -292,7 +367,7 @@
@Test
public void testShowNotification_stickyForSomeTime_autoDismissesWithAccessibilityTimeout() {
- final HeadsUpManager hum = createHeadsUpManager();
+ final BaseHeadsUpManager hum = createHeadsUpManager();
final NotificationEntry entry = createStickyForSomeTimeEntry(/* id = */ 0);
useAccessibilityTimeout(true);
@@ -306,7 +381,7 @@
@Test
public void testRemoveNotification_beforeMinimumDisplayTime() {
- final HeadsUpManager hum = createHeadsUpManager();
+ final BaseHeadsUpManager hum = createHeadsUpManager();
final NotificationEntry entry = createEntry(/* id = */ 0);
useAccessibilityTimeout(false);
@@ -329,7 +404,7 @@
@Test
public void testRemoveNotification_afterMinimumDisplayTime() {
- final HeadsUpManager hum = createHeadsUpManager();
+ final BaseHeadsUpManager hum = createHeadsUpManager();
final NotificationEntry entry = createEntry(/* id = */ 0);
useAccessibilityTimeout(false);
@@ -366,7 +441,7 @@
@Test
public void testRemoveNotification_releaseImmediately() {
- final HeadsUpManager hum = createHeadsUpManager();
+ final BaseHeadsUpManager hum = createHeadsUpManager();
final NotificationEntry entry = createEntry(/* id = */ 0);
hum.showNotification(entry);
@@ -382,14 +457,15 @@
@Test
public void testIsSticky_rowPinnedAndExpanded_true() {
- final HeadsUpManager hum = createHeadsUpManager();
+ final BaseHeadsUpManager hum = createHeadsUpManager();
final NotificationEntry notifEntry = createEntry(/* id = */ 0);
when(mRow.isPinned()).thenReturn(true);
notifEntry.setRow(mRow);
hum.showNotification(notifEntry);
- final HeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(notifEntry.getKey());
+ final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(
+ notifEntry.getKey());
headsUpEntry.setExpanded(true);
assertTrue(hum.isSticky(notifEntry.getKey()));
@@ -397,20 +473,21 @@
@Test
public void testIsSticky_remoteInputActive_true() {
- final HeadsUpManager hum = createHeadsUpManager();
+ final BaseHeadsUpManager hum = createHeadsUpManager();
final NotificationEntry notifEntry = createEntry(/* id = */ 0);
hum.showNotification(notifEntry);
- final HeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(notifEntry.getKey());
- headsUpEntry.remoteInputActive = true;
+ final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(
+ notifEntry.getKey());
+ headsUpEntry.mRemoteInputActive = true;
assertTrue(hum.isSticky(notifEntry.getKey()));
}
@Test
public void testIsSticky_hasFullScreenIntent_true() {
- final HeadsUpManager hum = createHeadsUpManager();
+ final BaseHeadsUpManager hum = createHeadsUpManager();
final NotificationEntry notifEntry = createFullScreenIntentEntry(/* id = */ 0);
hum.showNotification(notifEntry);
@@ -421,7 +498,7 @@
@Test
public void testIsSticky_stickyForSomeTime_false() {
- final HeadsUpManager hum = createHeadsUpManager();
+ final BaseHeadsUpManager hum = createHeadsUpManager();
final NotificationEntry entry = createStickyForSomeTimeEntry(/* id = */ 0);
hum.showNotification(entry);
@@ -432,21 +509,22 @@
@Test
public void testIsSticky_false() {
- final HeadsUpManager hum = createHeadsUpManager();
+ final BaseHeadsUpManager hum = createHeadsUpManager();
final NotificationEntry notifEntry = createEntry(/* id = */ 0);
hum.showNotification(notifEntry);
- final HeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(notifEntry.getKey());
+ final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(
+ notifEntry.getKey());
headsUpEntry.setExpanded(false);
- headsUpEntry.remoteInputActive = false;
+ headsUpEntry.mRemoteInputActive = false;
assertFalse(hum.isSticky(notifEntry.getKey()));
}
@Test
public void testCompareTo_withNullEntries() {
- final HeadsUpManager hum = createHeadsUpManager();
+ final BaseHeadsUpManager hum = createHeadsUpManager();
final NotificationEntry alertEntry = new NotificationEntryBuilder().setTag("alert").build();
hum.showNotification(alertEntry);
@@ -458,7 +536,7 @@
@Test
public void testCompareTo_withNonAlertEntries() {
- final HeadsUpManager hum = createHeadsUpManager();
+ final BaseHeadsUpManager hum = createHeadsUpManager();
final NotificationEntry nonAlertEntry1 = new NotificationEntryBuilder().setTag(
"nae1").build();
@@ -474,9 +552,9 @@
@Test
public void testAlertEntryCompareTo_ongoingCallLessThanActiveRemoteInput() {
- final HeadsUpManager hum = createHeadsUpManager();
+ final BaseHeadsUpManager hum = createHeadsUpManager();
- final HeadsUpManager.HeadsUpEntry ongoingCall = hum.new HeadsUpEntry();
+ final BaseHeadsUpManager.HeadsUpEntry ongoingCall = hum.new HeadsUpEntry();
ongoingCall.setEntry(new NotificationEntryBuilder()
.setSbn(createSbn(/* id = */ 0,
new Notification.Builder(mContext, "")
@@ -484,9 +562,9 @@
.setOngoing(true)))
.build());
- final HeadsUpManager.HeadsUpEntry activeRemoteInput = hum.new HeadsUpEntry();
+ final BaseHeadsUpManager.HeadsUpEntry activeRemoteInput = hum.new HeadsUpEntry();
activeRemoteInput.setEntry(createEntry(/* id = */ 1));
- activeRemoteInput.remoteInputActive = true;
+ activeRemoteInput.mRemoteInputActive = true;
assertThat(ongoingCall.compareTo(activeRemoteInput)).isLessThan(0);
assertThat(activeRemoteInput.compareTo(ongoingCall)).isGreaterThan(0);
@@ -494,9 +572,9 @@
@Test
public void testAlertEntryCompareTo_incomingCallLessThanActiveRemoteInput() {
- final HeadsUpManager hum = createHeadsUpManager();
+ final BaseHeadsUpManager hum = createHeadsUpManager();
- final HeadsUpManager.HeadsUpEntry incomingCall = hum.new HeadsUpEntry();
+ final BaseHeadsUpManager.HeadsUpEntry incomingCall = hum.new HeadsUpEntry();
final Person person = new Person.Builder().setName("person").build();
final PendingIntent intent = mock(PendingIntent.class);
incomingCall.setEntry(new NotificationEntryBuilder()
@@ -506,9 +584,9 @@
.forIncomingCall(person, intent, intent))))
.build());
- final HeadsUpManager.HeadsUpEntry activeRemoteInput = hum.new HeadsUpEntry();
+ final BaseHeadsUpManager.HeadsUpEntry activeRemoteInput = hum.new HeadsUpEntry();
activeRemoteInput.setEntry(createEntry(/* id = */ 1));
- activeRemoteInput.remoteInputActive = true;
+ activeRemoteInput.mRemoteInputActive = true;
assertThat(incomingCall.compareTo(activeRemoteInput)).isLessThan(0);
assertThat(activeRemoteInput.compareTo(incomingCall)).isGreaterThan(0);
@@ -516,10 +594,10 @@
@Test
public void testPinEntry_logsPeek() {
- final HeadsUpManager hum = createHeadsUpManager();
+ final BaseHeadsUpManager hum = createHeadsUpManager();
// Needs full screen intent in order to be pinned
- final HeadsUpManager.HeadsUpEntry entryToPin = hum.new HeadsUpEntry();
+ final BaseHeadsUpManager.HeadsUpEntry entryToPin = hum.new HeadsUpEntry();
entryToPin.setEntry(createFullScreenIntentEntry(/* id = */ 0));
// Note: the standard way to show a notification would be calling showNotification rather
@@ -530,13 +608,13 @@
hum.onAlertEntryAdded(entryToPin);
assertEquals(1, mUiEventLoggerFake.numLogs());
- assertEquals(HeadsUpManager.NotificationPeekEvent.NOTIFICATION_PEEK.getId(),
+ assertEquals(BaseHeadsUpManager.NotificationPeekEvent.NOTIFICATION_PEEK.getId(),
mUiEventLoggerFake.eventId(0));
}
@Test
public void testSetUserActionMayIndirectlyRemove() {
- final HeadsUpManager hum = createHeadsUpManager();
+ final BaseHeadsUpManager hum = createHeadsUpManager();
final NotificationEntry notifEntry = createEntry(/* id = */ 0);
hum.showNotification(notifEntry);
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/statusbar/policy/data/repository/DeviceProvisioningRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepositoryImplTest.kt
new file mode 100644
index 0000000..12694ae
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepositoryImplTest.kt
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.statusbar.policy.data.repository
+
+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.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.mockito.withArgCaptor
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceProvisioningRepositoryImplTest : SysuiTestCase() {
+
+ @Mock lateinit var deviceProvisionedController: DeviceProvisionedController
+
+ lateinit var underTest: DeviceProvisioningRepositoryImpl
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ underTest =
+ DeviceProvisioningRepositoryImpl(
+ deviceProvisionedController,
+ )
+ }
+
+ @Test
+ fun isDeviceProvisioned_reflectsCurrentControllerState() = runTest {
+ whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ val deviceProvisioned by collectLastValue(underTest.isDeviceProvisioned)
+ assertThat(deviceProvisioned).isTrue()
+ }
+
+ @Test
+ fun isDeviceProvisioned_updatesWhenControllerStateChanges_toTrue() = runTest {
+ val deviceProvisioned by collectLastValue(underTest.isDeviceProvisioned)
+ runCurrent()
+ whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ withArgCaptor { verify(deviceProvisionedController).addCallback(capture()) }
+ .onDeviceProvisionedChanged()
+ assertThat(deviceProvisioned).isTrue()
+ }
+
+ @Test
+ fun isDeviceProvisioned_updatesWhenControllerStateChanges_toFalse() = runTest {
+ val deviceProvisioned by collectLastValue(underTest.isDeviceProvisioned)
+ runCurrent()
+ whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(false)
+ withArgCaptor { verify(deviceProvisionedController).addCallback(capture()) }
+ .onDeviceProvisionedChanged()
+ assertThat(deviceProvisioned).isFalse()
+ }
+
+ @Test
+ fun isFrpActive_reflectsCurrentControllerState() = runTest {
+ whenever(deviceProvisionedController.isFrpActive).thenReturn(true)
+ val frpActive by collectLastValue(underTest.isFactoryResetProtectionActive)
+ assertThat(frpActive).isTrue()
+ }
+
+ @Test
+ fun isFrpActive_updatesWhenControllerStateChanges_toTrue() = runTest {
+ val frpActive by collectLastValue(underTest.isFactoryResetProtectionActive)
+ runCurrent()
+ whenever(deviceProvisionedController.isFrpActive).thenReturn(true)
+ withArgCaptor { verify(deviceProvisionedController).addCallback(capture()) }
+ .onFrpActiveChanged()
+ assertThat(frpActive).isTrue()
+ }
+
+ @Test
+ fun isFrpActive_updatesWhenControllerStateChanges_toFalse() = runTest {
+ val frpActive by collectLastValue(underTest.isFactoryResetProtectionActive)
+ runCurrent()
+ whenever(deviceProvisionedController.isFrpActive).thenReturn(false)
+ withArgCaptor { verify(deviceProvisionedController).addCallback(capture()) }
+ .onFrpActiveChanged()
+ assertThat(frpActive).isFalse()
+ }
+}
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/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
index af941d0..c56266d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
@@ -155,6 +155,9 @@
@Test
fun createUserInteractor_nonProcessUser_startsSecondaryService() {
+ val userId = Process.myUserHandle().identifier + 1
+ whenever(manager.aliveUsers).thenReturn(listOf(createUserInfo(userId, "abc")))
+
createUserInteractor(false /* startAsProcessUser */)
verify(spyContext).startServiceAsUser(any(), any())
}
@@ -655,9 +658,10 @@
@Test
fun userSwitchedBroadcast() {
- createUserInteractor()
testScope.runTest {
val userInfos = createUserInfos(count = 2, includeGuest = false)
+ whenever(manager.aliveUsers).thenReturn(userInfos)
+ createUserInteractor()
userRepository.setUserInfos(userInfos)
userRepository.setSelectedUserInfo(userInfos[0])
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
@@ -728,6 +732,26 @@
}
@Test
+ fun localeChanged_refreshUsers() {
+ createUserInteractor()
+ testScope.runTest {
+ val userInfos = createUserInfos(count = 2, includeGuest = false)
+ userRepository.setUserInfos(userInfos)
+ userRepository.setSelectedUserInfo(userInfos[0])
+ runCurrent()
+ val refreshUsersCallCount = userRepository.refreshUsersCallCount
+
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ spyContext,
+ Intent(Intent.ACTION_LOCALE_CHANGED)
+ )
+ runCurrent()
+
+ assertThat(userRepository.refreshUsersCallCount).isEqualTo(refreshUsersCallCount + 1)
+ }
+ }
+
+ @Test
fun nonSystemUserUnlockedBroadcast_doNotRefreshUsers() {
createUserInteractor()
testScope.runTest {
@@ -985,6 +1009,13 @@
}
}
+ @Test
+ fun initWithNoAliveUsers() {
+ whenever(manager.aliveUsers).thenReturn(listOf())
+ createUserInteractor()
+ verify(spyContext, never()).startServiceAsUser(any(), any())
+ }
+
private fun assertUsers(
models: List<UserModel>?,
count: Int,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
index 6932f5e..c236b12 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
@@ -28,6 +28,7 @@
import com.android.systemui.GuestResumeSessionReceiver
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Text
+import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
@@ -347,6 +348,24 @@
}
@Test
+ fun isFinishRequested_finishesWhenUserButtonIsClicked() =
+ testScope.runTest {
+ setUsers(count = 2)
+ val isFinishRequested = mutableListOf<Boolean>()
+ val job =
+ launch(testDispatcher) { underTest.isFinishRequested.toList(isFinishRequested) }
+
+ val userViewModels = collectLastValue(underTest.users)
+ assertThat(isFinishRequested.last()).isFalse()
+
+ userViewModels.invoke()?.firstOrNull()?.onClicked?.invoke()
+
+ assertThat(isFinishRequested.last()).isTrue()
+
+ job.cancel()
+ }
+
+ @Test
fun guestSelected_nameIsExitGuest() =
testScope.runTest {
val userInfos =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt
new file mode 100644
index 0000000..aaf8d07
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt
@@ -0,0 +1,98 @@
+/*
+ * 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.ui
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class AnimatedValueTest : SysuiTestCase() {
+
+ @Test
+ fun animatableEvent_updatesValue() = runTest {
+ val events = MutableSharedFlow<AnimatableEvent<Int>>()
+ val values = events.toAnimatedValueFlow(completionEvents = emptyFlow())
+ val value by collectLastValue(values)
+ runCurrent()
+
+ events.emit(AnimatableEvent(value = 1, startAnimating = false))
+
+ assertThat(value).isEqualTo(AnimatedValue(value = 1, isAnimating = false))
+ }
+
+ @Test
+ fun animatableEvent_startAnimation() = runTest {
+ val events = MutableSharedFlow<AnimatableEvent<Int>>()
+ val values = events.toAnimatedValueFlow(completionEvents = emptyFlow())
+ val value by collectLastValue(values)
+ runCurrent()
+
+ events.emit(AnimatableEvent(value = 1, startAnimating = true))
+
+ assertThat(value).isEqualTo(AnimatedValue(value = 1, isAnimating = true))
+ }
+
+ @Test
+ fun animatableEvent_startAnimation_alreadyAnimating() = runTest {
+ val events = MutableSharedFlow<AnimatableEvent<Int>>()
+ val values = events.toAnimatedValueFlow(completionEvents = emptyFlow())
+ val value by collectLastValue(values)
+ runCurrent()
+
+ events.emit(AnimatableEvent(value = 1, startAnimating = true))
+ events.emit(AnimatableEvent(value = 2, startAnimating = true))
+
+ assertThat(value).isEqualTo(AnimatedValue(value = 2, isAnimating = true))
+ }
+
+ @Test
+ fun animatedValue_stopAnimating() = runTest {
+ val events = MutableSharedFlow<AnimatableEvent<Int>>()
+ val stopEvent = MutableSharedFlow<Unit>()
+ val values = events.toAnimatedValueFlow(completionEvents = stopEvent)
+ val value by collectLastValue(values)
+ runCurrent()
+
+ events.emit(AnimatableEvent(value = 1, startAnimating = true))
+ stopEvent.emit(Unit)
+
+ assertThat(value).isEqualTo(AnimatedValue(value = 1, isAnimating = false))
+ }
+
+ @Test
+ fun animatedValue_stopAnimating_notAnimating() = runTest {
+ val events = MutableSharedFlow<AnimatableEvent<Int>>()
+ val stopEvent = MutableSharedFlow<Unit>()
+ val values = events.toAnimatedValueFlow(completionEvents = stopEvent)
+ values.launchIn(backgroundScope)
+ runCurrent()
+
+ events.emit(AnimatableEvent(value = 1, startAnimating = false))
+
+ assertThat(stopEvent.subscriptionCount.value).isEqualTo(0)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index 28fc5db..b8f747b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -42,6 +42,7 @@
import android.content.res.Configuration;
import android.media.AudioManager;
import android.os.SystemClock;
+import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.Log;
@@ -70,6 +71,10 @@
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.FakeConfigurationController;
+import com.android.systemui.util.settings.FakeSettings;
+import com.android.systemui.util.settings.SecureSettings;
+
+import dagger.Lazy;
import org.junit.After;
import org.junit.Before;
@@ -122,6 +127,8 @@
@Mock CsdWarningDialog mCsdWarningDialog;
@Mock
DevicePostureController mPostureController;
+ @Mock
+ private Lazy<SecureSettings> mLazySecureSettings;
private final CsdWarningDialog.Factory mCsdWarningDialogFactory =
new CsdWarningDialog.Factory() {
@@ -133,6 +140,7 @@
private FakeFeatureFlags mFeatureFlags;
private int mLongestHideShowAnimationDuration = 250;
+ private FakeSettings mSecureSettings;
@Rule
public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule();
@@ -162,6 +170,10 @@
mFeatureFlags = new FakeFeatureFlags();
+ mSecureSettings = new FakeSettings();
+
+ when(mLazySecureSettings.get()).thenReturn(mSecureSettings);
+
mDialog = new VolumeDialogImpl(
getContext(),
mVolumeDialogController,
@@ -177,7 +189,8 @@
mPostureController,
mTestableLooper.getLooper(),
mDumpManager,
- mFeatureFlags);
+ mFeatureFlags,
+ mLazySecureSettings);
mDialog.init(0, null);
State state = createShellState();
mDialog.onStateChangedH(state);
@@ -242,6 +255,17 @@
}
@Test
+ public void testSetTimeoutValue_ComputeTimeout() {
+ mSecureSettings.putInt(Settings.Secure.VOLUME_DIALOG_DISMISS_TIMEOUT, 7000);
+ Mockito.reset(mAccessibilityMgr);
+ mDialog.init(0, null);
+ mDialog.rescheduleTimeoutH();
+ verify(mAccessibilityMgr).getRecommendedTimeoutMillis(
+ 7000,
+ AccessibilityManager.FLAG_CONTENT_CONTROLS);
+ }
+
+ @Test
public void testComputeTimeout_tooltip() {
Mockito.reset(mAccessibilityMgr);
mDialog.showCaptionsTooltip();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 409ba48..123362a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -86,20 +86,36 @@
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.keyguard.KeyguardSecurityModel;
import com.android.launcher3.icons.BubbleIconFactory;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository;
+import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
+import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.flags.FakeFeatureFlagsClassic;
import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.keyguard.data.repository.FakeCommandQueue;
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository;
+import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor;
+import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.power.data.repository.FakePowerRepository;
+import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.scene.FakeWindowRootViewComponent;
import com.android.systemui.scene.SceneTestUtils;
+import com.android.systemui.scene.data.repository.SceneContainerRepository;
+import com.android.systemui.scene.domain.interactor.SceneInteractor;
import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags;
+import com.android.systemui.scene.shared.logger.SceneLogger;
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.NotificationShadeWindowControllerImpl;
@@ -139,7 +155,8 @@
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.user.domain.interactor.UserInteractor;
+import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository;
+import com.android.systemui.user.data.repository.FakeUserRepository;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.bubbles.Bubble;
@@ -329,6 +346,8 @@
private UserHandle mUser0;
private FakeBubbleProperties mBubbleProperties;
+ private FromLockscreenTransitionInteractor mFromLockscreenTransitionInteractor;
+ private FromPrimaryBouncerTransitionInteractor mFromPrimaryBouncerTransitionInteractor;
@Before
public void setUp() throws Exception {
@@ -350,21 +369,94 @@
when(mNotificationShadeWindowView.getViewTreeObserver())
.thenReturn(mock(ViewTreeObserver.class));
- mShadeInteractor = new ShadeInteractor(
+
+ FakeDeviceProvisioningRepository deviceProvisioningRepository =
+ new FakeDeviceProvisioningRepository();
+ deviceProvisioningRepository.setDeviceProvisioned(true);
+ FakeKeyguardRepository keyguardRepository = new FakeKeyguardRepository();
+ FakeFeatureFlagsClassic featureFlags = new FakeFeatureFlagsClassic();
+ FakeShadeRepository shadeRepository = new FakeShadeRepository();
+ FakePowerRepository powerRepository = new FakePowerRepository();
+ FakeConfigurationRepository configurationRepository = new FakeConfigurationRepository();
+
+ PowerInteractor powerInteractor = new PowerInteractor(
+ powerRepository,
+ new FalsingCollectorFake(),
+ mock(ScreenOffAnimationController.class),
+ mStatusBarStateController);
+
+ SceneInteractor sceneInteractor = new SceneInteractor(
mTestScope.getBackgroundScope(),
- new FakeDisableFlagsRepository(),
- new FakeSceneContainerFlags(),
- mUtils::sceneInteractor,
- new FakeKeyguardRepository(),
- new FakeUserSetupRepository(),
- mock(DeviceProvisionedController.class),
- mock(UserInteractor.class),
- new SharedNotificationContainerInteractor(
- new FakeConfigurationRepository(),
- mContext,
- new ResourcesSplitShadeStateController()),
- new FakeShadeRepository()
- );
+ new SceneContainerRepository(
+ mTestScope.getBackgroundScope(),
+ mUtils.fakeSceneContainerConfig(mUtils.fakeSceneKeys())),
+ powerRepository,
+ mock(SceneLogger.class));
+
+ FakeSceneContainerFlags sceneContainerFlags = new FakeSceneContainerFlags();
+ KeyguardInteractor keyguardInteractor = new KeyguardInteractor(
+ keyguardRepository,
+ new FakeCommandQueue(),
+ powerInteractor,
+ featureFlags,
+ sceneContainerFlags,
+ new FakeDeviceEntryRepository(),
+ new FakeKeyguardBouncerRepository(),
+ configurationRepository,
+ shadeRepository,
+ () -> sceneInteractor);
+
+ FakeKeyguardTransitionRepository keyguardTransitionRepository =
+ new FakeKeyguardTransitionRepository();
+
+ KeyguardTransitionInteractor keyguardTransitionInteractor =
+ new KeyguardTransitionInteractor(
+ mTestScope.getBackgroundScope(),
+ keyguardTransitionRepository,
+ () -> keyguardInteractor,
+ () -> mFromLockscreenTransitionInteractor,
+ () -> mFromPrimaryBouncerTransitionInteractor);
+
+ mFromLockscreenTransitionInteractor = new FromLockscreenTransitionInteractor(
+ keyguardTransitionRepository,
+ keyguardTransitionInteractor,
+ mTestScope.getBackgroundScope(),
+ keyguardInteractor,
+ featureFlags,
+ shadeRepository,
+ powerInteractor);
+
+ mFromPrimaryBouncerTransitionInteractor = new FromPrimaryBouncerTransitionInteractor(
+ keyguardTransitionRepository,
+ keyguardTransitionInteractor,
+ mTestScope.getBackgroundScope(),
+ keyguardInteractor,
+ featureFlags,
+ mock(KeyguardSecurityModel.class),
+ powerInteractor);
+
+ ResourcesSplitShadeStateController splitShadeStateController =
+ new ResourcesSplitShadeStateController();
+
+ mShadeInteractor =
+ new ShadeInteractor(
+ mTestScope.getBackgroundScope(),
+ deviceProvisioningRepository,
+ new FakeDisableFlagsRepository(),
+ mDozeParameters,
+ sceneContainerFlags,
+ () -> sceneInteractor,
+ keyguardRepository,
+ keyguardTransitionInteractor,
+ powerInteractor,
+ new FakeUserSetupRepository(),
+ new FakeUserRepository(),
+ new SharedNotificationContainerInteractor(
+ configurationRepository,
+ mContext,
+ splitShadeStateController),
+ new FakeShadeRepository()
+ );
mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(
mContext,
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/android/animation/PlatformAnimatorIsolationRule.kt b/packages/SystemUI/tests/utils/src/android/animation/PlatformAnimatorIsolationRule.kt
index 43a26f3..ca5e1d0 100644
--- a/packages/SystemUI/tests/utils/src/android/animation/PlatformAnimatorIsolationRule.kt
+++ b/packages/SystemUI/tests/utils/src/android/animation/PlatformAnimatorIsolationRule.kt
@@ -41,7 +41,7 @@
private fun onError() =
exceptionDeferrer.fail(
"Test's animations are not isolated! " +
- "Did you forget to add an AnimatorTestRule to your test class?"
+ "Did you forget to add an AnimatorTestRule as a @Rule?"
)
fun throwDeferred() = exceptionDeferrer.throwDeferred()
diff --git a/packages/SystemUI/tests/utils/src/androidx/core/animation/AndroidXAnimatorIsolationRule.kt b/packages/SystemUI/tests/utils/src/androidx/core/animation/AndroidXAnimatorIsolationRule.kt
index 7a97029..95335a6 100644
--- a/packages/SystemUI/tests/utils/src/androidx/core/animation/AndroidXAnimatorIsolationRule.kt
+++ b/packages/SystemUI/tests/utils/src/androidx/core/animation/AndroidXAnimatorIsolationRule.kt
@@ -37,7 +37,7 @@
private fun onError() =
exceptionDeferrer.fail(
"Test's animations are not isolated! " +
- "Did you forget to add an AnimatorTestRule to your test class?"
+ "Did you forget to add an AnimatorTestRule as a @Rule?"
)
fun throwDeferred() = exceptionDeferrer.throwDeferred()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/FakeSystemUiModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/FakeSystemUiModule.kt
index 0e59496..dc5fd95 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/FakeSystemUiModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/FakeSystemUiModule.kt
@@ -15,21 +15,29 @@
*/
package com.android.systemui
+import com.android.systemui.classifier.FakeClassifierModule
import com.android.systemui.data.FakeSystemUiDataLayerModule
import com.android.systemui.flags.FakeFeatureFlagsClassicModule
import com.android.systemui.log.FakeUiEventLoggerModule
import com.android.systemui.scene.FakeSceneModule
import com.android.systemui.settings.FakeSettingsModule
+import com.android.systemui.statusbar.policy.FakeConfigurationControllerModule
+import com.android.systemui.statusbar.policy.FakeSplitShadeStateControllerModule
import com.android.systemui.util.concurrency.FakeExecutorModule
+import com.android.systemui.util.time.FakeSystemClockModule
import dagger.Module
@Module(
includes =
[
+ FakeClassifierModule::class,
+ FakeConfigurationControllerModule::class,
FakeExecutorModule::class,
FakeFeatureFlagsClassicModule::class,
- FakeSettingsModule::class,
FakeSceneModule::class,
+ FakeSettingsModule::class,
+ FakeSplitShadeStateControllerModule::class,
+ FakeSystemClockModule::class,
FakeSystemUiDataLayerModule::class,
FakeUiEventLoggerModule::class,
]
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
index d6632a3..f7e0120 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
@@ -15,8 +15,6 @@
*/
package com.android.systemui;
-import static com.android.systemui.animation.FakeDialogLaunchAnimatorKt.fakeDialogLaunchAnimator;
-
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -37,19 +35,15 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.uiautomator.UiDevice;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.broadcast.FakeBroadcastDispatcher;
import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
-import org.junit.ClassRule;
import org.junit.Rule;
import org.mockito.Mockito;
@@ -69,8 +63,8 @@
private Handler mHandler;
// set the lowest order so it's the outermost rule
- @ClassRule(order = Integer.MIN_VALUE)
- public static AndroidXAnimatorIsolationRule mAndroidXAnimatorIsolationRule =
+ @Rule(order = Integer.MIN_VALUE)
+ public AndroidXAnimatorIsolationRule mAndroidXAnimatorIsolationRule =
new AndroidXAnimatorIsolationRule();
@Rule
@@ -94,10 +88,7 @@
if (isRobolectricTest()) {
mContext = mContext.createDefaultDisplayContext();
}
- SystemUIInitializer initializer = new SystemUIInitializerImpl(mContext);
- initializer.init(true);
- mDependency = new TestableDependency(initializer.getSysUIComponent().createDependency());
- Dependency.setInstance(mDependency);
+ mDependency = SysuiTestDependencyKt.installSysuiTestDependency(mContext);
mFakeBroadcastDispatcher = new FakeBroadcastDispatcher(
mContext,
mContext.getMainExecutor(),
@@ -124,13 +115,6 @@
// reference and are never sent to the Context. This will also prevent a real
// BroadcastDispatcher from actually registering receivers.
mDependency.injectTestDependency(BroadcastDispatcher.class, mFakeBroadcastDispatcher);
- mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
-
- // Make sure that all tests on any SystemUIDialog does not crash because this dependency
- // is missing (constructing the actual one would throw).
- // TODO(b/219008720): Remove this.
- mDependency.injectMockDependency(SystemUIDialogManager.class);
- mDependency.injectTestDependency(DialogLaunchAnimator.class, fakeDialogLaunchAnimator());
}
protected boolean shouldFailOnLeakedReceiver() {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestDependency.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestDependency.kt
new file mode 100644
index 0000000..c791f4f
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestDependency.kt
@@ -0,0 +1,26 @@
+package com.android.systemui
+
+import android.annotation.SuppressLint
+import android.content.Context
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.animation.fakeDialogLaunchAnimator
+import com.android.systemui.statusbar.phone.SystemUIDialogManager
+
+@SuppressLint("VisibleForTests")
+fun installSysuiTestDependency(context: Context): TestableDependency {
+ val initializer: SystemUIInitializer = SystemUIInitializerImpl(context)
+ initializer.init(true)
+
+ val dependency = TestableDependency(initializer.sysUIComponent.createDependency())
+ Dependency.setInstance(dependency)
+
+ dependency.injectMockDependency(KeyguardUpdateMonitor::class.java)
+
+ // Make sure that all tests on any SystemUIDialog does not crash because this dependency
+ // is missing (constructing the actual one would throw).
+ // TODO(b/219008720): Remove this.
+ dependency.injectMockDependency(SystemUIDialogManager::class.java)
+ dependency.injectTestDependency(DialogLaunchAnimator::class.java, fakeDialogLaunchAnimator())
+ return dependency
+}
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/authentication/data/FakeAuthenticationDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/FakeAuthenticationDataLayerModule.kt
new file mode 100644
index 0000000..8fa6695
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/FakeAuthenticationDataLayerModule.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.authentication.data
+
+import com.android.systemui.authentication.data.repository.FakeAuthenticationRepositoryModule
+import dagger.Module
+
+@Module(includes = [FakeAuthenticationRepositoryModule::class])
+object FakeAuthenticationDataLayerModule
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
index 4fc3e3f..ddfe79a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt
@@ -24,10 +24,17 @@
import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
import com.android.systemui.authentication.shared.model.AuthenticationResultModel
import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.currentTime
class FakeAuthenticationRepository(
private val deviceEntryRepository: FakeDeviceEntryRepository,
@@ -201,3 +208,19 @@
}
}
}
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@Module(includes = [FakeAuthenticationRepositoryModule.Bindings::class])
+object FakeAuthenticationRepositoryModule {
+ @Provides
+ @SysUISingleton
+ fun provideFake(
+ deviceEntryRepository: FakeDeviceEntryRepository,
+ scope: TestScope,
+ ) = FakeAuthenticationRepository(deviceEntryRepository, currentTime = { scope.currentTime })
+
+ @Module
+ interface Bindings {
+ @Binds fun bindFake(fake: FakeAuthenticationRepository): AuthenticationRepository
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeClassifierModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeClassifierModule.kt
new file mode 100644
index 0000000..23bad39
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeClassifierModule.kt
@@ -0,0 +1,21 @@
+/*
+ * 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.classifier
+
+import dagger.Module
+
+@Module(includes = [FakeFalsingCollectorModule::class, FakeFalsingManagerModule::class])
+object FakeClassifierModule
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeFalsingCollectorModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeFalsingCollectorModule.kt
new file mode 100644
index 0000000..92acc94
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeFalsingCollectorModule.kt
@@ -0,0 +1,25 @@
+/*
+ * 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.classifier
+
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface FakeFalsingCollectorModule {
+ @Binds @FalsingCollectorActual fun bindFake(fake: FalsingCollectorFake): FalsingCollector
+ @Binds fun bindFakeLegacy(fake: FalsingCollectorFake): FalsingCollector
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeFalsingManagerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeFalsingManagerModule.kt
new file mode 100644
index 0000000..554fc75
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FakeFalsingManagerModule.kt
@@ -0,0 +1,25 @@
+/*
+ * 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.classifier
+
+import com.android.systemui.plugins.FalsingManager
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface FakeFalsingManagerModule {
+ @Binds fun bindFake(fake: FalsingManagerFake): FalsingManager
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FalsingManagerFake.java b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FalsingManagerFake.java
index d47e88f..5038285 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FalsingManagerFake.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FalsingManagerFake.java
@@ -21,15 +21,19 @@
import android.net.Uri;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.FalsingManager;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import javax.inject.Inject;
+
/**
* Simple Fake for testing where {@link FalsingManager} is required.
*/
+@SysUISingleton
public class FalsingManagerFake implements FalsingManager {
private boolean mIsFalseTouch;
private boolean mIsSimpleTap;
@@ -46,6 +50,10 @@
private final List<FalsingBeliefListener> mFalsingBeliefListeners = new ArrayList<>();
private final List<FalsingTapListener> mTapListeners = new ArrayList<>();
+ @Inject
+ public FalsingManagerFake() {
+ }
+
@Override
public void onSuccessfulUnlock() {
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/data/FakeSystemUiDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/data/FakeSystemUiDataLayerModule.kt
index f866932..cffbf02 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/data/FakeSystemUiDataLayerModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/data/FakeSystemUiDataLayerModule.kt
@@ -15,8 +15,10 @@
*/
package com.android.systemui.data
+import com.android.systemui.authentication.data.FakeAuthenticationDataLayerModule
import com.android.systemui.bouncer.data.repository.FakeBouncerDataLayerModule
import com.android.systemui.common.ui.data.FakeCommonDataLayerModule
+import com.android.systemui.deviceentry.data.FakeDeviceEntryDataLayerModule
import com.android.systemui.keyguard.data.FakeKeyguardDataLayerModule
import com.android.systemui.power.data.FakePowerDataLayerModule
import com.android.systemui.shade.data.repository.FakeShadeDataLayerModule
@@ -28,8 +30,10 @@
@Module(
includes =
[
- FakeCommonDataLayerModule::class,
+ FakeAuthenticationDataLayerModule::class,
FakeBouncerDataLayerModule::class,
+ FakeCommonDataLayerModule::class,
+ FakeDeviceEntryDataLayerModule::class,
FakeKeyguardDataLayerModule::class,
FakePowerDataLayerModule::class,
FakeShadeDataLayerModule::class,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt
new file mode 100644
index 0000000..ef02bdd
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt
@@ -0,0 +1,21 @@
+/*
+ * 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.deviceentry.data
+
+import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepositoryModule
+import dagger.Module
+
+@Module(includes = [FakeDeviceEntryRepositoryModule::class]) object FakeDeviceEntryDataLayerModule
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryDataLayerModule.kt
new file mode 100644
index 0000000..f4feee1
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryDataLayerModule.kt
@@ -0,0 +1,20 @@
+/*
+ * 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.deviceentry.data.repository
+
+import dagger.Module
+
+@Module(includes = [FakeDeviceEntryRepositoryModule::class]) object FakeDeviceEntryDataLayerModule
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt
index 5e60a09..f029348 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt
@@ -1,22 +1,40 @@
+/*
+ * 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.deviceentry.data.repository
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
/** Fake implementation of [DeviceEntryRepository] */
-class FakeDeviceEntryRepository : DeviceEntryRepository {
+@SysUISingleton
+class FakeDeviceEntryRepository @Inject constructor() : DeviceEntryRepository {
private var isInsecureLockscreenEnabled = true
- private var isBypassEnabled = false
+
+ private val _isBypassEnabled = MutableStateFlow(false)
+ override val isBypassEnabled: StateFlow<Boolean> = _isBypassEnabled
private val _isUnlocked = MutableStateFlow(false)
override val isUnlocked: StateFlow<Boolean> = _isUnlocked.asStateFlow()
- override fun isBypassEnabled(): Boolean {
- return isBypassEnabled
- }
-
override suspend fun isInsecureLockscreenEnabled(): Boolean {
return isInsecureLockscreenEnabled
}
@@ -30,6 +48,11 @@
}
fun setBypassEnabled(isBypassEnabled: Boolean) {
- this.isBypassEnabled = isBypassEnabled
+ _isBypassEnabled.value = isBypassEnabled
}
}
+
+@Module
+interface FakeDeviceEntryRepositoryModule {
+ @Binds fun bindFake(fake: FakeDeviceEntryRepository): DeviceEntryRepository
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyevent/data/repository/FakeKeyEventRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyevent/data/repository/FakeKeyEventRepository.kt
new file mode 100644
index 0000000..95b5316
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyevent/data/repository/FakeKeyEventRepository.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 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.keyevent.data.repository
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+class FakeKeyEventRepository() : KeyEventRepository {
+ private val _isPowerButtonDown = MutableStateFlow(false)
+ override val isPowerButtonDown: Flow<Boolean> = _isPowerButtonDown.asStateFlow()
+
+ fun setPowerButtonDown(isDown: Boolean) {
+ _isPowerButtonDown.value = isDown
+ }
+}
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/statusbar/data/FakeStatusBarDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/FakeStatusBarDataLayerModule.kt
index 1bec82b..e59f642 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/FakeStatusBarDataLayerModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/FakeStatusBarDataLayerModule.kt
@@ -16,14 +16,18 @@
package com.android.systemui.statusbar.data
import com.android.systemui.statusbar.disableflags.data.FakeStatusBarDisableFlagsDataLayerModule
+import com.android.systemui.statusbar.notification.data.FakeStatusBarNotificationsDataLayerModule
import com.android.systemui.statusbar.pipeline.data.FakeStatusBarPipelineDataLayerModule
+import com.android.systemui.statusbar.policy.data.FakeStatusBarPolicyDataLayerModule
import dagger.Module
@Module(
includes =
[
FakeStatusBarDisableFlagsDataLayerModule::class,
+ FakeStatusBarNotificationsDataLayerModule::class,
FakeStatusBarPipelineDataLayerModule::class,
+ FakeStatusBarPolicyDataLayerModule::class,
]
)
object FakeStatusBarDataLayerModule
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/FakeStatusBarNotificationsDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/FakeStatusBarNotificationsDataLayerModule.kt
new file mode 100644
index 0000000..788e3aa
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/FakeStatusBarNotificationsDataLayerModule.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.data
+
+import com.android.systemui.statusbar.notification.data.repository.FakeNotificationsKeyguardStateRepositoryModule
+import dagger.Module
+
+@Module(includes = [FakeNotificationsKeyguardStateRepositoryModule::class])
+object FakeStatusBarNotificationsDataLayerModule
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/FakeNotificationsKeyguardViewStateRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/FakeNotificationsKeyguardViewStateRepository.kt
new file mode 100644
index 0000000..5d3cb4d
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/FakeNotificationsKeyguardViewStateRepository.kt
@@ -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.systemui.statusbar.notification.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+
+@SysUISingleton
+class FakeNotificationsKeyguardViewStateRepository @Inject constructor() :
+ NotificationsKeyguardViewStateRepository {
+ private val _notificationsFullyHidden = MutableStateFlow(false)
+ override val areNotificationsFullyHidden: Flow<Boolean> = _notificationsFullyHidden
+
+ private val _isPulseExpanding = MutableStateFlow(false)
+ override val isPulseExpanding: Flow<Boolean> = _isPulseExpanding
+
+ fun setNotificationsFullyHidden(fullyHidden: Boolean) {
+ _notificationsFullyHidden.value = fullyHidden
+ }
+
+ fun setPulseExpanding(expanding: Boolean) {
+ _isPulseExpanding.value = expanding
+ }
+}
+
+@Module
+interface FakeNotificationsKeyguardStateRepositoryModule {
+ @Binds
+ fun bindFake(
+ fake: FakeNotificationsKeyguardViewStateRepository
+ ): NotificationsKeyguardViewStateRepository
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt
index 16a3268..23477d8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt
@@ -1,9 +1,14 @@
package com.android.systemui.statusbar.policy
import android.content.res.Configuration
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
/** Fake implementation of [ConfigurationController] for tests. */
-class FakeConfigurationController : ConfigurationController {
+@SysUISingleton
+class FakeConfigurationController @Inject constructor() : ConfigurationController {
private var listeners = mutableListOf<ConfigurationController.ConfigurationListener>()
@@ -33,3 +38,8 @@
override fun isLayoutRtl(): Boolean = false
}
+
+@Module
+interface FakeConfigurationControllerModule {
+ @Binds fun bindFake(fake: FakeConfigurationController): ConfigurationController
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeSplitShadeStateControllerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeSplitShadeStateControllerModule.kt
new file mode 100644
index 0000000..14bab84
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeSplitShadeStateControllerModule.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.policy
+
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface FakeSplitShadeStateControllerModule {
+ @Binds fun bindFake(fake: ResourcesSplitShadeStateController): SplitShadeStateController
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/FakeStatusBarPolicyDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/FakeStatusBarPolicyDataLayerModule.kt
new file mode 100644
index 0000000..5aece1b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/FakeStatusBarPolicyDataLayerModule.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.policy.data
+
+import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepositoryModule
+import dagger.Module
+
+@Module(includes = [FakeDeviceProvisioningRepositoryModule::class])
+object FakeStatusBarPolicyDataLayerModule
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/FakeDeviceProvisioningRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/FakeDeviceProvisioningRepository.kt
new file mode 100644
index 0000000..3002299
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/FakeDeviceProvisioningRepository.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.policy.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+
+@SysUISingleton
+class FakeDeviceProvisioningRepository @Inject constructor() : DeviceProvisioningRepository {
+ private val _isDeviceProvisioned = MutableStateFlow(false)
+ override val isDeviceProvisioned: Flow<Boolean> = _isDeviceProvisioned
+ private val _isFactoryResetProtectionActive = MutableStateFlow(false)
+ override val isFactoryResetProtectionActive: Flow<Boolean> = _isFactoryResetProtectionActive
+ fun setDeviceProvisioned(isProvisioned: Boolean) {
+ _isDeviceProvisioned.value = isProvisioned
+ }
+ fun setFactoryResetProtectionActive(isActive: Boolean) {
+ _isFactoryResetProtectionActive.value = isActive
+ }
+}
+
+@Module
+interface FakeDeviceProvisioningRepositoryModule {
+ @Binds fun bindFake(fake: FakeDeviceProvisioningRepository): DeviceProvisioningRepository
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/FakeExecutorModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/FakeExecutorModule.kt
index 5de05c2..1f48d94 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/FakeExecutorModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/FakeExecutorModule.kt
@@ -15,6 +15,7 @@
*/
package com.android.systemui.util.concurrency
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.util.time.FakeSystemClock
import dagger.Binds
@@ -22,14 +23,12 @@
import dagger.Provides
import java.util.concurrent.Executor
-@Module(includes = [FakeExecutorModule.Bindings::class])
-class FakeExecutorModule(
- @get:Provides val clock: FakeSystemClock = FakeSystemClock(),
-) {
- @get:Provides val executor = FakeExecutor(clock)
+@Module
+interface FakeExecutorModule {
+ @Binds @Main @SysUISingleton fun bindMainExecutor(executor: FakeExecutor): Executor
- @Module
- interface Bindings {
- @Binds @Main fun bindMainExecutor(executor: FakeExecutor): Executor
+ companion object {
+ @Provides
+ fun provideFake(clock: FakeSystemClock) = FakeExecutor(clock)
}
}
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/packages/SystemUI/tests/utils/src/com/android/systemui/util/time/FakeSystemClockModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/time/FakeSystemClockModule.kt
new file mode 100644
index 0000000..3e3d7cb
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/time/FakeSystemClockModule.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.time
+
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+
+@Module
+interface FakeSystemClockModule {
+ @Binds fun bindFake(fake: FakeSystemClock): SystemClock
+
+ companion object {
+ @Provides @SysUISingleton fun providesFake() = FakeSystemClock()
+ }
+}
diff --git a/packages/WallpaperBackup/Android.bp b/packages/WallpaperBackup/Android.bp
index 155dc1a..18f78314 100644
--- a/packages/WallpaperBackup/Android.bp
+++ b/packages/WallpaperBackup/Android.bp
@@ -49,7 +49,7 @@
"androidx.test.core",
"androidx.test.rules",
"mockito-target-minus-junit4",
- "truth-prebuilt",
+ "truth",
],
resource_dirs: ["test/res"],
certificate: "platform",
diff --git a/packages/overlays/tests/Android.bp b/packages/overlays/tests/Android.bp
index b781602..0244c0f 100644
--- a/packages/overlays/tests/Android.bp
+++ b/packages/overlays/tests/Android.bp
@@ -34,7 +34,7 @@
"androidx.test.rules",
"androidx.test.espresso.core",
"mockito-target-minus-junit4",
- "truth-prebuilt",
+ "truth",
],
dxflags: ["--multi-dex"],
}
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/packages/SystemUI/tests/utils/src/com/android/systemui/RoboPilotTest.java b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodNativeSubstitutionClass.java
similarity index 67%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/RoboPilotTest.java
rename to ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodNativeSubstitutionClass.java
index 3fff136..eb883e2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/RoboPilotTest.java
+++ b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodNativeSubstitutionClass.java
@@ -13,19 +13,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package android.ravenwood.annotations;
-package com.android.systemui;
+import static java.lang.annotation.ElementType.TYPE;
-import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
- * Mark as tests for Robolectric pilot projects. The filter can better help grouping test results
- * that runs on CI
+ * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
+ * QUESTIONS ABOUT IT.
+ *
+ * TODO: Javadoc
*/
-@Target({ElementType.METHOD, ElementType.TYPE})
-@Retention(RetentionPolicy.RUNTIME)
-public @interface RoboPilotTest {
+@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/packages/SystemUI/tests/utils/src/com/android/systemui/RoboPilotTest.java b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodThrow.java
similarity index 63%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/RoboPilotTest.java
copy to ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodThrow.java
index 3fff136..a329d84 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/RoboPilotTest.java
+++ b/ravenwood/annotations-src/android/ravenwood/annotations/RavenwoodThrow.java
@@ -13,19 +13,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package android.ravenwood.annotations;
-package com.android.systemui;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.METHOD;
-import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
- * Mark as tests for Robolectric pilot projects. The filter can better help grouping test results
- * that runs on CI
+ * 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({ElementType.METHOD, ElementType.TYPE})
-@Retention(RetentionPolicy.RUNTIME)
-public @interface RoboPilotTest {
+@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..25b6244 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -40,4 +40,18 @@
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"
+}
+
+flag {
+ name: "scan_packages_without_lock"
+ namespace: "accessibility"
+ description: "Scans packages for accessibility service/activity info without holding the A11yMS lock"
+ bug: "295969873"
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 60d4ee6..8c1d444 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -290,14 +290,13 @@
private final Set<ComponentName> mTempComponentNameSet = new HashSet<>();
- private final List<AccessibilityServiceInfo> mTempAccessibilityServiceInfoList =
- new ArrayList<>();
-
private final IntArray mTempIntArray = new IntArray(0);
private final RemoteCallbackList<IAccessibilityManagerClient> mGlobalClients =
new RemoteCallbackList<>();
+ private PackageMonitor mPackageMonitor;
+
@VisibleForTesting
final SparseArray<AccessibilityUserState> mUserStates = new SparseArray<>();
@@ -531,6 +530,19 @@
disableAccessibilityMenuToMigrateIfNeeded();
}
+ /**
+ * Returns if the current thread is holding {@link #mLock}. Used for testing
+ * deadlock bug fixes.
+ *
+ * <p><strong>Warning:</strong> this should not be used for production logic
+ * because by the time you receive an answer it may no longer be valid.
+ * </p>
+ */
+ @VisibleForTesting
+ boolean unsafeIsLockHeld() {
+ return Thread.holdsLock(mLock);
+ }
+
@Override
public int getCurrentUserIdLocked() {
return mCurrentUserId;
@@ -690,6 +702,19 @@
}
}
+ private void onSomePackagesChangedLocked(
+ @Nullable List<AccessibilityServiceInfo> parsedAccessibilityServiceInfos,
+ @Nullable List<AccessibilityShortcutInfo> parsedAccessibilityShortcutInfos) {
+ final AccessibilityUserState userState = getCurrentUserStateLocked();
+ // Reload the installed services since some services may have different attributes
+ // or resolve info (does not support equals), etc. Remove them then to force reload.
+ userState.mInstalledServices.clear();
+ if (readConfigurationForUserStateLocked(userState,
+ parsedAccessibilityServiceInfos, parsedAccessibilityShortcutInfos)) {
+ onUserStateChangedLocked(userState);
+ }
+ }
+
private void onPackageRemovedLocked(String packageName) {
final AccessibilityUserState userState = getCurrentUserState();
final Predicate<ComponentName> filter =
@@ -721,8 +746,13 @@
}
}
+ @VisibleForTesting
+ PackageMonitor getPackageMonitor() {
+ return mPackageMonitor;
+ }
+
private void registerBroadcastReceivers() {
- PackageMonitor monitor = new PackageMonitor() {
+ mPackageMonitor = new PackageMonitor() {
@Override
public void onSomePackagesChanged() {
if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_PACKAGE_BROADCAST_RECEIVER)) {
@@ -730,13 +760,25 @@
FLAGS_PACKAGE_BROADCAST_RECEIVER);
}
+ final int userId = getChangingUserId();
+ List<AccessibilityServiceInfo> parsedAccessibilityServiceInfos = null;
+ List<AccessibilityShortcutInfo> parsedAccessibilityShortcutInfos = null;
+ if (Flags.scanPackagesWithoutLock()) {
+ parsedAccessibilityServiceInfos = parseAccessibilityServiceInfos(userId);
+ parsedAccessibilityShortcutInfos = parseAccessibilityShortcutInfos(userId);
+ }
synchronized (mLock) {
// Only the profile parent can install accessibility services.
// Therefore we ignore packages from linked profiles.
- if (getChangingUserId() != mCurrentUserId) {
+ if (userId != mCurrentUserId) {
return;
}
- onSomePackagesChangedLocked();
+ if (Flags.scanPackagesWithoutLock()) {
+ onSomePackagesChangedLocked(parsedAccessibilityServiceInfos,
+ parsedAccessibilityShortcutInfos);
+ } else {
+ onSomePackagesChangedLocked();
+ }
}
}
@@ -751,8 +793,14 @@
FLAGS_PACKAGE_BROADCAST_RECEIVER,
"packageName=" + packageName + ";uid=" + uid);
}
+ final int userId = getChangingUserId();
+ List<AccessibilityServiceInfo> parsedAccessibilityServiceInfos = null;
+ List<AccessibilityShortcutInfo> parsedAccessibilityShortcutInfos = null;
+ if (Flags.scanPackagesWithoutLock()) {
+ parsedAccessibilityServiceInfos = parseAccessibilityServiceInfos(userId);
+ parsedAccessibilityShortcutInfos = parseAccessibilityShortcutInfos(userId);
+ }
synchronized (mLock) {
- final int userId = getChangingUserId();
if (userId != mCurrentUserId) {
return;
}
@@ -765,8 +813,13 @@
// Reloads the installed services info to make sure the rebound service could
// get a new one.
userState.mInstalledServices.clear();
- final boolean configurationChanged =
- readConfigurationForUserStateLocked(userState);
+ final boolean configurationChanged;
+ if (Flags.scanPackagesWithoutLock()) {
+ configurationChanged = readConfigurationForUserStateLocked(userState,
+ parsedAccessibilityServiceInfos, parsedAccessibilityShortcutInfos);
+ } else {
+ configurationChanged = readConfigurationForUserStateLocked(userState);
+ }
if (reboundAService || configurationChanged) {
onUserStateChangedLocked(userState);
}
@@ -839,34 +892,34 @@
};
// package changes
- monitor.register(mContext, null, UserHandle.ALL, true);
+ mPackageMonitor.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
@@ -1831,8 +1884,15 @@
mA11yWindowManager.onTouchInteractionEnd();
}
- private void switchUser(int userId) {
+ @VisibleForTesting
+ void switchUser(int userId) {
mMagnificationController.updateUserIdIfNeeded(userId);
+ List<AccessibilityServiceInfo> parsedAccessibilityServiceInfos = null;
+ List<AccessibilityShortcutInfo> parsedAccessibilityShortcutInfos = null;
+ if (Flags.scanPackagesWithoutLock()) {
+ parsedAccessibilityServiceInfos = parseAccessibilityServiceInfos(userId);
+ parsedAccessibilityShortcutInfos = parseAccessibilityShortcutInfos(userId);
+ }
synchronized (mLock) {
if (mCurrentUserId == userId && mInitialized) {
return;
@@ -1857,7 +1917,12 @@
mCurrentUserId = userId;
AccessibilityUserState userState = getCurrentUserStateLocked();
- readConfigurationForUserStateLocked(userState);
+ if (Flags.scanPackagesWithoutLock()) {
+ readConfigurationForUserStateLocked(userState,
+ parsedAccessibilityServiceInfos, parsedAccessibilityShortcutInfos);
+ } else {
+ readConfigurationForUserStateLocked(userState);
+ }
mSecurityPolicy.onSwitchUserLocked(mCurrentUserId, userState.mEnabledServices);
// Even if reading did not yield change, we have to update
// the state since the context in which the current user
@@ -2105,8 +2170,17 @@
}
}
- private boolean readInstalledAccessibilityServiceLocked(AccessibilityUserState userState) {
- mTempAccessibilityServiceInfoList.clear();
+ /**
+ * Finds packages that provide AccessibilityService interfaces, and parses
+ * their metadata XML to build up {@link AccessibilityServiceInfo} objects.
+ *
+ * <p>
+ * <strong>Note:</strong> XML parsing is a resource-heavy operation that may
+ * stall, so this method should not be called while holding a lock.
+ * </p>
+ */
+ private List<AccessibilityServiceInfo> parseAccessibilityServiceInfos(int userId) {
+ List<AccessibilityServiceInfo> result = new ArrayList<>();
int flags = PackageManager.GET_SERVICES
| PackageManager.GET_META_DATA
@@ -2114,12 +2188,14 @@
| PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
- if (userState.getBindInstantServiceAllowedLocked()) {
- flags |= PackageManager.MATCH_INSTANT;
+ synchronized (mLock) {
+ if (getUserStateLocked(userId).getBindInstantServiceAllowedLocked()) {
+ flags |= PackageManager.MATCH_INSTANT;
+ }
}
List<ResolveInfo> installedServices = mPackageManager.queryIntentServicesAsUser(
- new Intent(AccessibilityService.SERVICE_INTERFACE), flags, mCurrentUserId);
+ new Intent(AccessibilityService.SERVICE_INTERFACE), flags, userId);
for (int i = 0, count = installedServices.size(); i < count; i++) {
ResolveInfo resolveInfo = installedServices.get(i);
@@ -2132,40 +2208,60 @@
AccessibilityServiceInfo accessibilityServiceInfo;
try {
accessibilityServiceInfo = new AccessibilityServiceInfo(resolveInfo, mContext);
- if (!accessibilityServiceInfo.isWithinParcelableSize()) {
- Slog.e(LOG_TAG, "Skipping service "
- + accessibilityServiceInfo.getResolveInfo().getComponentInfo()
- + " because service info size is larger than safe parcelable limits.");
- continue;
- }
- if (userState.mCrashedServices.contains(serviceInfo.getComponentName())) {
- // Restore the crashed attribute.
- accessibilityServiceInfo.crashed = true;
- }
- mTempAccessibilityServiceInfoList.add(accessibilityServiceInfo);
} catch (XmlPullParserException | IOException xppe) {
Slog.e(LOG_TAG, "Error while initializing AccessibilityServiceInfo", xppe);
+ continue;
+ }
+ if (!accessibilityServiceInfo.isWithinParcelableSize()) {
+ Slog.e(LOG_TAG, "Skipping service "
+ + accessibilityServiceInfo.getResolveInfo().getComponentInfo()
+ + " because service info size is larger than safe parcelable limits.");
+ continue;
+ }
+ result.add(accessibilityServiceInfo);
+ }
+ return result;
+ }
+
+ private boolean readInstalledAccessibilityServiceLocked(AccessibilityUserState userState,
+ @Nullable List<AccessibilityServiceInfo> parsedAccessibilityServiceInfos) {
+ for (int i = 0, count = parsedAccessibilityServiceInfos.size(); i < count; i++) {
+ AccessibilityServiceInfo accessibilityServiceInfo =
+ parsedAccessibilityServiceInfos.get(i);
+ if (userState.mCrashedServices.contains(accessibilityServiceInfo.getComponentName())) {
+ // Restore the crashed attribute.
+ accessibilityServiceInfo.crashed = true;
}
}
- if (!mTempAccessibilityServiceInfoList.equals(userState.mInstalledServices)) {
+ if (!parsedAccessibilityServiceInfos.equals(userState.mInstalledServices)) {
userState.mInstalledServices.clear();
- userState.mInstalledServices.addAll(mTempAccessibilityServiceInfoList);
- mTempAccessibilityServiceInfoList.clear();
+ userState.mInstalledServices.addAll(parsedAccessibilityServiceInfos);
return true;
}
-
- mTempAccessibilityServiceInfoList.clear();
return false;
}
- private boolean readInstalledAccessibilityShortcutLocked(AccessibilityUserState userState) {
- final List<AccessibilityShortcutInfo> shortcutInfos = AccessibilityManager
- .getInstance(mContext).getInstalledAccessibilityShortcutListAsUser(
- mContext, mCurrentUserId);
- if (!shortcutInfos.equals(userState.mInstalledShortcuts)) {
+ /**
+ * Returns the {@link AccessibilityShortcutInfo}s of the installed
+ * accessibility shortcut targets for the given user.
+ *
+ * <p>
+ * <strong>Note:</strong> XML parsing is a resource-heavy operation that may
+ * stall, so this method should not be called while holding a lock.
+ * </p>
+ */
+ private List<AccessibilityShortcutInfo> parseAccessibilityShortcutInfos(int userId) {
+ // TODO: b/297279151 - This should be implemented here, not by AccessibilityManager.
+ return AccessibilityManager.getInstance(mContext)
+ .getInstalledAccessibilityShortcutListAsUser(mContext, userId);
+ }
+
+ private boolean readInstalledAccessibilityShortcutLocked(AccessibilityUserState userState,
+ List<AccessibilityShortcutInfo> parsedAccessibilityShortcutInfos) {
+ if (!parsedAccessibilityShortcutInfos.equals(userState.mInstalledShortcuts)) {
userState.mInstalledShortcuts.clear();
- userState.mInstalledShortcuts.addAll(shortcutInfos);
+ userState.mInstalledShortcuts.addAll(parsedAccessibilityShortcutInfos);
return true;
}
return false;
@@ -2890,9 +2986,23 @@
userState.setFilterKeyEventsEnabledLocked(false);
}
+ // ErrorProne doesn't understand that this method is only called while locked,
+ // returning an error for accessing mCurrentUserId.
+ @SuppressWarnings("GuardedBy")
private boolean readConfigurationForUserStateLocked(AccessibilityUserState userState) {
- boolean somethingChanged = readInstalledAccessibilityServiceLocked(userState);
- somethingChanged |= readInstalledAccessibilityShortcutLocked(userState);
+ return readConfigurationForUserStateLocked(userState,
+ parseAccessibilityServiceInfos(mCurrentUserId),
+ parseAccessibilityShortcutInfos(mCurrentUserId));
+ }
+
+ private boolean readConfigurationForUserStateLocked(
+ AccessibilityUserState userState,
+ List<AccessibilityServiceInfo> parsedAccessibilityServiceInfos,
+ List<AccessibilityShortcutInfo> parsedAccessibilityShortcutInfos) {
+ boolean somethingChanged = readInstalledAccessibilityServiceLocked(
+ userState, parsedAccessibilityServiceInfos);
+ somethingChanged |= readInstalledAccessibilityShortcutLocked(
+ userState, parsedAccessibilityShortcutInfos);
somethingChanged |= readEnabledAccessibilityServicesLocked(userState);
somethingChanged |= readTouchExplorationGrantedAccessibilityServicesLocked(userState);
somethingChanged |= readTouchExplorationEnabledSettingLocked(userState);
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/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 7e09b5e..258820a 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -24,6 +24,7 @@
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -1660,8 +1661,21 @@
synchronized (mLock) {
ensureGroupStateLoadedLocked(userId);
+ final String pkg = componentName.getPackageName();
+ final ProviderId id;
+ if (!mPackageManagerInternal.isSameApp(pkg, callingUid, userId)) {
+ // If the calling process is requesting to pin appwidgets from another process,
+ // check if the calling process has the necessary permission.
+ if (!injectHasAccessWidgetsPermission(Binder.getCallingPid(), callingUid)) {
+ return false;
+ }
+ id = new ProviderId(mPackageManagerInternal.getPackageUid(
+ pkg, 0 /* flags */, userId), componentName);
+ } else {
+ id = new ProviderId(callingUid, componentName);
+ }
// Look for the widget associated with the caller.
- Provider provider = lookupProviderLocked(new ProviderId(callingUid, componentName));
+ Provider provider = lookupProviderLocked(id);
if (provider == null || provider.zombie) {
return false;
}
@@ -1675,6 +1689,14 @@
.requestPinAppWidget(callingPackage, info, extras, resultSender, userId);
}
+ /**
+ * Returns true if the caller has the proper permission to access app widgets.
+ */
+ private boolean injectHasAccessWidgetsPermission(int callingPid, int callingUid) {
+ return mContext.checkPermission(Manifest.permission.CLEAR_APP_USER_DATA,
+ callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
+ }
+
@Override
public ParceledListSlice<AppWidgetProviderInfo> getInstalledProvidersForProfile(int categoryFilter,
int profileId, String packageName) {
@@ -4131,7 +4153,7 @@
return false;
}
- @GuardedBy("mLock")
+ @GuardedBy("AppWidgetServiceImpl.mLock")
public AppWidgetProviderInfo getInfoLocked(Context context) {
if (!mInfoParsed) {
// parse
@@ -4159,18 +4181,18 @@
* be completely parsed and only contain placeHolder information like
* {@link AppWidgetProviderInfo#providerInfo}
*/
- @GuardedBy("mLock")
+ @GuardedBy("AppWidgetServiceImpl.mLock")
public AppWidgetProviderInfo getPartialInfoLocked() {
return info;
}
- @GuardedBy("mLock")
+ @GuardedBy("AppWidgetServiceImpl.mLock")
public void setPartialInfoLocked(AppWidgetProviderInfo info) {
this.info = info;
mInfoParsed = false;
}
- @GuardedBy("mLock")
+ @GuardedBy("AppWidgetServiceImpl.mLock")
public void setInfoLocked(AppWidgetProviderInfo info) {
this.info = info;
mInfoParsed = true;
diff --git a/services/autofill/Android.bp b/services/autofill/Android.bp
index d43a219..eb23f2f 100644
--- a/services/autofill/Android.bp
+++ b/services/autofill/Android.bp
@@ -19,19 +19,4 @@
defaults: ["platform_service_defaults"],
srcs: [":services.autofill-sources"],
libs: ["services.core"],
- static_libs: ["autofill_flags_java_lib"],
-}
-
-aconfig_declarations {
- name: "autofill_flags",
- package: "android.service.autofill",
- srcs: [
- "bugfixes.aconfig",
- "features.aconfig",
- ],
-}
-
-java_aconfig_library {
- name: "autofill_flags_java_lib",
- aconfig_declarations: "autofill_flags",
}
diff --git a/services/autofill/bugfixes.aconfig b/services/autofill/bugfixes.aconfig
index ef23754..b37bbd6 100644
--- a/services/autofill/bugfixes.aconfig
+++ b/services/autofill/bugfixes.aconfig
@@ -5,4 +5,18 @@
namespace: "autofill"
description: "Test flag "
bug: "297380045"
-}
\ No newline at end of file
+}
+
+flag {
+ name: "fill_fields_from_current_session_only"
+ namespace: "autofill"
+ description: "Only fill autofill fields that are part of the current session."
+ bug: "270722825"
+}
+
+flag {
+ name: "relayout"
+ namespace: "autofill"
+ description: "Mitigation for relayout issue"
+ bug: "294330426"
+}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index c7b53c5..72242d2 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -58,6 +58,7 @@
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.service.autofill.FillEventHistory;
+import android.service.autofill.Flags;
import android.service.autofill.UserData;
import android.text.TextUtils;
import android.text.TextUtils.SimpleStringSplitter;
@@ -226,6 +227,12 @@
@GuardedBy("mFlagLock")
private int mMaxInputLengthForAutofill;
+ @GuardedBy("mFlagLock")
+ private boolean mAutofillCredmanIntegrationEnabled;
+
+ @GuardedBy("mFlagLock")
+ private boolean mIsFillFieldsFromCurrentSessionOnly;
+
// Default flag values for Autofill PCC
private static final String DEFAULT_PCC_FEATURE_PROVIDER_HINTS = "";
@@ -701,12 +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);
}
}
}
@@ -965,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() {
@@ -1004,6 +1024,15 @@
}
}
+ /**
+ * Return if autofill should only fill in fields from current session.
+ */
+ public boolean getIsFillFieldsFromCurrentSessionOnly() {
+ synchronized (mFlagLock) {
+ return mIsFillFieldsFromCurrentSessionOnly;
+ }
+ }
+
@Nullable
@VisibleForTesting
static Map<String, String[]> getAllowedCompatModePackages(String setting) {
@@ -2096,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 0220dec..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")
@@ -2437,7 +2346,7 @@
+ id + " destroyed");
return;
}
- fillInIntent = createAuthFillInIntentLocked(requestId, extras);
+ fillInIntent = createAuthFillInIntentLocked(requestId, extras, /* authExtras= */ null);
if (fillInIntent == null) {
forceRemoveFromServiceLocked();
return;
@@ -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() {
@@ -5558,7 +5447,8 @@
mPresentationStatsEventLogger.maybeSetAuthenticationType(
AUTHENTICATION_TYPE_DATASET_AUTHENTICATION);
setViewStatesLocked(null, dataset, ViewState.STATE_WAITING_DATASET_AUTH, false);
- final Intent fillInIntent = createAuthFillInIntentLocked(requestId, mClientState);
+ final Intent fillInIntent = createAuthFillInIntentLocked(requestId, mClientState,
+ dataset.getAuthenticationExtras());
if (fillInIntent == null) {
forceRemoveFromServiceLocked();
return;
@@ -5574,7 +5464,8 @@
// TODO: this should never be null, but we got at least one occurrence, probably due to a race.
@GuardedBy("mLock")
@Nullable
- private Intent createAuthFillInIntentLocked(int requestId, Bundle extras) {
+ private Intent createAuthFillInIntentLocked(int requestId, Bundle extras,
+ @Nullable Bundle authExtras) {
final Intent fillInIntent = new Intent();
final FillContext context = getFillContextByRequestIdLocked(requestId);
@@ -5591,6 +5482,9 @@
}
fillInIntent.putExtra(AutofillManager.EXTRA_ASSIST_STRUCTURE, context.getStructure());
fillInIntent.putExtra(AutofillManager.EXTRA_CLIENT_STATE, extras);
+ if (authExtras != null) {
+ fillInIntent.putExtra(AutofillManager.EXTRA_AUTH_STATE, authExtras);
+ }
return fillInIntent;
}
@@ -5636,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.
*
@@ -6137,9 +6011,17 @@
continue;
}
final AutofillId viewId = dataset.getFieldIds().get(i);
+ final ViewState viewState = mViewStates.get(viewId);
+ if (mService.getMaster().getIsFillFieldsFromCurrentSessionOnly()
+ && viewState != null && viewState.id.getSessionId() != id) {
+ if (sVerbose) {
+ Slog.v(TAG, "Skipping filling view: " +
+ viewId + " as it isn't part of the current session: " + id);
+ }
+ continue;
+ }
ids.add(viewId);
values.add(dataset.getFieldValues().get(i));
- final ViewState viewState = mViewStates.get(viewId);
if (viewState != null
&& (viewState.getState() & ViewState.STATE_WAITING_DATASET_AUTH) != 0) {
if (sVerbose) {
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 8a2aa61..f5562d2 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -122,9 +122,9 @@
private static final String TAG = "VirtualDeviceImpl";
/**
- * Virtual displays created by a {@link VirtualDeviceManager.VirtualDevice} are more consistent
- * with virtual displays created via {@link DisplayManager} and allow for the creation of
- * private, auto-mirror, and fixed orientation displays since
+ * Virtual displays created by a {@code VirtualDeviceManager.VirtualDevice} are more consistent
+ * with virtual displays created via {@link android.hardware.display.DisplayManager} and allow
+ * for the creation of private, auto-mirror, and fixed orientation displays since
* {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}.
*
* @see DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index cfe56e9..5cb100a1 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -38,6 +38,7 @@
import android.companion.virtual.VirtualDeviceParams;
import android.companion.virtual.flags.Flags;
import android.companion.virtual.sensor.VirtualSensor;
+import android.companion.virtualnative.IVirtualDeviceManagerNative;
import android.content.AttributionSource;
import android.content.Context;
import android.content.Intent;
@@ -88,8 +89,11 @@
private static final String TAG = "VirtualDeviceManagerService";
+ private static final String VIRTUAL_DEVICE_NATIVE_SERVICE = "virtualdevice_native";
+
private final Object mVirtualDeviceManagerLock = new Object();
private final VirtualDeviceManagerImpl mImpl;
+ private final VirtualDeviceManagerNativeImpl mNativeImpl;
private final VirtualDeviceManagerInternal mLocalService;
private VirtualDeviceLog mVirtualDeviceLog = new VirtualDeviceLog(getContext());
private final Handler mHandler = new Handler(Looper.getMainLooper());
@@ -125,6 +129,7 @@
public VirtualDeviceManagerService(Context context) {
super(context);
mImpl = new VirtualDeviceManagerImpl();
+ mNativeImpl = Flags.enableNativeVdm() ? new VirtualDeviceManagerNativeImpl() : null;
mLocalService = new LocalService();
}
@@ -155,6 +160,9 @@
@Override
public void onStart() {
publishBinderService(Context.VIRTUAL_DEVICE_SERVICE, mImpl);
+ if (Flags.enableNativeVdm()) {
+ publishBinderService(VIRTUAL_DEVICE_NATIVE_SERVICE, mNativeImpl);
+ }
publishLocalService(VirtualDeviceManagerInternal.class, mLocalService);
ActivityTaskManagerInternal activityTaskManagerInternal = getLocalService(
ActivityTaskManagerInternal.class);
@@ -590,6 +598,19 @@
}
}
+ final class VirtualDeviceManagerNativeImpl extends IVirtualDeviceManagerNative.Stub {
+ @Override // Binder call
+ public int[] getDeviceIdsForUid(int uid) {
+ return mLocalService
+ .getDeviceIdsForUid(uid).stream().mapToInt(Integer::intValue).toArray();
+ }
+
+ @Override // Binder call
+ public int getDevicePolicy(int deviceId, int policyType) {
+ return mImpl.getDevicePolicy(deviceId, policyType);
+ }
+ }
+
private final class LocalService extends VirtualDeviceManagerInternal {
@GuardedBy("mVirtualDeviceManagerLock")
private final ArrayList<VirtualDisplayListener>
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index f594170..ee41a69 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -20,11 +20,15 @@
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;
import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_TRUE;
import static android.view.contentcapture.ContentCaptureSession.STATE_DISABLED;
+import static android.view.contentprotection.flags.Flags.parseGroupsConfigEnabled;
import static com.android.internal.util.FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ACCEPT_DATA_SHARE_REQUEST;
import static com.android.internal.util.FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_ERROR_CLIENT_PIPE_FAIL;
@@ -112,6 +116,8 @@
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
@@ -138,6 +144,9 @@
private static final int MAX_CONCURRENT_FILE_SHARING_REQUESTS = 10;
private static final int DATA_SHARE_BYTE_BUFFER_LENGTH = 1_024;
+ private static final String CONTENT_PROTECTION_GROUP_CONFIG_SEPARATOR_GROUP = ";";
+ private static final String CONTENT_PROTECTION_GROUP_CONFIG_SEPARATOR_VALUE = ",";
+
// Needed to pass checkstyle_hook as names are too long for one line.
private static final int EVENT__DATA_SHARE_ERROR_CONCURRENT_REQUEST =
CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_ERROR_CONCURRENT_REQUEST;
@@ -203,6 +212,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 +246,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 +447,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 +461,8 @@
/** @hide */
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
protected void setFineTuneParamsFromDeviceConfig() {
+ String contentProtectionRequiredGroupsConfig;
+ String contentProtectionOptionalGroupsConfig;
synchronized (mLock) {
mDevCfgMaxBufferSize =
DeviceConfig.getInt(
@@ -486,6 +516,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 +555,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 +849,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 +962,42 @@
return mContentCaptureManagerServiceStub;
}
+ /**
+ * Parses a simple config in format "group;group" where each "group" is itself in the format of
+ * "string1,string2", eg:
+ *
+ * <p>"a" -> [["a"]]
+ *
+ * <p>"a,b" -> [["a", "b"]]
+ *
+ * <p>"a,b;c;d,e" -> [["a", "b"], ["c"], ["d", "e"]]
+ *
+ * @hide
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ @NonNull
+ protected List<List<String>> parseContentProtectionGroupsConfig(@Nullable String config) {
+ if (verbose) {
+ Slog.v(TAG, "parseContentProtectionGroupsConfig: " + config);
+ }
+ if (!parseGroupsConfigEnabled()) {
+ return Collections.emptyList();
+ }
+ if (config == null) {
+ return Collections.emptyList();
+ }
+ return Arrays.stream(config.split(CONTENT_PROTECTION_GROUP_CONFIG_SEPARATOR_GROUP))
+ .map(this::parseContentProtectionGroupConfigValues)
+ .filter(group -> !group.isEmpty())
+ .toList();
+ }
+
+ private List<String> parseContentProtectionGroupConfigValues(@NonNull String group) {
+ return Arrays.stream(group.split(CONTENT_PROTECTION_GROUP_CONFIG_SEPARATOR_VALUE))
+ .filter(value -> !value.isEmpty())
+ .toList();
+ }
+
final class ContentCaptureManagerServiceStub extends IContentCaptureManager.Stub {
@Override
@@ -1277,7 +1385,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/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index cd87908..8df5456 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -1428,6 +1428,12 @@
@UserIdInt int userId);
/**
+ * Sends the PACKAGE_RESTARTED broadcast on the package manager handler thread.
+ */
+ public abstract void sendPackageRestartedBroadcast(@NonNull String packageName,
+ int uid, @Intent.Flags int flags);
+
+ /**
* Return a list of all historical install sessions for the given user.
*/
public abstract ParceledListSlice<PackageInstaller.SessionInfo> getHistoricalSessions(
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/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index 9bab261..57ed5a2 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -41,6 +41,7 @@
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
@@ -153,6 +154,9 @@
@GuardedBy("this")
private ArraySet<Integer> mPinKeys;
+ private static final String DEVICE_CONFIG_KEY_ANON_SIZE = "pin_shared_anon_size";
+ private static final long DEFAULT_ANON_SIZE =
+ SystemProperties.getLong("pinner.pin_shared_anon_size", 0);
private static final long MAX_ANON_SIZE = 2L * (1L << 30); // 2GB
private long mPinAnonSize;
private long mPinAnonAddress;
@@ -180,6 +184,17 @@
}
};
+ private DeviceConfig.OnPropertiesChangedListener mDeviceConfigListener =
+ new DeviceConfig.OnPropertiesChangedListener() {
+ @Override
+ public void onPropertiesChanged(DeviceConfig.Properties properties) {
+ if (DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT.equals(properties.getNamespace())
+ && properties.getKeyset().contains(DEVICE_CONFIG_KEY_ANON_SIZE)) {
+ refreshPinAnonConfig();
+ }
+ }
+ };
+
public PinnerService(Context context) {
super(context);
@@ -206,6 +221,11 @@
registerUidListener();
registerUserSetupCompleteListener();
+
+ DeviceConfig.addOnPropertiesChangedListener(
+ DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
+ new HandlerExecutor(mPinnerHandler),
+ mDeviceConfigListener);
}
@Override
@@ -344,6 +364,8 @@
}
}
}
+
+ refreshPinAnonConfig();
}
/**
@@ -359,8 +381,10 @@
@Override
public void onChange(boolean selfChange, Uri uri) {
if (userSetupCompleteUri.equals(uri)) {
- sendPinAppMessage(KEY_HOME, ActivityManager.getCurrentUser(),
- true /* force */);
+ if (mConfiguredToPinHome) {
+ sendPinAppMessage(KEY_HOME, ActivityManager.getCurrentUser(),
+ true /* force */);
+ }
}
}
}, UserHandle.USER_ALL);
@@ -558,11 +582,6 @@
pinKeys.add(KEY_ASSISTANT);
}
- mPinAnonSize = DeviceConfig.getLong(DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
- "pin_anon_size",
- SystemProperties.getLong("pinner.pin_anon_size", 0));
- mPinAnonSize = Math.max(0, Math.min(mPinAnonSize, MAX_ANON_SIZE));
-
return pinKeys;
}
@@ -602,7 +621,6 @@
int key = currentPinKeys.valueAt(i);
pinApp(key, userHandle, true /* force */);
}
- pinAnonRegion();
}
/**
@@ -687,10 +705,28 @@
}
/**
+ * Handle any changes in the anon region pinner config.
+ */
+ private void refreshPinAnonConfig() {
+ long newPinAnonSize =
+ DeviceConfig.getLong(
+ DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
+ DEVICE_CONFIG_KEY_ANON_SIZE,
+ DEFAULT_ANON_SIZE);
+ newPinAnonSize = Math.max(0, Math.min(newPinAnonSize, MAX_ANON_SIZE));
+ if (newPinAnonSize != mPinAnonSize) {
+ mPinAnonSize = newPinAnonSize;
+ pinAnonRegion();
+ }
+ }
+
+ /**
* Pin an empty anonymous region. This should only be used for ablation experiments.
*/
private void pinAnonRegion() {
if (mPinAnonSize == 0) {
+ Slog.d(TAG, "pinAnonRegion: releasing pinned region");
+ unpinAnonRegion();
return;
}
long alignedPinSize = mPinAnonSize;
@@ -698,15 +734,21 @@
alignedPinSize -= alignedPinSize % PAGE_SIZE;
Slog.e(TAG, "pinAnonRegion: aligning size to " + alignedPinSize);
}
- if (mPinAnonAddress != 0
- && mCurrentlyPinnedAnonSize != alignedPinSize) {
+ if (mPinAnonAddress != 0) {
+ if (mCurrentlyPinnedAnonSize == alignedPinSize) {
+ Slog.d(TAG, "pinAnonRegion: already pinned region of size " + alignedPinSize);
+ return;
+ }
+ Slog.d(TAG, "pinAnonRegion: resetting pinned region for new size " + alignedPinSize);
unpinAnonRegion();
}
long address = 0;
try {
+ // Map as SHARED to avoid changing rss.anon for system_server (per /proc/*/status).
+ // The mapping is visible in other rss metrics, and as private dirty in smaps/meminfo.
address = Os.mmap(0, alignedPinSize,
OsConstants.PROT_READ | OsConstants.PROT_WRITE,
- OsConstants.MAP_PRIVATE | OsConstants.MAP_ANONYMOUS,
+ OsConstants.MAP_SHARED | OsConstants.MAP_ANONYMOUS,
new FileDescriptor(), /*offset=*/0);
Unsafe tempUnsafe = null;
@@ -727,7 +769,7 @@
mCurrentlyPinnedAnonSize = alignedPinSize;
mPinAnonAddress = address;
address = -1;
- Slog.e(TAG, "pinAnonRegion success, size=" + mCurrentlyPinnedAnonSize);
+ Slog.w(TAG, "pinAnonRegion success, size=" + mCurrentlyPinnedAnonSize);
} catch (Exception ex) {
Slog.e(TAG, "Could not pin anon region of size " + alignedPinSize, ex);
return;
@@ -742,6 +784,8 @@
if (mPinAnonAddress != 0) {
safeMunmap(mPinAnonAddress, mCurrentlyPinnedAnonSize);
}
+ mPinAnonAddress = 0;
+ mCurrentlyPinnedAnonSize = 0;
}
/**
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 8cc2665..beea063 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -214,6 +214,12 @@
// external storage service.
public static final int FAILED_MOUNT_RESET_TIMEOUT_SECONDS = 10;
+ /** Extended timeout for the system server watchdog. */
+ private static final int SLOW_OPERATION_WATCHDOG_TIMEOUT_MS = 60 * 1000;
+
+ /** Extended timeout for the system server watchdog for vold#partition operation. */
+ private static final int PARTITION_OPERATION_WATCHDOG_TIMEOUT_MS = 3 * 60 * 1000;
+
@GuardedBy("mLock")
private final Set<Integer> mFuseMountedUser = new ArraySet<>();
@@ -1230,6 +1236,8 @@
private void onUserStopped(int userId) {
Slog.d(TAG, "onUserStopped " + userId);
+ Watchdog.getInstance().setOneOffTimeoutForMonitors(
+ SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#onUserStopped might be slow");
try {
mVold.onUserStopped(userId);
mStoraged.onUserStopped(userId);
@@ -1312,6 +1320,8 @@
unlockedUsers.add(userId);
}
}
+ Watchdog.getInstance().setOneOffTimeoutForMonitors(
+ SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#onUserStopped might be slow");
for (Integer userId : unlockedUsers) {
try {
mVold.onUserStopped(userId);
@@ -2331,6 +2341,8 @@
try {
// TODO(b/135341433): Remove cautious logging when FUSE is stable
Slog.i(TAG, "Mounting volume " + vol);
+ Watchdog.getInstance().setOneOffTimeoutForMonitors(
+ SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#mount might be slow");
mVold.mount(vol.id, vol.mountFlags, vol.mountUserId, new IVoldMountCallback.Stub() {
@Override
public boolean onVolumeChecking(FileDescriptor fd, String path,
@@ -2456,10 +2468,12 @@
@android.annotation.EnforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS)
@Override
public void partitionPublic(String diskId) {
-
super.partitionPublic_enforcePermission();
final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
+
+ Watchdog.getInstance().setOneOffTimeoutForMonitors(
+ PARTITION_OPERATION_WATCHDOG_TIMEOUT_MS, "#partition might be very slow");
try {
mVold.partition(diskId, IVold.PARTITION_TYPE_PUBLIC, -1);
waitForLatch(latch, "partitionPublic", 3 * DateUtils.MINUTE_IN_MILLIS);
@@ -2476,6 +2490,9 @@
enforceAdminUser();
final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
+
+ Watchdog.getInstance().setOneOffTimeoutForMonitors(
+ PARTITION_OPERATION_WATCHDOG_TIMEOUT_MS, "#partition might be very slow");
try {
mVold.partition(diskId, IVold.PARTITION_TYPE_PRIVATE, -1);
waitForLatch(latch, "partitionPrivate", 3 * DateUtils.MINUTE_IN_MILLIS);
@@ -2492,6 +2509,9 @@
enforceAdminUser();
final CountDownLatch latch = findOrCreateDiskScanLatch(diskId);
+
+ Watchdog.getInstance().setOneOffTimeoutForMonitors(
+ PARTITION_OPERATION_WATCHDOG_TIMEOUT_MS, "#partition might be very slow");
try {
mVold.partition(diskId, IVold.PARTITION_TYPE_MIXED, ratio);
waitForLatch(latch, "partitionMixed", 3 * DateUtils.MINUTE_IN_MILLIS);
@@ -3600,6 +3620,8 @@
@Override
public ParcelFileDescriptor open() throws AppFuseMountException {
+ Watchdog.getInstance().setOneOffTimeoutForMonitors(
+ SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#open might be slow");
try {
final FileDescriptor fd = mVold.mountAppFuse(uid, mountId);
mMounted = true;
@@ -3612,6 +3634,8 @@
@Override
public ParcelFileDescriptor openFile(int mountId, int fileId, int flags)
throws AppFuseMountException {
+ Watchdog.getInstance().setOneOffTimeoutForMonitors(
+ SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#openFile might be slow");
try {
return new ParcelFileDescriptor(
mVold.openAppFuseFile(uid, mountId, fileId, flags));
@@ -3622,6 +3646,8 @@
@Override
public void close() throws Exception {
+ Watchdog.getInstance().setOneOffTimeoutForMonitors(
+ SLOW_OPERATION_WATCHDOG_TIMEOUT_MS, "#close might be slow");
if (mMounted) {
mVold.unmountAppFuse(uid, mountId);
mMounted = false;
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index b05b397..55aa716 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -644,6 +644,16 @@
}
/**
+ * Sets a one-off timeout for the next run of the watchdog for the monitor thread.
+ *
+ * <p>Simiar to {@link setOneOffTimeoutForCurrentThread} but used for monitors added through
+ * {@link #addMonitor}
+ */
+ public void setOneOffTimeoutForMonitors(int oneOffTimeoutMillis, String reason) {
+ mMonitorChecker.setOneOffTimeoutLocked(oneOffTimeoutMillis, reason);
+ }
+
+ /**
* Pauses Watchdog action for the currently running thread. Useful before executing long running
* operations that could falsely trigger the watchdog. Each call to this will require a matching
* call to {@link #resumeWatchingCurrentThread}.
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 330742a..5fb889a 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -5023,7 +5023,7 @@
p.setDataPosition(0);
Bundle simulateBundle = p.readBundle();
p.recycle();
- Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT);
+ Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT, Intent.class);
if (intent != null && intent.getClass() != Intent.class) {
return false;
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 553b085..0956c6d 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -409,6 +409,13 @@
AppWidgetManagerInternal mAppWidgetManagerInternal;
+ /**
+ * The available ANR timers.
+ */
+ private final ProcessAnrTimer mActiveServiceAnrTimer;
+ private final ServiceAnrTimer mShortFGSAnrTimer;
+ private final ServiceAnrTimer mServiceFGAnrTimer;
+
// allowlisted packageName.
ArraySet<String> mAllowListWhileInUsePermissionInFgs = new ArraySet<>();
@@ -663,6 +670,15 @@
final IBinder b = ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE);
this.mFGSLogger = new ForegroundServiceTypeLoggerModule();
+ this.mActiveServiceAnrTimer = new ProcessAnrTimer(service,
+ ActivityManagerService.SERVICE_TIMEOUT_MSG,
+ "SERVICE_TIMEOUT");
+ this.mShortFGSAnrTimer = new ServiceAnrTimer(service,
+ ActivityManagerService.SERVICE_SHORT_FGS_ANR_TIMEOUT_MSG,
+ "FGS_TIMEOUT");
+ this.mServiceFGAnrTimer = new ServiceAnrTimer(service,
+ ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG,
+ "SERVICE_FOREGROUND_TIMEOUT");
}
void systemServicesReady() {
@@ -2083,8 +2099,7 @@
r.fgRequired = false;
r.fgWaiting = false;
alreadyStartedOp = stopProcStatsOp = true;
- mAm.mHandler.removeMessages(
- ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);
+ mServiceFGAnrTimer.cancel(r);
}
final ProcessServiceRecord psr = r.app.mServices;
@@ -3313,7 +3328,7 @@
}
void unscheduleShortFgsTimeoutLocked(ServiceRecord sr) {
- mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_SHORT_FGS_ANR_TIMEOUT_MSG, sr);
+ mShortFGSAnrTimer.cancel(sr);
mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_SHORT_FGS_PROCSTATE_TIMEOUT_MSG,
sr);
mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_SHORT_FGS_TIMEOUT_MSG, sr);
@@ -3387,9 +3402,11 @@
Slog.d(TAG_SERVICE, "[STALE] Short FGS timed out: " + sr
+ " " + sr.getShortFgsTimedEventDescription(nowUptime));
}
+ mShortFGSAnrTimer.discard(sr);
return;
}
Slog.e(TAG_SERVICE, "Short FGS timed out: " + sr);
+ mShortFGSAnrTimer.accept(sr);
traceInstant("short FGS timeout: ", sr);
logFGSStateChangeLocked(sr,
@@ -3413,11 +3430,10 @@
msg, sr.getShortFgsInfo().getProcStateDemoteTime());
}
- {
- final Message msg = mAm.mHandler.obtainMessage(
- ActivityManagerService.SERVICE_SHORT_FGS_ANR_TIMEOUT_MSG, sr);
- mAm.mHandler.sendMessageAtTime(msg, sr.getShortFgsInfo().getAnrTime());
- }
+ // ServiceRecord.getAnrTime() is an absolute time with a reference that is not "now".
+ // Compute the time from "now" when starting the anr timer.
+ mShortFGSAnrTimer.start(sr,
+ sr.getShortFgsInfo().getAnrTime() - SystemClock.uptimeMillis());
}
}
@@ -4847,8 +4863,7 @@
// a new SERVICE_FOREGROUND_TIMEOUT_MSG is scheduled in SERVICE_START_FOREGROUND_TIMEOUT
// again.
if (r.fgRequired && r.fgWaiting) {
- mAm.mHandler.removeMessages(
- ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);
+ mServiceFGAnrTimer.cancel(r);
r.fgWaiting = false;
}
@@ -5691,8 +5706,7 @@
}
mAm.mAppOpsService.finishOperation(AppOpsManager.getToken(mAm.mAppOpsService),
AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null);
- mAm.mHandler.removeMessages(
- ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);
+ mServiceFGAnrTimer.cancel(r);
if (r.app != null) {
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_FOREGROUND_CRASH_MSG);
@@ -6128,7 +6142,7 @@
if (psr.numberOfExecutingServices() == 0) {
if (DEBUG_SERVICE || DEBUG_SERVICE_EXECUTING) Slog.v(TAG_SERVICE_EXECUTING,
"No more executingServices of " + r.shortInstanceName);
- mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
+ if (r.app.mPid != 0) mActiveServiceAnrTimer.cancel(r.app);
} else if (r.executeFg) {
// Need to re-evaluate whether the app still needs to be in the foreground.
for (int i = psr.numberOfExecutingServices() - 1; i >= 0; i--) {
@@ -6816,13 +6830,16 @@
synchronized (mAm) {
if (proc.isDebugging()) {
// The app's being debugged, ignore timeout.
+ mActiveServiceAnrTimer.discard(proc);
return;
}
final ProcessServiceRecord psr = proc.mServices;
if (psr.numberOfExecutingServices() == 0 || proc.getThread() == null
|| proc.isKilled()) {
+ mActiveServiceAnrTimer.discard(proc);
return;
}
+ mActiveServiceAnrTimer.accept(proc);
final long now = SystemClock.uptimeMillis();
final long maxTime = now
- (psr.shouldExecServicesFg()
@@ -6855,12 +6872,11 @@
timeoutRecord = TimeoutRecord.forServiceExec(timeout.shortInstanceName,
waitedMillis);
} else {
- Message msg = mAm.mHandler.obtainMessage(
- ActivityManagerService.SERVICE_TIMEOUT_MSG);
- msg.obj = proc;
- mAm.mHandler.sendMessageAtTime(msg, psr.shouldExecServicesFg()
- ? (nextTime + mAm.mConstants.SERVICE_TIMEOUT) :
- (nextTime + mAm.mConstants.SERVICE_BACKGROUND_TIMEOUT));
+ final long delay = psr.shouldExecServicesFg()
+ ? (nextTime + mAm.mConstants.SERVICE_TIMEOUT) :
+ (nextTime + mAm.mConstants.SERVICE_BACKGROUND_TIMEOUT)
+ - SystemClock.uptimeMillis();
+ mActiveServiceAnrTimer.start(proc, delay);
}
}
@@ -6886,12 +6902,15 @@
synchronized (mAm) {
timeoutRecord.mLatencyTracker.waitingOnAMSLockEnded();
if (!r.fgRequired || !r.fgWaiting || r.destroying) {
+ mServiceFGAnrTimer.discard(r);
return;
}
+ mServiceFGAnrTimer.accept(r);
app = r.app;
if (app != null && app.isDebugging()) {
// The app's being debugged; let it ride
+ mServiceFGAnrTimer.discard(r);
return;
}
@@ -6948,26 +6967,46 @@
ForegroundServiceDidNotStartInTimeException.createExtrasForService(service));
}
+ private static class ProcessAnrTimer extends AnrTimer<ProcessRecord> {
+
+ ProcessAnrTimer(ActivityManagerService am, int msg, String label) {
+ super(Objects.requireNonNull(am).mHandler, msg, label);
+ }
+
+ void start(@NonNull ProcessRecord proc, long millis) {
+ start(proc, proc.getPid(), proc.uid, millis);
+ }
+ }
+
+ private static class ServiceAnrTimer extends AnrTimer<ServiceRecord> {
+
+ ServiceAnrTimer(ActivityManagerService am, int msg, String label) {
+ super(Objects.requireNonNull(am).mHandler, msg, label);
+ }
+
+ void start(@NonNull ServiceRecord service, long millis) {
+ start(service,
+ (service.app != null) ? service.app.getPid() : 0,
+ service.appInfo.uid,
+ millis);
+ }
+ }
+
void scheduleServiceTimeoutLocked(ProcessRecord proc) {
if (proc.mServices.numberOfExecutingServices() == 0 || proc.getThread() == null) {
return;
}
- Message msg = mAm.mHandler.obtainMessage(
- ActivityManagerService.SERVICE_TIMEOUT_MSG);
- msg.obj = proc;
- mAm.mHandler.sendMessageDelayed(msg, proc.mServices.shouldExecServicesFg()
- ? mAm.mConstants.SERVICE_TIMEOUT : mAm.mConstants.SERVICE_BACKGROUND_TIMEOUT);
+ final long delay = proc.mServices.shouldExecServicesFg()
+ ? mAm.mConstants.SERVICE_TIMEOUT : mAm.mConstants.SERVICE_BACKGROUND_TIMEOUT;
+ mActiveServiceAnrTimer.start(proc, delay);
}
void scheduleServiceForegroundTransitionTimeoutLocked(ServiceRecord r) {
if (r.app.mServices.numberOfExecutingServices() == 0 || r.app.getThread() == null) {
return;
}
- Message msg = mAm.mHandler.obtainMessage(
- ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG);
- msg.obj = r;
r.fgWaiting = true;
- mAm.mHandler.sendMessageDelayed(msg, mAm.mConstants.mServiceStartForegroundTimeoutMs);
+ mServiceFGAnrTimer.start(r, mAm.mConstants.mServiceStartForegroundTimeoutMs);
}
final class ServiceDumper {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d3ce47c..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;
}
}
@@ -4160,26 +4163,34 @@
@GuardedBy("this")
private void finishForceStopPackageLocked(final String packageName, int uid) {
- Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED,
- Uri.fromParts("package", packageName, null));
+ int flags = 0;
if (!mProcessesReady) {
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
- | Intent.FLAG_RECEIVER_FOREGROUND);
+ flags = Intent.FLAG_RECEIVER_REGISTERED_ONLY
+ | Intent.FLAG_RECEIVER_FOREGROUND;
}
- final int userId = UserHandle.getUserId(uid);
- final int[] broadcastAllowList =
- getPackageManagerInternal().getVisibilityAllowList(packageName, userId);
- intent.putExtra(Intent.EXTRA_UID, uid);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- broadcastIntentLocked(null /* callerApp */, null /* callerPackage */,
- null /* callerFeatureId */, intent, null /* resolvedType */,
- null /* resultToApp */, null /* resultTo */,
- 0 /* resultCode */, null /* resultData */, null /* resultExtras */,
- null /* requiredPermissions */, null /* excludedPermissions */,
- null /* excludedPackages */, OP_NONE, null /* bOptions */, false /* ordered */,
- false /* sticky */, MY_PID, SYSTEM_UID, Binder.getCallingUid(),
- Binder.getCallingPid(), userId, BackgroundStartPrivileges.NONE,
- broadcastAllowList, null /* filterExtrasForReceiver */);
+ if (android.content.pm.Flags.stayStopped()) {
+ // Sent async using the PM handler, to maintain ordering with PACKAGE_UNSTOPPED
+ mPackageManagerInt.sendPackageRestartedBroadcast(packageName,
+ uid, flags);
+ } else {
+ Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED,
+ Uri.fromParts("package", packageName, null));
+ intent.addFlags(flags);
+ final int userId = UserHandle.getUserId(uid);
+ final int[] broadcastAllowList =
+ getPackageManagerInternal().getVisibilityAllowList(packageName, userId);
+ intent.putExtra(Intent.EXTRA_UID, uid);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ broadcastIntentLocked(null /* callerApp */, null /* callerPackage */,
+ null /* callerFeatureId */, intent, null /* resolvedType */,
+ null /* resultToApp */, null /* resultTo */,
+ 0 /* resultCode */, null /* resultData */, null /* resultExtras */,
+ null /* requiredPermissions */, null /* excludedPermissions */,
+ null /* excludedPackages */, OP_NONE, null /* bOptions */, false /* ordered */,
+ false /* sticky */, MY_PID, SYSTEM_UID, Binder.getCallingUid(),
+ Binder.getCallingPid(), userId, BackgroundStartPrivileges.NONE,
+ broadcastAllowList, null /* filterExtrasForReceiver */);
+ }
}
private void cleanupDisabledPackageComponentsLocked(
@@ -4749,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.
@@ -4786,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) {
@@ -4865,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);
@@ -5002,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.
*/
@@ -5126,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/AnrTimer.java b/services/core/java/com/android/server/am/AnrTimer.java
index 378a386..3e17930 100644
--- a/services/core/java/com/android/server/am/AnrTimer.java
+++ b/services/core/java/com/android/server/am/AnrTimer.java
@@ -28,6 +28,7 @@
import android.os.SystemClock;
import android.os.Trace;
import android.text.TextUtils;
+import android.text.format.TimeMigrationUtils;
import android.util.ArrayMap;
import android.util.IndentingPrintWriter;
import android.util.Log;
@@ -44,7 +45,6 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Date;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
@@ -108,6 +108,14 @@
private static final boolean ENABLE_TRACING = false;
/**
+ * Return true if the feature is enabled. By default, the value is take from the Flags class
+ * but it can be changed for local testing.
+ */
+ private static boolean anrTimerServiceEnabled() {
+ return Flags.anrTimerServiceEnabled();
+ }
+
+ /**
* The status of an ANR timer. TIMER_INVALID status is returned when an error is detected.
*/
private static final int TIMER_INVALID = 0;
@@ -142,7 +150,7 @@
/** A partial stack that localizes the caller of the operation. */
final StackTraceElement[] stack;
/** The date, in local time, the error was created. */
- final String date;
+ final long timestamp;
Error(@NonNull String issue, @NonNull String operation, @NonNull String tag,
@NonNull StackTraceElement[] stack, @NonNull String arg) {
@@ -151,7 +159,7 @@
this.tag = tag;
this.stack = stack;
this.arg = arg;
- this.date = new Date().toString();
+ this.timestamp = SystemClock.elapsedRealtime();
}
}
@@ -327,18 +335,36 @@
*/
@VisibleForTesting
static class Injector {
- /**
- * Return a handler for the given Callback.
- */
- Handler getHandler(@NonNull Handler.Callback callback) {
- return null;
- };
+ private final Handler mReferenceHandler;
+
+ Injector(@NonNull Handler handler) {
+ mReferenceHandler = handler;
+ }
/**
- * Return a CpuTracker.
+ * Return a handler for the given Callback, based on the reference handler. The handler
+ * might be mocked, in which case it does not have a valid Looper. In this case, use the
+ * main Looper.
*/
- CpuTracker getTracker() {
- return null;
+ @NonNull
+ Handler newHandler(@NonNull Handler.Callback callback) {
+ Looper looper = mReferenceHandler.getLooper();
+ if (looper == null) looper = Looper.getMainLooper();
+ return new Handler(looper, callback);
+ }
+
+ /**
+ * Return a CpuTracker. The default behavior is to create a new CpuTracker but this changes
+ * for unit tests.
+ **/
+ @NonNull
+ CpuTracker newTracker() {
+ return new CpuTracker();
+ }
+
+ /** Return true if the feature is enabled. */
+ boolean isFeatureEnabled() {
+ return anrTimerServiceEnabled();
}
}
@@ -375,17 +401,11 @@
/** The interface to fetch process statistics that might extend an ANR timeout. */
private final CpuTracker mCpu;
- /** Create a HandlerTimerService based on the input handler. */
- HandlerTimerService(@NonNull Handler handler) {
- mHandler = new Handler(handler.getLooper(), this::expires);
- mCpu = new CpuTracker();
- }
-
/** Create a HandlerTimerService that directly uses the supplied handler and tracker. */
@VisibleForTesting
HandlerTimerService(@NonNull Injector injector) {
- mHandler = injector.getHandler(this::expires);
- mCpu = injector.getTracker();
+ mHandler = injector.newHandler(this::expires);
+ mCpu = injector.newTracker();
}
/** Post a message with the specified timeout. The timer is not modified. */
@@ -491,38 +511,88 @@
private final boolean mLenientCancel = true;
/**
- * The common constructor. A null injector results in a normal, production timer.
+ * The top-level switch for the feature enabled or disabled.
+ */
+ private final FeatureSwitch mFeature;
+
+ /**
+ * Create one AnrTimer instance. The instance is given a handler and a "what". Individual
+ * timers are started with {@link #start}. If a timer expires, then a {@link Message} is sent
+ * immediately to the handler with {@link Message.what} set to what and {@link Message.obj} set
+ * to the timer key.
+ *
+ * AnrTimer instances have a label, which must be unique. The label is used for reporting and
+ * debug.
+ *
+ * If an individual timer expires internally, and the "extend" parameter is true, then the
+ * AnrTimer may extend the individual timer rather than immediately delivering the timeout to
+ * the client. The extension policy is not part of the instance.
+ *
+ * This method accepts an {@link #Injector} to tune behavior for testing. This method should
+ * not be called directly by regular clients.
+ *
+ * @param handler The handler to which the expiration message will be delivered.
+ * @param what The "what" parameter for the expiration message.
+ * @param label A name for this instance.
+ * @param extend A flag to indicate if expired timers can be granted extensions.
+ * @param injector An {@link #Injector} to tune behavior for testing.
*/
@VisibleForTesting
AnrTimer(@NonNull Handler handler, int what, @NonNull String label, boolean extend,
- @Nullable Injector injector) {
+ @NonNull Injector injector) {
mHandler = handler;
mWhat = what;
mLabel = label;
mExtend = extend;
- if (injector == null) {
- mTimerService = new HandlerTimerService(handler);
+ boolean enabled = injector.isFeatureEnabled();
+ if (!enabled) {
+ mFeature = new FeatureDisabled();
+ mTimerService = null;
} else {
+ mFeature = new FeatureEnabled();
mTimerService = new HandlerTimerService(injector);
+
+ synchronized (sAnrTimerList) {
+ sAnrTimerList.add(new WeakReference(this));
+ }
}
- synchronized (sAnrTimerList) {
- sAnrTimerList.add(new WeakReference(this));
- }
- Log.i(TAG, formatSimple("created %s label: \"%s\"", mTimerService.toString(), label));
+ Log.i(TAG, formatSimple("created %s label: \"%s\"", mTimerService, label));
}
/**
- * Create one timer instance for production. The client can ask for extensible timeouts.
+ * Create an AnrTimer instance with the default {@link #Injector}. See {@link AnrTimer(Handler,
+ * int, String, boolean, Injector} for a functional description.
+ *
+ * @param handler The handler to which the expiration message will be delivered.
+ * @param what The "what" parameter for the expiration message.
+ * @param label A name for this instance.
+ * @param extend A flag to indicate if expired timers can be granted extensions.
*/
AnrTimer(@NonNull Handler handler, int what, @NonNull String label, boolean extend) {
- this(handler, what, label, extend, null);
+ this(handler, what, label, extend, new Injector(handler));
}
/**
- * Create one timer instance for production. There are no extensible timeouts.
+ * Create an AnrTimer instance with the default {@link #Injector} and with extensions disabled.
+ * See {@link AnrTimer(Handler, int, String, boolean, Injector} for a functional description.
+ *
+ * @param handler The handler to which the expiration message will be delivered.
+ * @param what The "what" parameter for the expiration message.
+ * @param label A name for this instance.
*/
AnrTimer(@NonNull Handler handler, int what, @NonNull String label) {
- this(handler, what, label, false, null);
+ this(handler, what, label, false);
+ }
+
+ /**
+ * Return true if the service is enabled on this instance. Clients should use this method to
+ * decide if the feature is enabled, and not read the flags directly. This method should be
+ * deleted if and when the feature is enabled permanently.
+ *
+ * @return true if the service is flag-enabled.
+ */
+ boolean serviceEnabled() {
+ return mFeature.enabled();
}
/**
@@ -607,99 +677,223 @@
}
/**
- * Report something about a timer.
+ * Generate a log message for a timer.
*/
private void report(@NonNull Timer timer, @NonNull String msg) {
Log.i(TAG, msg + " " + timer + " " + Objects.toString(timer.arg));
}
- /**
- * Start a timer.
+ /**
+ * The FeatureSwitch class provides a quick switch between feature-enabled behavior and
+ * feature-disabled behavior.
*/
- boolean start(@NonNull V arg, int pid, int uid, long timeoutMs) {
- final Timer timer = Timer.obtain(pid, uid, arg, timeoutMs, this);
- synchronized (mLock) {
- Timer old = mTimerMap.get(arg);
- if (old != null) {
- // There is an existing timer. This is a protocol error in the client. Record
- // the error and then clean up by canceling running timers and discarding expired
- // timers.
- restartedLocked(old.status, arg);
- if (old.status == TIMER_EXPIRED) {
- discard(arg);
+ private abstract class FeatureSwitch {
+ abstract boolean start(@NonNull V arg, int pid, int uid, long timeoutMs);
+
+ abstract boolean cancel(@NonNull V arg);
+
+ abstract boolean accept(@NonNull V arg);
+
+ abstract boolean discard(@NonNull V arg);
+
+ abstract boolean enabled();
+ }
+
+ /**
+ * The FeatureDisabled class bypasses almost all AnrTimer logic. It is used when the AnrTimer
+ * service is disabled via Flags.anrTimerServiceEnabled.
+ */
+ private class FeatureDisabled extends FeatureSwitch {
+ /** Start a timer by sending a message to the client's handler. */
+ @Override
+ boolean start(@NonNull V arg, int pid, int uid, long timeoutMs) {
+ final Message msg = mHandler.obtainMessage(mWhat, arg);
+ mHandler.sendMessageDelayed(msg, timeoutMs);
+ return true;
+ }
+
+ /** Cancel a timer by removing the message from the client's handler. */
+ @Override
+ boolean cancel(@NonNull V arg) {
+ mHandler.removeMessages(mWhat, arg);
+ return true;
+ }
+
+ /** accept() is a no-op when the feature is disabled. */
+ @Override
+ boolean accept(@NonNull V arg) {
+ return true;
+ }
+
+ /** discard() is a no-op when the feature is disabled. */
+ @Override
+ boolean discard(@NonNull V arg) {
+ return true;
+ }
+
+ /** The feature is not enabled. */
+ @Override
+ boolean enabled() {
+ return false;
+ }
+ }
+
+ /**
+ * The FeatureEnabled class enables the AnrTimer logic. It is used when the AnrTimer service
+ * is enabled via Flags.anrTimerServiceEnabled.
+ */
+ private class FeatureEnabled extends FeatureSwitch {
+
+ /**
+ * Start a timer.
+ */
+ @Override
+ boolean start(@NonNull V arg, int pid, int uid, long timeoutMs) {
+ final Timer timer = Timer.obtain(pid, uid, arg, timeoutMs, AnrTimer.this);
+ synchronized (mLock) {
+ Timer old = mTimerMap.get(arg);
+ // There is an existing timer. If the timer was running, then cancel the running
+ // timer and restart it. If the timer was expired record a protocol error and
+ // discard the expired timer.
+ if (old != null) {
+ if (old.status == TIMER_EXPIRED) {
+ restartedLocked(old.status, arg);
+ discard(arg);
+ } else {
+ cancel(arg);
+ }
+ }
+ if (mTimerService.start(timer)) {
+ timer.status = TIMER_RUNNING;
+ mTimerMap.put(arg, timer);
+ mTotalStarted++;
+ mMaxStarted = Math.max(mMaxStarted, mTimerMap.size());
+ if (DEBUG) report(timer, "start");
+ return true;
} else {
- cancel(arg);
+ Log.e(TAG, "AnrTimer.start failed");
+ return false;
}
}
- if (mTimerService.start(timer)) {
- timer.status = TIMER_RUNNING;
- mTimerMap.put(arg, timer);
- mTotalStarted++;
- mMaxStarted = Math.max(mMaxStarted, mTimerMap.size());
- if (DEBUG) report(timer, "start");
+ }
+
+ /**
+ * Cancel a timer. Return false if the timer was not found.
+ */
+ @Override
+ boolean cancel(@NonNull V arg) {
+ synchronized (mLock) {
+ Timer timer = removeLocked(arg);
+ if (timer == null) {
+ if (!mLenientCancel) notFoundLocked("cancel", arg);
+ return false;
+ }
+ mTimerService.cancel(timer);
+ // There may be an expiration message in flight. Cancel it.
+ mHandler.removeMessages(mWhat, arg);
+ if (DEBUG) report(timer, "cancel");
+ timer.release();
return true;
- } else {
- Log.e(TAG, "AnrTimer.start failed");
- return false;
}
}
+
+ /**
+ * Accept a timer in the framework-level handler. The timeout has been accepted and the
+ * timeout handler is executing. Return false if the timer was not found.
+ */
+ @Override
+ boolean accept(@NonNull V arg) {
+ synchronized (mLock) {
+ Timer timer = removeLocked(arg);
+ if (timer == null) {
+ notFoundLocked("accept", arg);
+ return false;
+ }
+ mTimerService.accept(timer);
+ traceEnd(timer);
+ if (DEBUG) report(timer, "accept");
+ timer.release();
+ return true;
+ }
+ }
+
+ /**
+ * Discard a timer in the framework-level handler. For whatever reason, the timer is no
+ * longer interesting. No statistics are collected. Return false if the time was not
+ * found.
+ */
+ @Override
+ boolean discard(@NonNull V arg) {
+ synchronized (mLock) {
+ Timer timer = removeLocked(arg);
+ if (timer == null) {
+ notFoundLocked("discard", arg);
+ return false;
+ }
+ mTimerService.discard(timer);
+ traceEnd(timer);
+ if (DEBUG) report(timer, "discard");
+ timer.release();
+ return true;
+ }
+ }
+
+ /** The feature is enabled. */
+ @Override
+ boolean enabled() {
+ return true;
+ }
}
/**
- * Cancel a timer. Return false if the timer was not found.
+ * Start a timer associated with arg. The same object must be used to cancel, accept, or
+ * discard a timer later. If a timer already exists with the same arg, then the existing timer
+ * is canceled and a new timer is created.
+ *
+ * @param arg The key by which the timer is known. This is never examined or modified.
+ * @param pid The Linux process ID of the target being timed.
+ * @param uid The Linux user ID of the target being timed.
+ * @param timeoutMs The timer timeout, in milliseconds.
+ * @return true if the timer was successfully created.
+ */
+ boolean start(@NonNull V arg, int pid, int uid, long timeoutMs) {
+ return mFeature.start(arg, pid, uid, timeoutMs);
+ }
+
+ /**
+ * Cancel the running timer associated with arg. The timer is forgotten. If the timer has
+ * expired, the call is treated as a discard. No errors are reported if the timer does not
+ * exist or if the timer has expired.
+ *
+ * @return true if the timer was found and was running.
*/
boolean cancel(@NonNull V arg) {
- synchronized (mLock) {
- Timer timer = removeLocked(arg);
- if (timer == null) {
- if (!mLenientCancel) notFoundLocked("cancel", arg);
- return false;
- }
- mTimerService.cancel(timer);
- // There may be an expiration message in flight. Cancel it.
- mHandler.removeMessages(mWhat, arg);
- if (DEBUG) report(timer, "cancel");
- timer.release();
- return true;
- }
+ return mFeature.cancel(arg);
}
/**
- * Accept a timer in the framework-level handler. The timeout has been accepted and the
- * timeout handler is executing. Return false if the timer was not found.
+ * Accept the expired timer associated with arg. This indicates that the caller considers the
+ * timer expiration to be a true ANR. (See {@link #discard} for an alternate response.) It is
+ * an error to accept a running timer, however the running timer will be canceled.
+ *
+ * @return true if the timer was found and was expired.
*/
boolean accept(@NonNull V arg) {
- synchronized (mLock) {
- Timer timer = removeLocked(arg);
- if (timer == null) {
- notFoundLocked("accept", arg);
- return false;
- }
- mTimerService.accept(timer);
- traceEnd(timer);
- if (DEBUG) report(timer, "accept");
- timer.release();
- return true;
- }
+ return mFeature.accept(arg);
}
/**
- * Discard a timer in the framework-level handler. For whatever reason, the timer is no
- * longer interesting. No statistics are collected. Return false if the time was not found.
+ * Discard the expired timer associated with arg. This indicates that the caller considers the
+ * timer expiration to be a false ANR. ((See {@link #accept} for an alternate response.) One
+ * reason to discard an expired timer is if the process being timed was also being debugged:
+ * such a process could be stopped at a breakpoint and its failure to respond would not be an
+ * error. It is an error to discard a running timer, however the running timer will be
+ * canceled.
+ *
+ * @return true if the timer was found and was expired.
*/
boolean discard(@NonNull V arg) {
- synchronized (mLock) {
- Timer timer = removeLocked(arg);
- if (timer == null) {
- notFoundLocked("discard", arg);
- return false;
- }
- mTimerService.discard(timer);
- traceEnd(timer);
- if (DEBUG) report(timer, "discard");
- timer.release();
- return true;
- }
+ return mFeature.discard(arg);
}
/**
@@ -785,7 +979,10 @@
private static void dump(IndentingPrintWriter ipw, int seq, Error err) {
ipw.format("%2d: op:%s tag:%s issue:%s arg:%s\n", seq, err.operation, err.tag,
err.issue, err.arg);
- ipw.format(" date:%s\n", err.date);
+
+ final long offset = System.currentTimeMillis() - SystemClock.elapsedRealtime();
+ final long etime = offset + err.timestamp;
+ ipw.println(" date:" + TimeMigrationUtils.formatMillisWithFixedFormat(etime));
ipw.increaseIndent();
for (int i = 0; i < err.stack.length; i++) {
ipw.println(" " + err.stack[i].toString());
diff --git a/services/core/java/com/android/server/am/AppBatteryTracker.java b/services/core/java/com/android/server/am/AppBatteryTracker.java
index 907069d..147f8d1 100644
--- a/services/core/java/com/android/server/am/AppBatteryTracker.java
+++ b/services/core/java/com/android/server/am/AppBatteryTracker.java
@@ -580,7 +580,11 @@
batteryStatsInternal);
curDuration += curStart - lastUidBatteryUsageStartTs;
try {
- statsCommit.close();
+ if (statsCommit != null) {
+ statsCommit.close();
+ } else {
+ Slog.w(TAG, "Stat was null");
+ }
} catch (IOException e) {
Slog.w(TAG, "Failed to close a stat");
}
@@ -660,7 +664,11 @@
}
}
try {
- stats.close();
+ if (stats != null) {
+ stats.close();
+ } else {
+ Slog.w(TAG, "Stat was null");
+ }
} catch (IOException e) {
Slog.w(TAG, "Failed to close a stat");
}
@@ -684,7 +692,11 @@
final BatteryUsageStats stats = statsList.get(0);
for (int i = 1; i < statsList.size(); i++) {
try {
- statsList.get(i).close();
+ if (statsList.get(i) != null) {
+ statsList.get(i).close();
+ } else {
+ Slog.w(TAG, "Stat was null");
+ }
} catch (IOException e) {
Slog.w(TAG, "Failed to close a stat in BatteryUsageStats List");
}
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index 928b5d8..3cf4332 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -1205,6 +1205,8 @@
trackerMemFactor = mService.mProcessStats.getMemFactorLocked();
}
+ mLastMemoryLevel = memFactor;
+ mLastNumProcesses = mService.mProcessList.getLruSizeLOSP();
if (mService.mConstants.USE_MODERN_TRIM) {
// Modern trim is not sent based on lowmem state
// Dispatch UI_HIDDEN to processes that need it
@@ -1235,8 +1237,6 @@
return false;
}
- mLastMemoryLevel = memFactor;
- mLastNumProcesses = mService.mProcessList.getLruSizeLOSP();
if (memFactor != ADJ_MEM_FACTOR_NORMAL) {
if (mLowRamStartTime == 0) {
mLowRamStartTime = now;
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index 5d31d15..e07c2bc 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -106,6 +106,14 @@
private boolean mTimeoutScheduled;
/**
+ * Snapshotted value of {@link ProcessRecord#getCpuDelayTime()}, typically
+ * used when deciding if we should extend the soft ANR timeout.
+ *
+ * Required when Flags.anrTimerServiceEnabled is false.
+ */
+ long lastCpuDelayTime;
+
+ /**
* Snapshotted value of {@link ProcessStateRecord#getCurProcState()} before
* dispatching the current broadcast to the receiver in this process.
*/
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index eb219a8..d19eae5 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -258,6 +258,10 @@
private static final int MSG_PROCESS_FREEZABLE_CHANGED = 6;
private static final int MSG_UID_STATE_CHANGED = 7;
+ // Required when Flags.anrTimerServiceEnabled is false. This constant should be deleted if and
+ // when the flag is fused on.
+ private static final int MSG_DELIVERY_TIMEOUT_SOFT = 8;
+
private void enqueueUpdateRunningList() {
mLocalHandler.removeMessages(MSG_UPDATE_RUNNING_LIST);
mLocalHandler.sendEmptyMessage(MSG_UPDATE_RUNNING_LIST);
@@ -271,6 +275,14 @@
updateRunningList();
return true;
}
+ // Required when Flags.anrTimerServiceEnabled is false. This case should be deleted if
+ // and when the flag is fused on.
+ case MSG_DELIVERY_TIMEOUT_SOFT: {
+ synchronized (mService) {
+ deliveryTimeoutSoftLocked((BroadcastProcessQueue) msg.obj, msg.arg1);
+ return true;
+ }
+ }
case MSG_DELIVERY_TIMEOUT: {
deliveryTimeout((BroadcastProcessQueue) msg.obj);
return true;
@@ -1030,7 +1042,7 @@
queue.setTimeoutScheduled(true);
final int softTimeoutMillis = (int) (r.isForeground() ? mFgConstants.TIMEOUT
: mBgConstants.TIMEOUT);
- mAnrTimer.start(queue, softTimeoutMillis);
+ startDeliveryTimeoutLocked(queue, softTimeoutMillis);
} else {
queue.setTimeoutScheduled(false);
}
@@ -1110,7 +1122,7 @@
// If we were trying to deliver a manifest broadcast, throw the error as we need
// to try redelivering the broadcast to this receiver.
if (receiver instanceof ResolveInfo) {
- mAnrTimer.cancel(queue);
+ cancelDeliveryTimeoutLocked(queue);
throw new BroadcastDeliveryFailedException(e);
}
finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_FAILURE,
@@ -1159,6 +1171,44 @@
r.resultTo = null;
}
+ // Required when Flags.anrTimerServiceEnabled is false. This function can be replaced with a
+ // single call to {@code mAnrTimer.start()} if and when the flag is fused on.
+ private void startDeliveryTimeoutLocked(@NonNull BroadcastProcessQueue queue,
+ int softTimeoutMillis) {
+ if (mAnrTimer.serviceEnabled()) {
+ mAnrTimer.start(queue, softTimeoutMillis);
+ } else {
+ queue.lastCpuDelayTime = queue.app.getCpuDelayTime();
+ mLocalHandler.sendMessageDelayed(Message.obtain(mLocalHandler,
+ MSG_DELIVERY_TIMEOUT_SOFT, softTimeoutMillis, 0, queue), softTimeoutMillis);
+ }
+ }
+
+ // Required when Flags.anrTimerServiceEnabled is false. This function can be replaced with a
+ // single call to {@code mAnrTimer.cancel()} if and when the flag is fused on.
+ private void cancelDeliveryTimeoutLocked(@NonNull BroadcastProcessQueue queue) {
+ mAnrTimer.cancel(queue);
+ if (!mAnrTimer.serviceEnabled()) {
+ mLocalHandler.removeMessages(MSG_DELIVERY_TIMEOUT_SOFT, queue);
+ }
+ }
+
+ // Required when Flags.anrTimerServiceEnabled is false. This function can be deleted entirely
+ // if and when the flag is fused on.
+ private void deliveryTimeoutSoftLocked(@NonNull BroadcastProcessQueue queue,
+ int softTimeoutMillis) {
+ if (queue.app != null) {
+ // 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 = queue.app.getCpuDelayTime() - queue.lastCpuDelayTime;
+ final long hardTimeoutMillis = MathUtils.constrain(cpuDelayTime, 0, softTimeoutMillis);
+ mAnrTimer.start(queue, hardTimeoutMillis);
+ } else {
+ deliveryTimeoutLocked(queue);
+ }
+ }
+
private void deliveryTimeout(@NonNull BroadcastProcessQueue queue) {
synchronized (mService) {
deliveryTimeoutLocked(queue);
@@ -1292,7 +1342,7 @@
mAnrTimer.discard(queue);
}
} else if (queue.timeoutScheduled()) {
- mAnrTimer.cancel(queue);
+ cancelDeliveryTimeoutLocked(queue);
}
// Given that a receiver just finished, check if the "waitingFor" conditions are met.
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/OomConnection.java b/services/core/java/com/android/server/am/OomConnection.java
new file mode 100644
index 0000000..17a4ce5
--- /dev/null
+++ b/services/core/java/com/android/server/am/OomConnection.java
@@ -0,0 +1,63 @@
+/*
+ * 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.am;
+
+import android.os.OomKillRecord;
+import android.util.Slog;
+
+/** Connection to the out-of-memory (OOM) events' file */
+public final class OomConnection {
+ private static final String TAG = "OomConnection";
+
+ /** Connection listener interface */
+ public interface OomConnectionListener {
+
+ /**
+ * Callback function to handle the newest OOM kills.
+ *
+ * @param oomKills List of oom kills received from `waitOom()`
+ */
+ void handleOomEvent(OomKillRecord[] oomKills);
+ }
+
+ private final OomConnectionListener mOomListener;
+
+ private final OomConnectionThread mOomConnectionThread;
+
+ private static native OomKillRecord[] waitOom();
+
+ public OomConnection(OomConnectionListener listener) {
+ mOomListener = listener;
+ mOomConnectionThread = new OomConnectionThread();
+ mOomConnectionThread.start();
+ }
+
+ private final class OomConnectionThread extends Thread {
+ public void run() {
+ while (true) {
+ OomKillRecord[] oom_kills = null;
+ try {
+ oom_kills = waitOom();
+ mOomListener.handleOomEvent(oom_kills);
+ } catch (RuntimeException e) {
+ Slog.e(TAG, "failed waiting for OOM events: " + e);
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index a97675f..e0e6cad 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -97,6 +97,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.OomKillRecord;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteCallbackList;
@@ -412,6 +413,8 @@
private static LmkdConnection sLmkdConnection = null;
+ private static OomConnection sOomConnection = null;
+
private boolean mOomLevelsSet = false;
private boolean mAppDataIsolationEnabled = false;
@@ -855,6 +858,21 @@
THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
sKillThread.start();
sKillHandler = new KillHandler(sKillThread.getLooper());
+ sOomConnection = new OomConnection(new OomConnection.OomConnectionListener() {
+ @Override
+ public void handleOomEvent(OomKillRecord[] oomKills) {
+ for (OomKillRecord oomKill: oomKills) {
+ synchronized (mProcLock) {
+ noteAppKill(
+ oomKill.getPid(),
+ oomKill.getUid(),
+ ApplicationExitInfo.REASON_LOW_MEMORY,
+ ApplicationExitInfo.SUBREASON_OOM_KILL,
+ "oom");
+ }
+ }
+ }
+ });
sLmkdConnection = new LmkdConnection(sKillThread.getLooper().getQueue(),
new LmkdConnection.LmkdConnectionListener() {
@Override
@@ -1421,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];
@@ -1452,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/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index d8a2695..3771c05 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1255,11 +1255,10 @@
killProcessGroup = true;
}
if (killProcessGroup) {
- if (async) {
- ProcessList.killProcessGroup(uid, mPid);
- } else {
+ if (!async) {
Process.sendSignalToProcessGroup(uid, mPid, OsConstants.SIGKILL);
}
+ ProcessList.killProcessGroup(uid, mPid);
}
}
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 4a0bc4b..16e3fdf2 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -131,13 +131,17 @@
"car_telemetry",
"codec_fwk",
"companion",
+ "content_protection",
"context_hub",
"core_experiments_team_internal",
"core_graphics",
"haptics",
"hardware_backed_security_mainline",
+ "input",
"machine_learning",
+ "mainline_sdk",
"media_audio",
+ "media_drm",
"media_solutions",
"nfc",
"pixel_audio_android",
@@ -159,9 +163,12 @@
"wear_system_health",
"wear_systems",
"window_surfaces",
- "windowing_frontend"
+ "windowing_frontend",
};
+ public static final String NAMESPACE_REBOOT_STAGING = "staged";
+ public static final String NAMESPACE_REBOOT_STAGING_DELIMITER = "*";
+
private final String[] mGlobalSettings;
private final String[] mDeviceConfigScopes;
@@ -257,6 +264,22 @@
}
});
}
+
+ // add sys prop sync callback for staged flag values
+ DeviceConfig.addOnPropertiesChangedListener(
+ NAMESPACE_REBOOT_STAGING,
+ AsyncTask.THREAD_POOL_EXECUTOR,
+ (DeviceConfig.Properties properties) -> {
+ String scope = properties.getNamespace();
+ for (String key : properties.getKeyset()) {
+ String aconfigPropertyName = makeAconfigFlagStagedPropertyName(key);
+ if (aconfigPropertyName == null) {
+ log("unable to construct system property for " + scope + "/" + key);
+ return;
+ }
+ setProperty(aconfigPropertyName, properties.getString(key, null));
+ }
+ });
}
public static SettingsToPropertiesMapper start(ContentResolver contentResolver) {
@@ -328,6 +351,35 @@
}
/**
+ * system property name constructing rule for staged aconfig flags, the flag name
+ * is in the form of [namespace]*[actual flag name], we should push the following
+ * to system properties
+ * "next_boot.[actual sys prop name]".
+ * If the name contains invalid characters or substrings for system property name,
+ * will return null.
+ * @param flagName
+ * @return
+ */
+ @VisibleForTesting
+ static String makeAconfigFlagStagedPropertyName(String flagName) {
+ int idx = flagName.indexOf(NAMESPACE_REBOOT_STAGING_DELIMITER);
+ if (idx == -1 || idx == flagName.length() - 1 || idx == 0) {
+ log("invalid staged flag: " + flagName);
+ return null;
+ }
+
+ String propertyName = "next_boot." + makeAconfigFlagPropertyName(
+ flagName.substring(0, idx), flagName.substring(idx+1));
+
+ if (!propertyName.matches(SYSTEM_PROPERTY_VALID_CHARACTERS_REGEX)
+ || propertyName.contains(SYSTEM_PROPERTY_INVALID_SUBSTRING)) {
+ return null;
+ }
+
+ return propertyName;
+ }
+
+ /**
* system property name constructing rule for aconfig flags:
* "persist.device_config.aconfig_flags.[category_name].[flag_name]".
* If the name contains invalid characters or substrings for system property name,
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index b03cc62..26d99d8 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -6,4 +6,12 @@
description: "Utilize new OomAdjuster implementation"
bug: "298055811"
is_fixed_read_only: true
-}
\ No newline at end of file
+}
+
+flag {
+ name: "anr_timer_service_enabled"
+ namespace: "system_performance"
+ is_fixed_read_only: true
+ description: "Feature flag for the ANR timer service"
+ bug: "282428924"
+}
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..d65c7c2c 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
@@ -189,6 +189,8 @@
private final AtomicBoolean mEnableCsd = new AtomicBoolean(false);
+ private final AtomicBoolean mForceCsdProperty = new AtomicBoolean(false);
+
private final Object mCsdAsAFeatureLock = new Object();
@GuardedBy("mCsdAsAFeatureLock")
@@ -375,9 +377,21 @@
}
}
+ private boolean updateCsdForTestApi() {
+ if (mForceCsdProperty.get() != SystemProperties.getBoolean(
+ SYSTEM_PROPERTY_SAFEMEDIA_CSD_FORCE, false)) {
+ updateCsdEnabled("SystemPropertiesChangeCallback");
+ }
+
+ return mEnableCsd.get();
+ }
+
float getCsd() {
if (!mEnableCsd.get()) {
- return -1.f;
+ // since this will only be called by a test api enable csd if system property is set
+ if (!updateCsdForTestApi()) {
+ return -1.f;
+ }
}
final ISoundDose soundDose = mSoundDose.get();
@@ -396,7 +410,10 @@
void setCsd(float csd) {
if (!mEnableCsd.get()) {
- return;
+ // since this will only be called by a test api enable csd if system property is set
+ if (!updateCsdForTestApi()) {
+ return;
+ }
}
SoundDoseRecord[] doseRecordsArray;
@@ -430,7 +447,10 @@
void resetCsdTimeouts() {
if (!mEnableCsd.get()) {
- return;
+ // since this will only be called by a test api enable csd if system property is set
+ if (!updateCsdForTestApi()) {
+ return;
+ }
}
synchronized (mCsdStateLock) {
@@ -440,7 +460,10 @@
void forceUseFrameworkMel(boolean useFrameworkMel) {
if (!mEnableCsd.get()) {
- return;
+ // since this will only be called by a test api enable csd if system property is set
+ if (!updateCsdForTestApi()) {
+ return;
+ }
}
final ISoundDose soundDose = mSoundDose.get();
@@ -458,7 +481,10 @@
void forceComputeCsdOnAllDevices(boolean computeCsdOnAllDevices) {
if (!mEnableCsd.get()) {
- return;
+ // since this will only be called by a test api enable csd if system property is set
+ if (!updateCsdForTestApi()) {
+ return;
+ }
}
final ISoundDose soundDose = mSoundDose.get();
@@ -488,7 +514,7 @@
try {
return soundDose.isSoundDoseHalSupported();
} catch (RemoteException e) {
- Log.e(TAG, "Exception while forcing CSD computation on all devices", e);
+ Log.e(TAG, "Exception while querying the csd enabled status", e);
}
return false;
}
@@ -544,7 +570,7 @@
audioDeviceCategory.csdCompatible = isHeadphone;
soundDose.setAudioDeviceCategory(audioDeviceCategory);
} catch (RemoteException e) {
- Log.e(TAG, "Exception while forcing the internal MEL computation", e);
+ Log.e(TAG, "Exception while setting the audio device category", e);
}
}
@@ -894,7 +920,7 @@
mCachedAudioDeviceCategories.clear();
}
} catch (RemoteException e) {
- Log.e(TAG, "Exception while forcing the internal MEL computation", e);
+ Log.e(TAG, "Exception while initializing the cached audio device categories", e);
}
synchronized (mCsdAsAFeatureLock) {
@@ -991,19 +1017,20 @@
}
private void updateCsdEnabled(String caller) {
- boolean csdForce = SystemProperties.getBoolean(SYSTEM_PROPERTY_SAFEMEDIA_CSD_FORCE, false);
+ mForceCsdProperty.set(SystemProperties.getBoolean(SYSTEM_PROPERTY_SAFEMEDIA_CSD_FORCE,
+ false));
// we are using the MCC overlaid legacy flag used for the safe volume enablement
// to determine whether the MCC enforces any safe hearing standard.
boolean mccEnforcedSafeMedia = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_safe_media_volume_enabled);
boolean csdEnable = mContext.getResources().getBoolean(
R.bool.config_safe_sound_dosage_enabled);
- boolean newEnabledCsd = (mccEnforcedSafeMedia && csdEnable) || csdForce;
+ boolean newEnabledCsd = (mccEnforcedSafeMedia && csdEnable) || mForceCsdProperty.get();
synchronized (mCsdAsAFeatureLock) {
if (!mccEnforcedSafeMedia && csdEnable) {
mIsCsdAsAFeatureAvailable = true;
- newEnabledCsd = mIsCsdAsAFeatureEnabled || csdForce;
+ newEnabledCsd = mIsCsdAsAFeatureEnabled || mForceCsdProperty.get();
Log.v(TAG, caller + ": CSD as a feature is not enforced and enabled: "
+ newEnabledCsd);
} else {
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 4538cad..1760bb3 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -41,6 +41,7 @@
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.IAuthService;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
+import android.hardware.biometrics.IBiometricPromptStatusListener;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IInvalidationCallback;
@@ -357,6 +358,18 @@
}
@Override
+ public void registerBiometricPromptStatusListener(
+ IBiometricPromptStatusListener listener) throws RemoteException {
+ checkInternalPermission();
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mBiometricService.registerBiometricPromptStatusListener(listener);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void invalidateAuthenticatorIds(int userId, int fromSensorId,
IInvalidationCallback callback) throws RemoteException {
checkInternalPermission();
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 1898b80..9569f23 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -41,6 +41,7 @@
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
+import android.hardware.biometrics.IBiometricPromptStatusListener;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceReceiver;
@@ -88,6 +89,7 @@
import java.util.Map;
import java.util.Random;
import java.util.Set;
+import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Supplier;
@@ -105,6 +107,8 @@
@VisibleForTesting
final SettingObserver mSettingObserver;
private final List<EnabledOnKeyguardCallback> mEnabledOnKeyguardCallbacks;
+ private final ConcurrentLinkedQueue<BiometricPromptStatusListener>
+ mBiometricPromptStatusListeners;
private final Random mRandom = new Random();
@NonNull private final Supplier<Long> mRequestCounter;
@NonNull private final BiometricContext mBiometricContext;
@@ -425,6 +429,42 @@
}
}
+ final class BiometricPromptStatusListener implements IBinder.DeathRecipient {
+ private final IBiometricPromptStatusListener mBiometricPromptStatusListener;
+
+ BiometricPromptStatusListener(IBiometricPromptStatusListener callback) {
+ mBiometricPromptStatusListener = callback;
+ }
+
+ void notifyBiometricPromptShowing() {
+ try {
+ mBiometricPromptStatusListener.onBiometricPromptShowing();
+ } catch (DeadObjectException e) {
+ Slog.w(TAG, "Death while invoking notifyHandleAuthenticate", e);
+ mBiometricPromptStatusListeners.remove(this);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to invoke notifyHandleAuthenticate", e);
+ }
+ }
+
+ void notifyBiometricPromptIdle() {
+ try {
+ mBiometricPromptStatusListener.onBiometricPromptIdle();
+ } catch (DeadObjectException e) {
+ Slog.w(TAG, "Death while invoking notifyDialogDismissed", e);
+ mBiometricPromptStatusListeners.remove(this);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to invoke notifyDialogDismissed", e);
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ Slog.e(TAG, "Biometric prompt callback binder died");
+ mBiometricPromptStatusListeners.remove(this);
+ }
+ }
+
// Receives events from individual biometric sensors.
private IBiometricSensorReceiver createBiometricSensorReceiver(final long requestId) {
return new IBiometricSensorReceiver.Stub() {
@@ -705,6 +745,22 @@
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override // Binder call
+ public void registerBiometricPromptStatusListener(IBiometricPromptStatusListener callback) {
+ super.registerBiometricPromptStatusListener_enforcePermission();
+
+ BiometricPromptStatusListener biometricPromptStatusListener =
+ new BiometricPromptStatusListener(callback);
+ mBiometricPromptStatusListeners.add(biometricPromptStatusListener);
+
+ if (mAuthSession != null) {
+ biometricPromptStatusListener.notifyBiometricPromptShowing();
+ } else {
+ biometricPromptStatusListener.notifyBiometricPromptIdle();
+ }
+ }
+
+ @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
+ @Override // Binder call
public void invalidateAuthenticatorIds(int userId, int fromSensorId,
IInvalidationCallback callback) {
@@ -1044,6 +1100,7 @@
mDevicePolicyManager = mInjector.getDevicePolicyManager(context);
mImpl = new BiometricServiceWrapper();
mEnabledOnKeyguardCallbacks = new ArrayList<>();
+ mBiometricPromptStatusListeners = new ConcurrentLinkedQueue<>();
mSettingObserver = mInjector.getSettingObserver(context, mHandler,
mEnabledOnKeyguardCallbacks);
mRequestCounter = mInjector.getRequestGenerator();
@@ -1158,6 +1215,7 @@
if (finished) {
Slog.d(TAG, "handleOnError: AuthSession finished");
mAuthSession = null;
+ notifyAuthSessionChanged();
}
} catch (RemoteException e) {
Slog.e(TAG, "RemoteException", e);
@@ -1186,6 +1244,7 @@
session.onDialogDismissed(reason, credentialAttestation);
mAuthSession = null;
+ notifyAuthSessionChanged();
}
private void handleOnTryAgainPressed(long requestId) {
@@ -1235,6 +1294,7 @@
final boolean finished = session.onClientDied();
if (finished) {
mAuthSession = null;
+ notifyAuthSessionChanged();
}
}
@@ -1349,6 +1409,16 @@
});
}
+ private void notifyAuthSessionChanged() {
+ for (BiometricPromptStatusListener listener : mBiometricPromptStatusListeners) {
+ if (mAuthSession == null) {
+ listener.notifyBiometricPromptIdle();
+ } else {
+ listener.notifyBiometricPromptShowing();
+ }
+ }
+ }
+
/**
* handleAuthenticate() (above) which is called from BiometricPrompt determines which
* modality/modalities to start authenticating with. authenticateInternal() should only be
@@ -1386,6 +1456,7 @@
} catch (RemoteException e) {
Slog.e(TAG, "RemoteException", e);
}
+ notifyAuthSessionChanged();
}
private void handleCancelAuthentication(long requestId) {
@@ -1400,6 +1471,7 @@
if (finished) {
Slog.d(TAG, "handleCancelAuthentication: AuthSession finished");
mAuthSession = null;
+ notifyAuthSessionChanged();
}
}
diff --git a/services/core/java/com/android/server/biometrics/biometrics.aconfig b/services/core/java/com/android/server/biometrics/biometrics.aconfig
index b537e0e..b12d831 100644
--- a/services/core/java/com/android/server/biometrics/biometrics.aconfig
+++ b/services/core/java/com/android/server/biometrics/biometrics.aconfig
@@ -6,3 +6,10 @@
description: "This flag controls tunscany virtual HAL feature"
bug: "294254230"
}
+
+flag {
+ name: "de_hidl"
+ namespace: "biometrics_framework"
+ description: "feature flag for biometrics de-hidl"
+ bug: "287332354"
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 78c95ad..a47135f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -35,6 +35,7 @@
import android.util.Slog;
import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.Flags;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
@@ -116,7 +117,24 @@
@LockoutTracker.LockoutMode
public int handleFailedAttempt(int userId) {
- return LockoutTracker.LOCKOUT_NONE;
+ if (Flags.deHidl()) {
+ if (mLockoutTracker != null) {
+ mLockoutTracker.addFailedAttemptForUser(getTargetUserId());
+ }
+ @LockoutTracker.LockoutMode final int lockoutMode =
+ getLockoutTracker().getLockoutModeForUser(userId);
+ final PerformanceTracker performanceTracker =
+ PerformanceTracker.getInstanceForSensorId(getSensorId());
+ if (lockoutMode == LockoutTracker.LOCKOUT_PERMANENT) {
+ performanceTracker.incrementPermanentLockoutForUser(userId);
+ } else if (lockoutMode == LockoutTracker.LOCKOUT_TIMED) {
+ performanceTracker.incrementTimedLockoutForUser(userId);
+ }
+
+ return lockoutMode;
+ } else {
+ return LockoutTracker.LOCKOUT_NONE;
+ }
}
protected long getStartTimeMs() {
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
index 8a54ae5..037ea38a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -586,4 +586,13 @@
}
}, 10000);
}
+
+ /**
+ * Handle stop user client when user switching occurs.
+ */
+ public void onUserStopped() {}
+
+ public Handler getHandler() {
+ return mHandler;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/LockoutCache.java b/services/core/java/com/android/server/biometrics/sensors/LockoutCache.java
index 95c4903..35e9bcb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/LockoutCache.java
+++ b/services/core/java/com/android/server/biometrics/sensors/LockoutCache.java
@@ -32,6 +32,7 @@
mUserLockoutStates = new SparseIntArray();
}
+ @Override
public void setLockoutModeForUser(int userId, @LockoutMode int mode) {
Slog.d(TAG, "Lockout for user: " + userId + " is " + mode);
synchronized (this) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/LockoutTracker.java b/services/core/java/com/android/server/biometrics/sensors/LockoutTracker.java
index 4a59c9d..8271380 100644
--- a/services/core/java/com/android/server/biometrics/sensors/LockoutTracker.java
+++ b/services/core/java/com/android/server/biometrics/sensors/LockoutTracker.java
@@ -35,4 +35,7 @@
@interface LockoutMode {}
@LockoutMode int getLockoutModeForUser(int userId);
+ void setLockoutModeForUser(int userId, @LockoutMode int mode);
+ default void resetFailedAttemptsForUser(boolean clearAttemptCounter, int userId) {}
+ default void addFailedAttemptForUser(int userId) {}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
index 694dfd2..8075470 100644
--- a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
@@ -165,6 +165,7 @@
}
}
+ @Override
public void onUserStopped() {
if (mStopUserClient == null) {
Slog.e(getTag(), "Unexpected onUserStopped");
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/LockoutHalImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/LockoutHalImpl.java
index cc00227..e5d4a63 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/LockoutHalImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/LockoutHalImpl.java
@@ -31,6 +31,11 @@
return mCurrentUserLockoutMode;
}
+ @Override
+ public void setLockoutModeForUser(int userId, @LockoutMode int mode) {
+ setCurrentUserLockoutMode(mode);
+ }
+
public void setCurrentUserLockoutMode(@LockoutMode int lockoutMode) {
mCurrentUserLockoutMode = lockoutMode;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlConversionUtils.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlConversionUtils.java
index 573c20f..d149f52 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlConversionUtils.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlConversionUtils.java
@@ -38,7 +38,7 @@
/**
* Utilities for converting from hardware to framework-defined AIDL models.
*/
-final class AidlConversionUtils {
+public final class AidlConversionUtils {
private static final String TAG = "AidlConversionUtils";
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlResponseHandler.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlResponseHandler.java
new file mode 100644
index 0000000..57f5b34
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlResponseHandler.java
@@ -0,0 +1,319 @@
+/*
+ * 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.biometrics.sensors.face.aidl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.biometrics.face.AuthenticationFrame;
+import android.hardware.biometrics.face.EnrollmentFrame;
+import android.hardware.biometrics.face.Error;
+import android.hardware.biometrics.face.ISessionCallback;
+import android.hardware.face.Face;
+import android.hardware.keymaster.HardwareAuthToken;
+import android.util.Slog;
+
+import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.sensors.AcquisitionClient;
+import com.android.server.biometrics.sensors.AuthSessionCoordinator;
+import com.android.server.biometrics.sensors.AuthenticationConsumer;
+import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.EnumerateConsumer;
+import com.android.server.biometrics.sensors.ErrorConsumer;
+import com.android.server.biometrics.sensors.LockoutConsumer;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.RemovalConsumer;
+import com.android.server.biometrics.sensors.face.FaceUtils;
+
+import java.util.ArrayList;
+import java.util.function.Consumer;
+
+/**
+ * Response handler for the {@link ISessionCallback} HAL AIDL interface.
+ */
+public class AidlResponseHandler extends ISessionCallback.Stub {
+ /**
+ * Interface to send results to the AidlResponseHandler's owner.
+ */
+ public interface HardwareUnavailableCallback {
+ /**
+ * Invoked when the HAL sends ERROR_HW_UNAVAILABLE.
+ */
+ void onHardwareUnavailable();
+ }
+
+ private static final String TAG = "AidlResponseHandler";
+
+ @NonNull
+ private final Context mContext;
+ @NonNull
+ private final BiometricScheduler mScheduler;
+ private final int mSensorId;
+ private final int mUserId;
+ @NonNull
+ private final LockoutTracker mLockoutCache;
+ @NonNull
+ private final LockoutResetDispatcher mLockoutResetDispatcher;
+
+ @NonNull
+ private final AuthSessionCoordinator mAuthSessionCoordinator;
+ @NonNull
+ private final HardwareUnavailableCallback mHardwareUnavailableCallback;
+
+ public AidlResponseHandler(@NonNull Context context,
+ @NonNull BiometricScheduler scheduler, int sensorId, int userId,
+ @NonNull LockoutTracker lockoutTracker,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull AuthSessionCoordinator authSessionCoordinator,
+ @NonNull HardwareUnavailableCallback hardwareUnavailableCallback) {
+ mContext = context;
+ mScheduler = scheduler;
+ mSensorId = sensorId;
+ mUserId = userId;
+ mLockoutCache = lockoutTracker;
+ mLockoutResetDispatcher = lockoutResetDispatcher;
+ mAuthSessionCoordinator = authSessionCoordinator;
+ mHardwareUnavailableCallback = hardwareUnavailableCallback;
+ }
+
+ @Override
+ public int getInterfaceVersion() {
+ return this.VERSION;
+ }
+
+ @Override
+ public String getInterfaceHash() {
+ return this.HASH;
+ }
+
+ @Override
+ public void onChallengeGenerated(long challenge) {
+ handleResponse(FaceGenerateChallengeClient.class, (c) -> c.onChallengeGenerated(mSensorId,
+ mUserId, challenge), null);
+ }
+
+ @Override
+ public void onChallengeRevoked(long challenge) {
+ handleResponse(FaceRevokeChallengeClient.class, (c) -> c.onChallengeRevoked(mSensorId,
+ mUserId, challenge), null);
+ }
+
+ @Override
+ public void onAuthenticationFrame(AuthenticationFrame frame) {
+ handleResponse(FaceAuthenticationClient.class, (c) -> {
+ if (frame == null) {
+ Slog.e(TAG, "Received null enrollment frame for face authentication client.");
+ return;
+ }
+ c.onAuthenticationFrame(AidlConversionUtils.toFrameworkAuthenticationFrame(frame));
+ }, null);
+ }
+
+ @Override
+ public void onEnrollmentFrame(EnrollmentFrame frame) {
+ handleResponse(FaceEnrollClient.class, (c) -> {
+ if (frame == null) {
+ Slog.e(TAG, "Received null enrollment frame for face enroll client.");
+ return;
+ }
+ c.onEnrollmentFrame(AidlConversionUtils.toFrameworkEnrollmentFrame(frame));
+ }, null);
+ }
+
+ @Override
+ public void onError(byte error, int vendorCode) {
+ onError(AidlConversionUtils.toFrameworkError(error), vendorCode);
+ }
+
+ /**
+ * Handle error messages from the HAL.
+ */
+ public void onError(int error, int vendorCode) {
+ handleResponse(ErrorConsumer.class, (c) -> {
+ c.onError(error, vendorCode);
+ if (error == Error.HW_UNAVAILABLE) {
+ mHardwareUnavailableCallback.onHardwareUnavailable();
+ }
+ }, null);
+ }
+
+ @Override
+ public void onEnrollmentProgress(int enrollmentId, int remaining) {
+ BaseClientMonitor client = mScheduler.getCurrentClient();
+ final int currentUserId;
+ if (client == null) {
+ return;
+ } else {
+ currentUserId = client.getTargetUserId();
+ }
+ final CharSequence name = FaceUtils.getInstance(mSensorId)
+ .getUniqueName(mContext, currentUserId);
+ final Face face = new Face(name, enrollmentId, mSensorId);
+
+ handleResponse(FaceEnrollClient.class, (c) -> c.onEnrollResult(face, remaining), null);
+ }
+
+ @Override
+ public void onAuthenticationSucceeded(int enrollmentId, HardwareAuthToken hat) {
+ final Face face = new Face("" /* name */, enrollmentId, mSensorId);
+ final byte[] byteArray = HardwareAuthTokenUtils.toByteArray(hat);
+ final ArrayList<Byte> byteList = new ArrayList<>();
+ for (byte b : byteArray) {
+ byteList.add(b);
+ }
+ handleResponse(AuthenticationConsumer.class, (c) -> c.onAuthenticated(face,
+ true /* authenticated */, byteList), null);
+ }
+
+ @Override
+ public void onAuthenticationFailed() {
+ final Face face = new Face("" /* name */, 0 /* faceId */, mSensorId);
+ handleResponse(AuthenticationConsumer.class, (c) -> c.onAuthenticated(face,
+ false /* authenticated */, null /* hat */), null);
+ }
+
+ @Override
+ public void onLockoutTimed(long durationMillis) {
+ handleResponse(LockoutConsumer.class, (c) -> c.onLockoutTimed(durationMillis), null);
+ }
+
+ @Override
+ public void onLockoutPermanent() {
+ handleResponse(LockoutConsumer.class, LockoutConsumer::onLockoutPermanent, null);
+ }
+
+ @Override
+ public void onLockoutCleared() {
+ handleResponse(FaceResetLockoutClient.class, FaceResetLockoutClient::onLockoutCleared,
+ (c) -> FaceResetLockoutClient.resetLocalLockoutStateToNone(mSensorId, mUserId,
+ mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator,
+ Utils.getCurrentStrength(mSensorId), -1 /* requestId */));
+ }
+
+ @Override
+ public void onInteractionDetected() {
+ handleResponse(FaceDetectClient.class, FaceDetectClient::onInteractionDetected, null);
+ }
+
+ @Override
+ public void onEnrollmentsEnumerated(int[] enrollmentIds) {
+ if (enrollmentIds.length > 0) {
+ for (int i = 0; i < enrollmentIds.length; ++i) {
+ final Face face = new Face("" /* name */, enrollmentIds[i], mSensorId);
+ final int finalI = i;
+ handleResponse(EnumerateConsumer.class, (c) -> c.onEnumerationResult(face,
+ enrollmentIds.length - finalI - 1), null);
+ }
+ } else {
+ handleResponse(EnumerateConsumer.class, (c) -> c.onEnumerationResult(
+ null /* identifier */, 0 /* remaining */), null);
+ }
+ }
+
+ @Override
+ public void onFeaturesRetrieved(byte[] features) {
+ handleResponse(FaceGetFeatureClient.class, (c) -> c.onFeatureGet(true /* success */,
+ features), null);
+ }
+
+ @Override
+ public void onFeatureSet(byte feature) {
+ handleResponse(FaceSetFeatureClient.class, (c) -> c.onFeatureSet(true /* success */), null);
+ }
+
+ @Override
+ public void onEnrollmentsRemoved(int[] enrollmentIds) {
+ if (enrollmentIds.length > 0) {
+ for (int i = 0; i < enrollmentIds.length; i++) {
+ final Face face = new Face("" /* name */, enrollmentIds[i], mSensorId);
+ final int finalI = i;
+ handleResponse(RemovalConsumer.class,
+ (c) -> c.onRemoved(face, enrollmentIds.length - finalI - 1),
+ null);
+ }
+ } else {
+ handleResponse(RemovalConsumer.class, (c) -> c.onRemoved(null /* identifier */,
+ 0 /* remaining */), null);
+ }
+ }
+
+ @Override
+ public void onAuthenticatorIdRetrieved(long authenticatorId) {
+ handleResponse(FaceGetAuthenticatorIdClient.class, (c) -> c.onAuthenticatorIdRetrieved(
+ authenticatorId), null);
+ }
+
+ @Override
+ public void onAuthenticatorIdInvalidated(long newAuthenticatorId) {
+ handleResponse(FaceInvalidationClient.class, (c) -> c.onAuthenticatorIdInvalidated(
+ newAuthenticatorId), null);
+ }
+
+ /**
+ * Handles acquired messages sent by the HAL (specifically for HIDL HAL).
+ */
+ public void onAcquired(int acquiredInfo, int vendorCode) {
+ handleResponse(AcquisitionClient.class, (c) -> c.onAcquired(acquiredInfo, vendorCode),
+ null);
+ }
+
+ /**
+ * Handles lockout changed messages sent by the HAL (specifically for HIDL HAL).
+ */
+ public void onLockoutChanged(long duration) {
+ mScheduler.getHandler().post(() -> {
+ @LockoutTracker.LockoutMode final int lockoutMode;
+ if (duration == 0) {
+ lockoutMode = LockoutTracker.LOCKOUT_NONE;
+ } else if (duration == -1 || duration == Long.MAX_VALUE) {
+ lockoutMode = LockoutTracker.LOCKOUT_PERMANENT;
+ } else {
+ lockoutMode = LockoutTracker.LOCKOUT_TIMED;
+ }
+
+ mLockoutCache.setLockoutModeForUser(mUserId, lockoutMode);
+
+ if (duration == 0) {
+ mLockoutResetDispatcher.notifyLockoutResetCallbacks(mSensorId);
+ }
+ });
+ }
+
+ private <T> void handleResponse(@NonNull Class<T> className,
+ @NonNull Consumer<T> actionIfClassMatchesClient,
+ @Nullable Consumer<BaseClientMonitor> alternateAction) {
+ mScheduler.getHandler().post(() -> {
+ final BaseClientMonitor client = mScheduler.getCurrentClient();
+ if (className.isInstance(client)) {
+ actionIfClassMatchesClient.accept((T) client);
+ } else {
+ Slog.d(TAG, "Current client is not an instance of " + className.getName());
+ if (alternateAction != null) {
+ alternateAction.accept(client);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onSessionClosed() {
+ mScheduler.getHandler().post(mScheduler::onUserStopped);
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java
index 29eee6b..858bb86 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java
@@ -16,10 +16,14 @@
package com.android.server.biometrics.sensors.face.aidl;
-import static com.android.server.biometrics.sensors.face.aidl.Sensor.HalSessionCallback;
-
import android.annotation.NonNull;
+import android.content.Context;
import android.hardware.biometrics.face.ISession;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+
+import com.android.server.biometrics.sensors.face.hidl.AidlToHidlAdapter;
+
+import java.util.function.Supplier;
/**
* A holder for an AIDL {@link ISession} with additional metadata about the current user
@@ -31,14 +35,22 @@
@NonNull
private final ISession mSession;
private final int mUserId;
- @NonNull private final HalSessionCallback mHalSessionCallback;
+ @NonNull private final AidlResponseHandler mAidlResponseHandler;
public AidlSession(int halInterfaceVersion, @NonNull ISession session, int userId,
- HalSessionCallback halSessionCallback) {
+ AidlResponseHandler aidlResponseHandler) {
mHalInterfaceVersion = halInterfaceVersion;
mSession = session;
mUserId = userId;
- mHalSessionCallback = halSessionCallback;
+ mAidlResponseHandler = aidlResponseHandler;
+ }
+
+ public AidlSession(Context context, Supplier<IBiometricsFace> session, int userId,
+ AidlResponseHandler aidlResponseHandler) {
+ mSession = new AidlToHidlAdapter(context, session, userId, aidlResponseHandler);
+ mHalInterfaceVersion = 0;
+ mUserId = userId;
+ mAidlResponseHandler = aidlResponseHandler;
}
/** The underlying {@link ISession}. */
@@ -52,8 +64,8 @@
}
/** The HAL callback, which should only be used in tests {@See BiometricTestSessionImpl}. */
- HalSessionCallback getHalSessionCallback() {
- return mHalSessionCallback;
+ AidlResponseHandler getHalSessionCallback() {
+ return mAidlResponseHandler;
}
/**
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index 35fc43a..470dc4b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -46,8 +46,8 @@
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
-import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutConsumer;
+import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.PerformanceTracker;
import com.android.server.biometrics.sensors.face.UsageStats;
@@ -57,7 +57,8 @@
/**
* Face-specific authentication client for the {@link IFace} AIDL HAL interface.
*/
-class FaceAuthenticationClient extends AuthenticationClient<AidlSession, FaceAuthenticateOptions>
+public class FaceAuthenticationClient
+ extends AuthenticationClient<AidlSession, FaceAuthenticateOptions>
implements LockoutConsumer {
private static final String TAG = "FaceAuthenticationClient";
@@ -74,11 +75,11 @@
@Nullable
private ICancellationSignal mCancellationSignal;
@Nullable
- private SensorPrivacyManager mSensorPrivacyManager;
+ private final SensorPrivacyManager mSensorPrivacyManager;
@FaceManager.FaceAcquired
private int mLastAcquire = FaceManager.FACE_ACQUIRED_UNKNOWN;
- FaceAuthenticationClient(@NonNull Context context,
+ public FaceAuthenticationClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon,
@NonNull IBinder token, long requestId,
@NonNull ClientMonitorCallbackConverter listener, long operationId,
@@ -86,7 +87,7 @@
boolean requireConfirmation,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
boolean isStrongBiometric, @NonNull UsageStats usageStats,
- @NonNull LockoutCache lockoutCache, boolean allowBackgroundAuthentication,
+ @NonNull LockoutTracker lockoutCache, boolean allowBackgroundAuthentication,
@Authenticators.Types int sensorStrength) {
this(context, lazyDaemon, token, requestId, listener, operationId,
restricted, options, cookie, requireConfirmation, logger, biometricContext,
@@ -103,12 +104,12 @@
boolean requireConfirmation,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
boolean isStrongBiometric, @NonNull UsageStats usageStats,
- @NonNull LockoutCache lockoutCache, boolean allowBackgroundAuthentication,
+ @NonNull LockoutTracker lockoutTracker, boolean allowBackgroundAuthentication,
SensorPrivacyManager sensorPrivacyManager,
@Authenticators.Types int biometricStrength) {
super(context, lazyDaemon, token, listener, operationId, restricted,
options, cookie, requireConfirmation, logger, biometricContext,
- isStrongBiometric, null /* taskStackListener */, null /* lockoutCache */,
+ isStrongBiometric, null /* taskStackListener */, lockoutTracker,
allowBackgroundAuthentication, false /* shouldVibrate */,
biometricStrength);
setRequestId(requestId);
@@ -263,8 +264,13 @@
mLastAcquire = acquireInfo;
final boolean shouldSend = shouldSendAcquiredMessage(acquireInfo, vendorCode);
onAcquiredInternal(acquireInfo, vendorCode, shouldSend);
- PerformanceTracker pt = PerformanceTracker.getInstanceForSensorId(getSensorId());
- pt.incrementAcquireForUser(getTargetUserId(), isCryptoOperation());
+
+ //Check if it is AIDL (lockoutTracker = null) or if it there is no lockout for HIDL
+ if (getLockoutTracker() == null || getLockoutTracker().getLockoutModeForUser(
+ getTargetUserId()) == LockoutTracker.LOCKOUT_NONE) {
+ PerformanceTracker pt = PerformanceTracker.getInstanceForSensorId(getSensorId());
+ pt.incrementAcquireForUser(getTargetUserId(), isCryptoOperation());
+ }
}
/**
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
index f55cf05..dbed1f7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
@@ -85,7 +85,7 @@
}
};
- FaceEnrollClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
+ public FaceEnrollClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull byte[] hardwareAuthToken, @NonNull String opPackageName, long requestId,
@NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec,
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
index 165c3a2..e404bd2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
@@ -36,7 +36,7 @@
public class FaceGenerateChallengeClient extends GenerateChallengeClient<AidlSession> {
private static final String TAG = "FaceGenerateChallengeClient";
- FaceGenerateChallengeClient(@NonNull Context context,
+ public FaceGenerateChallengeClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner,
int sensorId, @NonNull BiometricLogger logger,
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java
index ef3b345..c15049b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.face.IFace;
+import android.hardware.biometrics.face.ISession;
import android.os.IBinder;
import android.os.RemoteException;
import android.provider.Settings;
@@ -33,6 +34,7 @@
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.ErrorConsumer;
import com.android.server.biometrics.sensors.HalClientMonitor;
+import com.android.server.biometrics.sensors.face.hidl.AidlToHidlAdapter;
import java.util.HashMap;
import java.util.Map;
@@ -46,14 +48,16 @@
private static final String TAG = "FaceGetFeatureClient";
private final int mUserId;
+ private final int mFeature;
- FaceGetFeatureClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
+ public FaceGetFeatureClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
@NonNull IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId,
@NonNull String owner, int sensorId, @NonNull BiometricLogger logger,
- @NonNull BiometricContext biometricContext) {
+ @NonNull BiometricContext biometricContext, int feature) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
logger, biometricContext);
mUserId = userId;
+ mFeature = feature;
}
@Override
@@ -70,7 +74,11 @@
@Override
protected void startHalOperation() {
try {
- getFreshDaemon().getSession().getFeatures();
+ ISession session = getFreshDaemon().getSession();
+ if (session instanceof AidlToHidlAdapter) {
+ ((AidlToHidlAdapter) session).setFeature(mFeature);
+ }
+ session.getFeatures();
} catch (RemoteException e) {
Slog.e(TAG, "Unable to getFeature", e);
mCallback.onClientFinished(this, false /* success */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
index f09d192..e75c6ab 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
@@ -38,9 +38,9 @@
/**
* Face-specific internal cleanup client for the {@link IFace} AIDL HAL interface.
*/
-class FaceInternalCleanupClient extends InternalCleanupClient<Face, AidlSession> {
+public class FaceInternalCleanupClient extends InternalCleanupClient<Face, AidlSession> {
- FaceInternalCleanupClient(@NonNull Context context,
+ public FaceInternalCleanupClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, int userId, @NonNull String owner,
int sensorId, @NonNull BiometricLogger logger,
@NonNull BiometricContext biometricContext,
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index cc3118c..dd9c6d5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -493,7 +493,7 @@
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
mAuthenticationStatsCollector),
mBiometricContext, isStrongBiometric,
- mUsageStats, mFaceSensors.get(sensorId).getLockoutCache(),
+ mUsageStats, null /* lockoutTracker */,
allowBackgroundAuthentication, Utils.getCurrentStrength(sensorId));
scheduleForSensor(sensorId, client, new ClientMonitorCallback() {
@Override
@@ -619,7 +619,7 @@
final FaceGetFeatureClient client = new FaceGetFeatureClient(mContext,
mFaceSensors.get(sensorId).getLazySession(), token, callback, userId,
mContext.getOpPackageName(), sensorId, BiometricLogger.ofUnknown(mContext),
- mBiometricContext);
+ mBiometricContext, feature);
scheduleForSensor(sensorId, client);
});
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java
index 0512017..0793888 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java
@@ -36,12 +36,12 @@
/**
* Face-specific removal client for the {@link IFace} AIDL HAL interface.
*/
-class FaceRemovalClient extends RemovalClient<Face, AidlSession> {
+public class FaceRemovalClient extends RemovalClient<Face, AidlSession> {
private static final String TAG = "FaceRemovalClient";
final int[] mBiometricIds;
- FaceRemovalClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
+ public FaceRemovalClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
int[] biometricIds, int userId, @NonNull String owner,
@NonNull BiometricUtils<Face> utils, int sensorId,
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
index 1a12fcdf..77b5592 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
@@ -32,7 +32,6 @@
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ErrorConsumer;
import com.android.server.biometrics.sensors.HalClientMonitor;
-import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
@@ -48,14 +47,14 @@
private static final String TAG = "FaceResetLockoutClient";
private final HardwareAuthToken mHardwareAuthToken;
- private final LockoutCache mLockoutCache;
+ private final LockoutTracker mLockoutCache;
private final LockoutResetDispatcher mLockoutResetDispatcher;
private final int mBiometricStrength;
- FaceResetLockoutClient(@NonNull Context context,
+ public FaceResetLockoutClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, int userId, String owner, int sensorId,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
- @NonNull byte[] hardwareAuthToken, @NonNull LockoutCache lockoutTracker,
+ @NonNull byte[] hardwareAuthToken, @NonNull LockoutTracker lockoutTracker,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@Authenticators.Types int biometricStrength) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
@@ -107,7 +106,7 @@
* be used instead.
*/
static void resetLocalLockoutStateToNone(int sensorId, int userId,
- @NonNull LockoutCache lockoutTracker,
+ @NonNull LockoutTracker lockoutTracker,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull AuthSessionCoordinator authSessionCoordinator,
@Authenticators.Types int biometricStrength, long requestId) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java
index 8838345..0d6143a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java
@@ -38,7 +38,7 @@
private final long mChallenge;
- FaceRevokeChallengeClient(@NonNull Context context,
+ public FaceRevokeChallengeClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token,
int userId, @NonNull String owner, int sensorId,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java
index 6c14387..f6da872 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java
@@ -46,7 +46,7 @@
private final boolean mEnabled;
private final HardwareAuthToken mHardwareAuthToken;
- FaceSetFeatureClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
+ public FaceSetFeatureClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull String owner, int sensorId,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
index 2ad41c2..54e66eb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
@@ -23,16 +23,10 @@
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
-import android.hardware.biometrics.face.AuthenticationFrame;
-import android.hardware.biometrics.face.EnrollmentFrame;
-import android.hardware.biometrics.face.Error;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.ISession;
-import android.hardware.biometrics.face.ISessionCallback;
-import android.hardware.face.Face;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorPropertiesInternal;
-import android.hardware.keymaster.HardwareAuthToken;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -44,29 +38,22 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
-import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.SensorServiceStateProto;
import com.android.server.biometrics.SensorStateProto;
import com.android.server.biometrics.UserStateProto;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
-import com.android.server.biometrics.sensors.AuthSessionCoordinator;
-import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
-import com.android.server.biometrics.sensors.EnumerateConsumer;
import com.android.server.biometrics.sensors.ErrorConsumer;
import com.android.server.biometrics.sensors.LockoutCache;
-import com.android.server.biometrics.sensors.LockoutConsumer;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
-import com.android.server.biometrics.sensors.RemovalConsumer;
import com.android.server.biometrics.sensors.StartUserClient;
import com.android.server.biometrics.sensors.StopUserClient;
import com.android.server.biometrics.sensors.UserAwareBiometricScheduler;
import com.android.server.biometrics.sensors.face.FaceUtils;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
@@ -91,397 +78,6 @@
@NonNull private final Supplier<AidlSession> mLazySession;
@Nullable AidlSession mCurrentSession;
- @VisibleForTesting
- public static class HalSessionCallback extends ISessionCallback.Stub {
- /**
- * Interface to sends results to the HalSessionCallback's owner.
- */
- public interface Callback {
- /**
- * Invoked when the HAL sends ERROR_HW_UNAVAILABLE.
- */
- void onHardwareUnavailable();
- }
-
- @NonNull
- private final Context mContext;
- @NonNull
- private final Handler mHandler;
- @NonNull
- private final String mTag;
- @NonNull
- private final UserAwareBiometricScheduler mScheduler;
- private final int mSensorId;
- private final int mUserId;
- @NonNull
- private final LockoutCache mLockoutCache;
- @NonNull
- private final LockoutResetDispatcher mLockoutResetDispatcher;
-
- @NonNull
- private AuthSessionCoordinator mAuthSessionCoordinator;
- @NonNull
- private final Callback mCallback;
-
- HalSessionCallback(@NonNull Context context, @NonNull Handler handler, @NonNull String tag,
- @NonNull UserAwareBiometricScheduler scheduler, int sensorId, int userId,
- @NonNull LockoutCache lockoutTracker,
- @NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull AuthSessionCoordinator authSessionCoordinator,
- @NonNull Callback callback) {
- mContext = context;
- mHandler = handler;
- mTag = tag;
- mScheduler = scheduler;
- mSensorId = sensorId;
- mUserId = userId;
- mLockoutCache = lockoutTracker;
- mLockoutResetDispatcher = lockoutResetDispatcher;
- mAuthSessionCoordinator = authSessionCoordinator;
- mCallback = callback;
- }
-
- @Override
- public int getInterfaceVersion() {
- return this.VERSION;
- }
-
- @Override
- public String getInterfaceHash() {
- return this.HASH;
- }
-
- @Override
- public void onChallengeGenerated(long challenge) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FaceGenerateChallengeClient)) {
- Slog.e(mTag, "onChallengeGenerated for wrong client: "
- + Utils.getClientName(client));
- return;
- }
-
- final FaceGenerateChallengeClient generateChallengeClient =
- (FaceGenerateChallengeClient) client;
- generateChallengeClient.onChallengeGenerated(mSensorId, mUserId, challenge);
- });
- }
-
- @Override
- public void onChallengeRevoked(long challenge) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FaceRevokeChallengeClient)) {
- Slog.e(mTag, "onChallengeRevoked for wrong client: "
- + Utils.getClientName(client));
- return;
- }
-
- final FaceRevokeChallengeClient revokeChallengeClient =
- (FaceRevokeChallengeClient) client;
- revokeChallengeClient.onChallengeRevoked(mSensorId, mUserId, challenge);
- });
- }
-
- @Override
- public void onAuthenticationFrame(AuthenticationFrame frame) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FaceAuthenticationClient)) {
- Slog.e(mTag, "onAuthenticationFrame for incompatible client: "
- + Utils.getClientName(client));
- return;
-
- }
- if (frame == null) {
- Slog.e(mTag, "Received null authentication frame for client: "
- + Utils.getClientName(client));
- return;
- }
- ((FaceAuthenticationClient) client).onAuthenticationFrame(
- AidlConversionUtils.toFrameworkAuthenticationFrame(frame));
- });
- }
-
- @Override
- public void onEnrollmentFrame(EnrollmentFrame frame) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FaceEnrollClient)) {
- Slog.e(mTag, "onEnrollmentFrame for incompatible client: "
- + Utils.getClientName(client));
- return;
- }
- if (frame == null) {
- Slog.e(mTag, "Received null enrollment frame for client: "
- + Utils.getClientName(client));
- return;
- }
- ((FaceEnrollClient) client).onEnrollmentFrame(
- AidlConversionUtils.toFrameworkEnrollmentFrame(frame));
- });
- }
-
- @Override
- public void onError(byte error, int vendorCode) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- Slog.d(mTag, "onError"
- + ", client: " + Utils.getClientName(client)
- + ", error: " + error
- + ", vendorCode: " + vendorCode);
- if (!(client instanceof ErrorConsumer)) {
- Slog.e(mTag, "onError for non-error consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final ErrorConsumer errorConsumer = (ErrorConsumer) client;
- errorConsumer.onError(AidlConversionUtils.toFrameworkError(error), vendorCode);
-
- if (error == Error.HW_UNAVAILABLE) {
- mCallback.onHardwareUnavailable();
- }
- });
- }
-
- @Override
- public void onEnrollmentProgress(int enrollmentId, int remaining) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FaceEnrollClient)) {
- Slog.e(mTag, "onEnrollmentProgress for non-enroll client: "
- + Utils.getClientName(client));
- return;
- }
-
- final int currentUserId = client.getTargetUserId();
- final CharSequence name = FaceUtils.getInstance(mSensorId)
- .getUniqueName(mContext, currentUserId);
- final Face face = new Face(name, enrollmentId, mSensorId);
-
- final FaceEnrollClient enrollClient = (FaceEnrollClient) client;
- enrollClient.onEnrollResult(face, remaining);
- });
- }
-
- @Override
- public void onAuthenticationSucceeded(int enrollmentId, HardwareAuthToken hat) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof AuthenticationConsumer)) {
- Slog.e(mTag, "onAuthenticationSucceeded for non-authentication consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final AuthenticationConsumer authenticationConsumer =
- (AuthenticationConsumer) client;
- final Face face = new Face("" /* name */, enrollmentId, mSensorId);
- final byte[] byteArray = HardwareAuthTokenUtils.toByteArray(hat);
- final ArrayList<Byte> byteList = new ArrayList<>();
- for (byte b : byteArray) {
- byteList.add(b);
- }
- authenticationConsumer.onAuthenticated(face, true /* authenticated */, byteList);
- });
- }
-
- @Override
- public void onAuthenticationFailed() {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof AuthenticationConsumer)) {
- Slog.e(mTag, "onAuthenticationFailed for non-authentication consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final AuthenticationConsumer authenticationConsumer =
- (AuthenticationConsumer) client;
- final Face face = new Face("" /* name */, 0 /* faceId */, mSensorId);
- authenticationConsumer.onAuthenticated(face, false /* authenticated */,
- null /* hat */);
- });
- }
-
- @Override
- public void onLockoutTimed(long durationMillis) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof LockoutConsumer)) {
- Slog.e(mTag, "onLockoutTimed for non-lockout consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final LockoutConsumer lockoutConsumer = (LockoutConsumer) client;
- lockoutConsumer.onLockoutTimed(durationMillis);
- });
- }
-
- @Override
- public void onLockoutPermanent() {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof LockoutConsumer)) {
- Slog.e(mTag, "onLockoutPermanent for non-lockout consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final LockoutConsumer lockoutConsumer = (LockoutConsumer) client;
- lockoutConsumer.onLockoutPermanent();
- });
- }
-
- @Override
- public void onLockoutCleared() {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FaceResetLockoutClient)) {
- Slog.d(mTag, "onLockoutCleared outside of resetLockout by HAL");
- // Given that onLockoutCleared() can happen at any time, and is not necessarily
- // coming from a specific client, set this to -1 to indicate it wasn't for a
- // specific request.
- FaceResetLockoutClient.resetLocalLockoutStateToNone(mSensorId, mUserId,
- mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator,
- Utils.getCurrentStrength(mSensorId), -1 /* requestId */);
- } else {
- Slog.d(mTag, "onLockoutCleared after resetLockout");
- final FaceResetLockoutClient resetLockoutClient =
- (FaceResetLockoutClient) client;
- resetLockoutClient.onLockoutCleared();
- }
- });
- }
-
- @Override
- public void onInteractionDetected() {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FaceDetectClient)) {
- Slog.e(mTag, "onInteractionDetected for wrong client: "
- + Utils.getClientName(client));
- return;
- }
-
- final FaceDetectClient detectClient = (FaceDetectClient) client;
- detectClient.onInteractionDetected();
- });
- }
-
- @Override
- public void onEnrollmentsEnumerated(int[] enrollmentIds) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof EnumerateConsumer)) {
- Slog.e(mTag, "onEnrollmentsEnumerated for non-enumerate consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final EnumerateConsumer enumerateConsumer =
- (EnumerateConsumer) client;
- if (enrollmentIds.length > 0) {
- for (int i = 0; i < enrollmentIds.length; ++i) {
- final Face face = new Face("" /* name */, enrollmentIds[i], mSensorId);
- enumerateConsumer.onEnumerationResult(face, enrollmentIds.length - i - 1);
- }
- } else {
- enumerateConsumer.onEnumerationResult(null /* identifier */, 0 /* remaining */);
- }
- });
- }
-
- @Override
- public void onFeaturesRetrieved(byte[] features) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FaceGetFeatureClient)) {
- Slog.e(mTag, "onFeaturesRetrieved for non-get feature consumer: "
- + Utils.getClientName(client));
- return;
- }
- final FaceGetFeatureClient faceGetFeatureClient = (FaceGetFeatureClient) client;
- faceGetFeatureClient.onFeatureGet(true /* success */, features);
- });
-
- }
-
- @Override
- public void onFeatureSet(byte feature) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FaceSetFeatureClient)) {
- Slog.e(mTag, "onFeatureSet for non-set consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final FaceSetFeatureClient faceSetFeatureClient = (FaceSetFeatureClient) client;
- faceSetFeatureClient.onFeatureSet(true /* success */);
- });
- }
-
- @Override
- public void onEnrollmentsRemoved(int[] enrollmentIds) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof RemovalConsumer)) {
- Slog.e(mTag, "onRemoved for non-removal consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final RemovalConsumer removalConsumer = (RemovalConsumer) client;
- if (enrollmentIds.length > 0) {
- for (int i = 0; i < enrollmentIds.length; i++) {
- final Face face = new Face("" /* name */, enrollmentIds[i], mSensorId);
- removalConsumer.onRemoved(face, enrollmentIds.length - i - 1);
- }
- } else {
- removalConsumer.onRemoved(null /* identifier */, 0 /* remaining */);
- }
- });
- }
-
- @Override
- public void onAuthenticatorIdRetrieved(long authenticatorId) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FaceGetAuthenticatorIdClient)) {
- Slog.e(mTag, "onAuthenticatorIdRetrieved for wrong consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final FaceGetAuthenticatorIdClient getAuthenticatorIdClient =
- (FaceGetAuthenticatorIdClient) client;
- getAuthenticatorIdClient.onAuthenticatorIdRetrieved(authenticatorId);
- });
- }
-
- @Override
- public void onAuthenticatorIdInvalidated(long newAuthenticatorId) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FaceInvalidationClient)) {
- Slog.e(mTag, "onAuthenticatorIdInvalidated for wrong consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final FaceInvalidationClient invalidationClient = (FaceInvalidationClient) client;
- invalidationClient.onAuthenticatorIdInvalidated(newAuthenticatorId);
- });
- }
-
- @Override
- public void onSessionClosed() {
- mHandler.post(mScheduler::onUserStopped);
- }
- }
Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context,
@NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties,
@@ -511,9 +107,9 @@
public StartUserClient<?, ?> getStartUserClient(int newUserId) {
final int sensorId = mSensorProperties.sensorId;
- final HalSessionCallback resultController = new HalSessionCallback(mContext,
- mHandler, mTag, mScheduler, sensorId, newUserId, mLockoutCache,
- lockoutResetDispatcher,
+ final AidlResponseHandler resultController = new AidlResponseHandler(
+ mContext, mScheduler, sensorId, newUserId,
+ mLockoutCache, lockoutResetDispatcher,
biometricContext.getAuthSessionCoordinator(), () -> {
Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE");
mCurrentSession = null;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/AidlToHidlAdapter.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/AidlToHidlAdapter.java
new file mode 100644
index 0000000..eecf44b
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/AidlToHidlAdapter.java
@@ -0,0 +1,347 @@
+/*
+ * 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.biometrics.sensors.face.hidl;
+
+import android.annotation.DurationMillisLong;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricFaceConstants;
+import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.biometrics.common.OperationContext;
+import android.hardware.biometrics.face.EnrollmentStageConfig;
+import android.hardware.biometrics.face.ISession;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.biometrics.face.V1_0.OptionalBool;
+import android.hardware.biometrics.face.V1_0.Status;
+import android.hardware.common.NativeHandle;
+import android.hardware.face.Face;
+import android.hardware.face.FaceManager;
+import android.hardware.keymaster.HardwareAuthToken;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.sensors.face.aidl.AidlConversionUtils;
+import com.android.server.biometrics.sensors.face.aidl.AidlResponseHandler;
+
+import java.time.Clock;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Supplier;
+
+/**
+ * Adapter to convert AIDL-specific interface {@link ISession} methods to HIDL implementation.
+ */
+public class AidlToHidlAdapter implements ISession {
+
+ private final String TAG = "AidlToHidlAdapter";
+ private static final int CHALLENGE_TIMEOUT_SEC = 600;
+ @DurationMillisLong
+ private static final int GENERATE_CHALLENGE_REUSE_INTERVAL_MILLIS = 60 * 1000;
+ @DurationMillisLong
+ private static final int GENERATE_CHALLENGE_COUNTER_TTL_MILLIS = CHALLENGE_TIMEOUT_SEC * 1000;
+ private static final int INVALID_VALUE = -1;
+ private final Clock mClock;
+ private final List<Long> mGeneratedChallengeCount = new ArrayList<>();
+ @VisibleForTesting static final int ENROLL_TIMEOUT_SEC = 75;
+ private long mGenerateChallengeCreatedAt = INVALID_VALUE;
+ private long mGenerateChallengeResult = INVALID_VALUE;
+ @NonNull private Supplier<IBiometricsFace> mSession;
+ private final int mUserId;
+ private HidlToAidlCallbackConverter mHidlToAidlCallbackConverter;
+ private final Context mContext;
+ private int mFeature = INVALID_VALUE;
+
+ public AidlToHidlAdapter(Context context, Supplier<IBiometricsFace> session,
+ int userId, AidlResponseHandler aidlResponseHandler) {
+ this(context, session, userId, aidlResponseHandler, Clock.systemUTC());
+ }
+
+ AidlToHidlAdapter(Context context, Supplier<IBiometricsFace> session, int userId,
+ AidlResponseHandler aidlResponseHandler, Clock clock) {
+ mSession = session;
+ mUserId = userId;
+ mContext = context;
+ mClock = clock;
+ setCallback(aidlResponseHandler);
+ }
+
+ private void setCallback(AidlResponseHandler aidlResponseHandler) {
+ mHidlToAidlCallbackConverter = new HidlToAidlCallbackConverter(aidlResponseHandler);
+ try {
+ mSession.get().setCallback(mHidlToAidlCallbackConverter);
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Failed to set callback");
+ }
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return null;
+ }
+
+ private boolean isGeneratedChallengeCacheValid() {
+ return mGenerateChallengeCreatedAt != INVALID_VALUE
+ && mGenerateChallengeResult != INVALID_VALUE
+ && mClock.millis() - mGenerateChallengeCreatedAt
+ < GENERATE_CHALLENGE_REUSE_INTERVAL_MILLIS;
+ }
+
+ private void incrementChallengeCount() {
+ mGeneratedChallengeCount.add(0, mClock.millis());
+ }
+
+ private int decrementChallengeCount() {
+ final long now = mClock.millis();
+ // ignore values that are old in case generate/revoke calls are not matched
+ // this doesn't ensure revoke if calls are mismatched but it keeps the list from growing
+ mGeneratedChallengeCount.removeIf(x -> now - x > GENERATE_CHALLENGE_COUNTER_TTL_MILLIS);
+ if (!mGeneratedChallengeCount.isEmpty()) {
+ mGeneratedChallengeCount.remove(0);
+ }
+ return mGeneratedChallengeCount.size();
+ }
+
+ @Override
+ public void generateChallenge() throws RemoteException {
+ incrementChallengeCount();
+ if (isGeneratedChallengeCacheValid()) {
+ Slog.d(TAG, "Current challenge is cached and will be reused");
+ mHidlToAidlCallbackConverter.onChallengeGenerated(mGenerateChallengeResult);
+ return;
+ }
+ mGenerateChallengeCreatedAt = mClock.millis();
+ mGenerateChallengeResult = mSession.get().generateChallenge(CHALLENGE_TIMEOUT_SEC).value;
+ mHidlToAidlCallbackConverter.onChallengeGenerated(mGenerateChallengeResult);
+ }
+
+ @Override
+ public void revokeChallenge(long challenge) throws RemoteException {
+ final boolean shouldRevoke = decrementChallengeCount() == 0;
+ if (!shouldRevoke) {
+ Slog.w(TAG, "scheduleRevokeChallenge skipped - challenge still in use: "
+ + mGeneratedChallengeCount);
+ mHidlToAidlCallbackConverter.onError(0 /* deviceId */, mUserId,
+ BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS, 0 /* vendorCode */);
+ return;
+ }
+ mGenerateChallengeCreatedAt = INVALID_VALUE;
+ mGenerateChallengeResult = INVALID_VALUE;
+ mSession.get().revokeChallenge();
+ mHidlToAidlCallbackConverter.onChallengeRevoked(0L);
+ }
+
+ @Override
+ public EnrollmentStageConfig[] getEnrollmentConfig(byte enrollmentType) throws RemoteException {
+ //unsupported in HIDL
+ return null;
+ }
+
+ @Override
+ public ICancellationSignal enroll(HardwareAuthToken hat, byte type, byte[] features,
+ NativeHandle previewSurface) throws RemoteException {
+ final ArrayList<Byte> token = new ArrayList<>();
+ final byte[] hardwareAuthTokenArray = HardwareAuthTokenUtils.toByteArray(hat);
+ for (byte b : hardwareAuthTokenArray) {
+ token.add(b);
+ }
+ final ArrayList<Integer> disabledFeatures = new ArrayList<>();
+ for (byte b: features) {
+ disabledFeatures.add(AidlConversionUtils.convertAidlToFrameworkFeature(b));
+ }
+ mSession.get().enroll(token, ENROLL_TIMEOUT_SEC, disabledFeatures);
+ return new Cancellation();
+ }
+
+ @Override
+ public ICancellationSignal authenticate(long operationId) throws RemoteException {
+ mSession.get().authenticate(operationId);
+ return new Cancellation();
+ }
+
+ @Override
+ public ICancellationSignal detectInteraction() throws RemoteException {
+ mSession.get().authenticate(0);
+ return new Cancellation();
+ }
+
+ @Override
+ public void enumerateEnrollments() throws RemoteException {
+ mSession.get().enumerate();
+ }
+
+ @Override
+ public void removeEnrollments(int[] enrollmentIds) throws RemoteException {
+ mSession.get().remove(enrollmentIds[0]);
+ }
+
+ /**
+ * Needs to be called before getFeatures is invoked.
+ */
+ public void setFeature(int feature) {
+ mFeature = feature;
+ }
+
+ @Override
+ public void getFeatures() throws RemoteException {
+ final int faceId = getFaceId();
+ if (faceId == INVALID_VALUE || mFeature == INVALID_VALUE) {
+ return;
+ }
+
+ final OptionalBool result = mSession.get()
+ .getFeature(mFeature, faceId);
+
+ if (result.status == Status.OK && result.value) {
+ mHidlToAidlCallbackConverter.onFeatureGet(new byte[]{AidlConversionUtils
+ .convertFrameworkToAidlFeature(mFeature)});
+ } else if (result.status == Status.OK) {
+ mHidlToAidlCallbackConverter.onFeatureGet(new byte[]{});
+ } else {
+ mHidlToAidlCallbackConverter.onError(0 /* deviceId */, mUserId,
+ BiometricFaceConstants.FACE_ERROR_UNKNOWN, 0 /* vendorCode */);
+ }
+
+ mFeature = INVALID_VALUE;
+ }
+
+ @Override
+ public void setFeature(HardwareAuthToken hat, byte feature, boolean enabled)
+ throws RemoteException {
+ final int faceId = getFaceId();
+ if (faceId == INVALID_VALUE) {
+ return;
+ }
+ ArrayList<Byte> hardwareAuthTokenList = new ArrayList<>();
+ for (byte b: HardwareAuthTokenUtils.toByteArray(hat)) {
+ hardwareAuthTokenList.add(b);
+ }
+ final int result = mSession.get().setFeature(
+ AidlConversionUtils.convertAidlToFrameworkFeature(feature),
+ enabled, hardwareAuthTokenList, faceId);
+ if (result == Status.OK) {
+ mHidlToAidlCallbackConverter.onFeatureSet(feature);
+ } else {
+ mHidlToAidlCallbackConverter.onError(0 /* deviceId */, mUserId,
+ BiometricFaceConstants.FACE_ERROR_UNKNOWN, 0 /* vendorCode */);
+ }
+ }
+
+ private int getFaceId() {
+ FaceManager faceManager = mContext.getSystemService(FaceManager.class);
+ List<Face> faces = faceManager.getEnrolledFaces(mUserId);
+ if (faces.isEmpty()) {
+ Slog.d(TAG, "No faces to get feature from.");
+ mHidlToAidlCallbackConverter.onError(0 /* deviceId */, mUserId,
+ BiometricFaceConstants.FACE_ERROR_NOT_ENROLLED, 0 /* vendorCode */);
+ return INVALID_VALUE;
+ }
+
+ return faces.get(0).getBiometricId();
+ }
+
+ @Override
+ public void getAuthenticatorId() throws RemoteException {
+ long authenticatorId = mSession.get().getAuthenticatorId().value;
+ mHidlToAidlCallbackConverter.onAuthenticatorIdRetrieved(authenticatorId);
+ }
+
+ @Override
+ public void invalidateAuthenticatorId() throws RemoteException {
+ //unsupported in HIDL
+ }
+
+ @Override
+ public void resetLockout(HardwareAuthToken hat) throws RemoteException {
+ ArrayList<Byte> hardwareAuthToken = new ArrayList<>();
+ for (byte b : HardwareAuthTokenUtils.toByteArray(hat)) {
+ hardwareAuthToken.add(b);
+ }
+ mSession.get().resetLockout(hardwareAuthToken);
+ }
+
+ @Override
+ public void close() throws RemoteException {
+ //Unsupported in HIDL
+ }
+
+ @Override
+ public ICancellationSignal authenticateWithContext(long operationId, OperationContext context)
+ throws RemoteException {
+ //Unsupported in HIDL
+ return null;
+ }
+
+ @Override
+ public ICancellationSignal enrollWithContext(HardwareAuthToken hat, byte type, byte[] features,
+ NativeHandle previewSurface, OperationContext context) throws RemoteException {
+ //Unsupported in HIDL
+ return null;
+ }
+
+ @Override
+ public ICancellationSignal detectInteractionWithContext(OperationContext context)
+ throws RemoteException {
+ //Unsupported in HIDL
+ return null;
+ }
+
+ @Override
+ public void onContextChanged(OperationContext context) throws RemoteException {
+ //Unsupported in HIDL
+ }
+
+ @Override
+ public int getInterfaceVersion() throws RemoteException {
+ //Unsupported in HIDL
+ return 0;
+ }
+
+ @Override
+ public String getInterfaceHash() throws RemoteException {
+ //Unsupported in HIDL
+ return null;
+ }
+
+ /**
+ * Cancellation in HIDL occurs for the entire session, instead of a specific client.
+ */
+ private class Cancellation extends ICancellationSignal.Stub {
+
+ Cancellation() {}
+ @Override
+ public void cancel() throws RemoteException {
+ try {
+ mSession.get().cancel();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when requesting cancel", e);
+ }
+ }
+
+ @Override
+ public int getInterfaceVersion() throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public String getInterfaceHash() throws RemoteException {
+ return null;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index 1499317..46ce0b6 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -54,6 +54,7 @@
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.AuthenticationStatsBroadcastReceiver;
import com.android.server.biometrics.AuthenticationStatsCollector;
+import com.android.server.biometrics.Flags;
import com.android.server.biometrics.SensorServiceStateProto;
import com.android.server.biometrics.SensorStateProto;
import com.android.server.biometrics.UserStateProto;
@@ -61,6 +62,7 @@
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AcquisitionClient;
+import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
@@ -78,6 +80,8 @@
import com.android.server.biometrics.sensors.face.LockoutHalImpl;
import com.android.server.biometrics.sensors.face.ServiceProvider;
import com.android.server.biometrics.sensors.face.UsageStats;
+import com.android.server.biometrics.sensors.face.aidl.AidlResponseHandler;
+import com.android.server.biometrics.sensors.face.aidl.AidlSession;
import org.json.JSONArray;
import org.json.JSONException;
@@ -131,7 +135,9 @@
private int mCurrentUserId = UserHandle.USER_NULL;
private final int mSensorId;
private final List<Long> mGeneratedChallengeCount = new ArrayList<>();
+ private final LockoutResetDispatcher mLockoutResetDispatcher;
private FaceGenerateChallengeClient mGeneratedChallengeCache = null;
+ private AidlSession mSession;
private final UserSwitchObserver mUserSwitchObserver = new SynchronousUserSwitchObserver() {
@Override
@@ -361,6 +367,7 @@
mLockoutTracker = new LockoutHalImpl();
mHalResultController = new HalResultController(sensorProps.sensorId, context, mHandler,
mScheduler, mLockoutTracker, lockoutResetDispatcher);
+ mLockoutResetDispatcher = lockoutResetDispatcher;
mHalResultController.setCallback(() -> {
mDaemon = null;
mCurrentUserId = UserHandle.USER_NULL;
@@ -420,6 +427,24 @@
});
}
+ public int getCurrentUserId() {
+ return mCurrentUserId;
+ }
+
+ synchronized AidlSession getSession() {
+ if (mDaemon != null && mSession != null) {
+ return mSession;
+ } else {
+ return mSession = new AidlSession(mContext, this::getDaemon, mCurrentUserId,
+ new AidlResponseHandler(mContext, mScheduler, mSensorId,
+ mCurrentUserId, mLockoutTracker, mLockoutResetDispatcher,
+ new AuthSessionCoordinator(), () -> {
+ mDaemon = null;
+ mCurrentUserId = UserHandle.USER_NULL;
+ }));
+ }
+ }
+
private synchronized IBiometricsFace getDaemon() {
if (mTestHalEnabled) {
final TestHal testHal = new TestHal(mContext, mSensorId);
@@ -551,32 +576,63 @@
public void scheduleGenerateChallenge(int sensorId, int userId, @NonNull IBinder token,
@NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
mHandler.post(() -> {
- incrementChallengeCount();
-
- if (isGeneratedChallengeCacheValid()) {
- Slog.d(TAG, "Current challenge is cached and will be reused");
- mGeneratedChallengeCache.reuseResult(receiver);
- return;
- }
-
scheduleUpdateActiveUserWithoutHandler(userId);
- final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(mContext,
- mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
- opPackageName, mSensorId,
- createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext, sSystemClock.millis());
- mGeneratedChallengeCache = client;
- mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
- @Override
- public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
- if (client != clientMonitor) {
- Slog.e(TAG, "scheduleGenerateChallenge onClientStarted, mismatched client."
- + " Expecting: " + client + ", received: " + clientMonitor);
- }
+ if (Flags.deHidl()) {
+ scheduleGenerateChallengeAidl(userId, token, receiver, opPackageName);
+ } else {
+ scheduleGenerateChallengeHidl(userId, token, receiver, opPackageName);
+ }
+ });
+ }
+
+ private void scheduleGenerateChallengeAidl(int userId, @NonNull IBinder token,
+ @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
+ final com.android.server.biometrics.sensors.face.aidl.FaceGenerateChallengeClient client =
+ new com.android.server.biometrics.sensors.face.aidl.FaceGenerateChallengeClient(
+ mContext, this::getSession, token,
+ new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
+ mSensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
+ mBiometricContext);
+ mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
+ @Override
+ public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
+ if (client != clientMonitor) {
+ Slog.e(TAG,
+ "scheduleGenerateChallenge onClientStarted, mismatched client."
+ + " Expecting: " + client + ", received: "
+ + clientMonitor);
}
- });
+ }
+ });
+ }
+
+ private void scheduleGenerateChallengeHidl(int userId, @NonNull IBinder token,
+ @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
+ incrementChallengeCount();
+ if (isGeneratedChallengeCacheValid()) {
+ Slog.d(TAG, "Current challenge is cached and will be reused");
+ mGeneratedChallengeCache.reuseResult(receiver);
+ return;
+ }
+
+ final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(mContext,
+ mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
+ opPackageName, mSensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
+ mBiometricContext, sSystemClock.millis());
+ mGeneratedChallengeCache = client;
+ mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
+ @Override
+ public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
+ if (client != clientMonitor) {
+ Slog.e(TAG,
+ "scheduleGenerateChallenge onClientStarted, mismatched client."
+ + " Expecting: " + client + ", received: "
+ + clientMonitor);
+ }
+ }
});
}
@@ -584,31 +640,62 @@
public void scheduleRevokeChallenge(int sensorId, int userId, @NonNull IBinder token,
@NonNull String opPackageName, long challenge) {
mHandler.post(() -> {
- final boolean shouldRevoke = decrementChallengeCount() == 0;
- if (!shouldRevoke) {
- Slog.w(TAG, "scheduleRevokeChallenge skipped - challenge still in use: "
- + mGeneratedChallengeCount);
- return;
+ if (Flags.deHidl()) {
+ scheduleRevokeChallengeAidl(userId, token, opPackageName);
+ } else {
+ scheduleRevokeChallengeHidl(userId, token, opPackageName);
}
+ });
+ }
- Slog.d(TAG, "scheduleRevokeChallenge executing - no active clients");
- mGeneratedChallengeCache = null;
-
- final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(mContext,
- mLazyDaemon, token, userId, opPackageName, mSensorId,
- createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext);
- mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
- @Override
- public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
- boolean success) {
- if (client != clientMonitor) {
- Slog.e(TAG, "scheduleRevokeChallenge, mismatched client."
- + "Expecting: " + client + ", received: " + clientMonitor);
- }
+ private void scheduleRevokeChallengeAidl(int userId, @NonNull IBinder token,
+ @NonNull String opPackageName) {
+ final com.android.server.biometrics.sensors.face.aidl.FaceRevokeChallengeClient
+ client =
+ new com.android.server.biometrics.sensors.face.aidl.FaceRevokeChallengeClient(
+ mContext, this::getSession, token, userId, opPackageName, mSensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector), mBiometricContext, 0L);
+ mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
+ @Override
+ public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
+ boolean success) {
+ if (client != clientMonitor) {
+ Slog.e(TAG,
+ "scheduleRevokeChallenge, mismatched client." + "Expecting: "
+ + client + ", received: " + clientMonitor);
}
- });
+ }
+ });
+ }
+
+ private void scheduleRevokeChallengeHidl(int userId, @NonNull IBinder token,
+ @NonNull String opPackageName) {
+ final boolean shouldRevoke = decrementChallengeCount() == 0;
+ if (!shouldRevoke) {
+ Slog.w(TAG, "scheduleRevokeChallenge skipped - challenge still in use: "
+ + mGeneratedChallengeCount);
+ return;
+ }
+
+ Slog.d(TAG, "scheduleRevokeChallenge executing - no active clients");
+ mGeneratedChallengeCache = null;
+ final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(mContext,
+ mLazyDaemon, token, userId, opPackageName, mSensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
+ mBiometricContext);
+ mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
+ @Override
+ public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
+ boolean success) {
+ if (client != clientMonitor) {
+ Slog.e(TAG,
+ "scheduleRevokeChallenge, mismatched client." + "Expecting: "
+ + client + ", received: " + clientMonitor);
+ }
+ }
});
}
@@ -620,7 +707,62 @@
final long id = mRequestCounter.incrementAndGet();
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
+ if (Flags.deHidl()) {
+ scheduleEnrollAidl(token, hardwareAuthToken, userId, receiver,
+ opPackageName, disabledFeatures, previewSurface, id);
+ } else {
+ scheduleEnrollHidl(token, hardwareAuthToken, userId, receiver,
+ opPackageName, disabledFeatures, previewSurface, id);
+ }
+ });
+ return id;
+ }
+ private void scheduleEnrollAidl(@NonNull IBinder token,
+ @NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver,
+ @NonNull String opPackageName, @NonNull int[] disabledFeatures,
+ @Nullable Surface previewSurface, long id) {
+ final com.android.server.biometrics.sensors.face.aidl.FaceEnrollClient client =
+ new com.android.server.biometrics.sensors.face.aidl.FaceEnrollClient(
+ mContext, this::getSession, token,
+ new ClientMonitorCallbackConverter(receiver), userId,
+ hardwareAuthToken, opPackageName, id,
+ FaceUtils.getLegacyInstance(mSensorId), disabledFeatures,
+ ENROLL_TIMEOUT_SEC, previewSurface, mSensorId,
+ createLogger(BiometricsProtoEnums.ACTION_ENROLL,
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector), mBiometricContext,
+ mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_faceMaxTemplatesPerUser),
+ false);
+
+ mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
+ @Override
+ public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
+ mBiometricStateCallback.onClientStarted(clientMonitor);
+ }
+
+ @Override
+ public void onBiometricAction(int action) {
+ mBiometricStateCallback.onBiometricAction(action);
+ }
+
+ @Override
+ public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
+ boolean success) {
+ mBiometricStateCallback.onClientFinished(clientMonitor, success);
+ if (success) {
+ // Update authenticatorIds
+ scheduleUpdateActiveUserWithoutHandler(client.getTargetUserId());
+ }
+ }
+ });
+ }
+
+ private void scheduleEnrollHidl(@NonNull IBinder token,
+ @NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver,
+ @NonNull String opPackageName, @NonNull int[] disabledFeatures,
+ @Nullable Surface previewSurface, long id) {
final FaceEnrollClient client = new FaceEnrollClient(mContext, mLazyDaemon, token,
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
opPackageName, id, FaceUtils.getLegacyInstance(mSensorId), disabledFeatures,
@@ -628,7 +770,6 @@
createLogger(BiometricsProtoEnums.ACTION_ENROLL,
BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
mBiometricContext);
-
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
@@ -650,8 +791,6 @@
}
}
});
- });
- return id;
}
@Override
@@ -683,18 +822,46 @@
scheduleUpdateActiveUserWithoutHandler(userId);
final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorId);
- final FaceAuthenticationClient client = new FaceAuthenticationClient(mContext,
- mLazyDaemon, token, requestId, receiver, operationId, restricted,
- options, cookie, false /* requireConfirmation */,
- createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
- mAuthenticationStatsCollector),
- mBiometricContext, isStrongBiometric, mLockoutTracker,
- mUsageStats, allowBackgroundAuthentication,
- Utils.getCurrentStrength(mSensorId));
- mScheduler.scheduleClientMonitor(client);
+ if (Flags.deHidl()) {
+ scheduleAuthenticateAidl(token, operationId, cookie, receiver, options, requestId,
+ restricted, statsClient, allowBackgroundAuthentication, isStrongBiometric);
+ } else {
+ scheduleAuthenticateHidl(token, operationId, cookie, receiver, options, requestId,
+ restricted, statsClient, allowBackgroundAuthentication, isStrongBiometric);
+ }
});
}
+ private void scheduleAuthenticateAidl(@NonNull IBinder token, long operationId,
+ int cookie, @NonNull ClientMonitorCallbackConverter receiver,
+ @NonNull FaceAuthenticateOptions options, long requestId, boolean restricted,
+ int statsClient, boolean allowBackgroundAuthentication, boolean isStrongBiometric) {
+ final com.android.server.biometrics.sensors.face.aidl.FaceAuthenticationClient
+ client =
+ new com.android.server.biometrics.sensors.face.aidl.FaceAuthenticationClient(
+ mContext, this::getSession, token, requestId, receiver, operationId,
+ restricted, options, cookie, false /* requireConfirmation */,
+ createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
+ mAuthenticationStatsCollector), mBiometricContext,
+ isStrongBiometric, mUsageStats, mLockoutTracker,
+ allowBackgroundAuthentication, Utils.getCurrentStrength(mSensorId));
+ mScheduler.scheduleClientMonitor(client);
+ }
+
+ private void scheduleAuthenticateHidl(@NonNull IBinder token, long operationId,
+ int cookie, @NonNull ClientMonitorCallbackConverter receiver,
+ @NonNull FaceAuthenticateOptions options, long requestId, boolean restricted,
+ int statsClient, boolean allowBackgroundAuthentication, boolean isStrongBiometric) {
+ final FaceAuthenticationClient client = new FaceAuthenticationClient(mContext,
+ mLazyDaemon, token, requestId, receiver, operationId, restricted, options,
+ cookie, false /* requireConfirmation */,
+ createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
+ mAuthenticationStatsCollector), mBiometricContext,
+ isStrongBiometric, mLockoutTracker, mUsageStats,
+ allowBackgroundAuthentication, Utils.getCurrentStrength(mSensorId));
+ mScheduler.scheduleClientMonitor(client);
+ }
+
@Override
public long scheduleAuthenticate(@NonNull IBinder token, long operationId,
int cookie, @NonNull ClientMonitorCallbackConverter receiver,
@@ -719,13 +886,11 @@
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
- final FaceRemovalClient client = new FaceRemovalClient(mContext, mLazyDaemon, token,
- new ClientMonitorCallbackConverter(receiver), faceId, userId, opPackageName,
- FaceUtils.getLegacyInstance(mSensorId), mSensorId,
- createLogger(BiometricsProtoEnums.ACTION_REMOVE,
- BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext, mAuthenticatorIds);
- mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
+ if (Flags.deHidl()) {
+ scheduleRemoveAidl(token, userId, receiver, opPackageName, faceId);
+ } else {
+ scheduleRemoveHidl(token, userId, receiver, opPackageName, faceId);
+ }
});
}
@@ -736,17 +901,39 @@
scheduleUpdateActiveUserWithoutHandler(userId);
// For IBiometricsFace@1.0, remove(0) means remove all enrollments
- final FaceRemovalClient client = new FaceRemovalClient(mContext, mLazyDaemon, token,
- new ClientMonitorCallbackConverter(receiver), 0 /* faceId */, userId,
- opPackageName,
- FaceUtils.getLegacyInstance(mSensorId), mSensorId,
- createLogger(BiometricsProtoEnums.ACTION_REMOVE,
- BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext, mAuthenticatorIds);
- mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
+ if (Flags.deHidl()) {
+ scheduleRemoveAidl(token, userId, receiver, opPackageName, 0);
+ } else {
+ scheduleRemoveHidl(token, userId, receiver, opPackageName, 0);
+ }
});
}
+ private void scheduleRemoveAidl(@NonNull IBinder token, int userId,
+ @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName, int faceId) {
+ final com.android.server.biometrics.sensors.face.aidl.FaceRemovalClient client =
+ new com.android.server.biometrics.sensors.face.aidl.FaceRemovalClient(
+ mContext, this::getSession, token,
+ new ClientMonitorCallbackConverter(receiver), new int[]{faceId}, userId,
+ opPackageName, FaceUtils.getLegacyInstance(mSensorId), mSensorId,
+ createLogger(BiometricsProtoEnums.ACTION_REMOVE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector), mBiometricContext,
+ mAuthenticatorIds);
+ mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
+ }
+
+ private void scheduleRemoveHidl(@NonNull IBinder token, int userId,
+ @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName, int faceId) {
+ final FaceRemovalClient client = new FaceRemovalClient(mContext, mLazyDaemon, token,
+ new ClientMonitorCallbackConverter(receiver), faceId, userId,
+ opPackageName, FaceUtils.getLegacyInstance(mSensorId), mSensorId,
+ createLogger(BiometricsProtoEnums.ACTION_REMOVE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
+ mBiometricContext, mAuthenticatorIds);
+ mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
+ }
+
@Override
public void scheduleResetLockout(int sensorId, int userId, @NonNull byte[] hardwareAuthToken) {
mHandler.post(() -> {
@@ -756,89 +943,180 @@
}
scheduleUpdateActiveUserWithoutHandler(userId);
-
- final FaceResetLockoutClient client = new FaceResetLockoutClient(mContext,
- mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId,
- createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext, hardwareAuthToken);
- mScheduler.scheduleClientMonitor(client);
+ if (Flags.deHidl()) {
+ scheduleResetLockoutAidl(userId, hardwareAuthToken);
+ } else {
+ scheduleResetLockoutHidl(userId, hardwareAuthToken);
+ }
});
}
+ private void scheduleResetLockoutAidl(int userId,
+ @NonNull byte[] hardwareAuthToken) {
+ final com.android.server.biometrics.sensors.face.aidl.FaceResetLockoutClient client =
+ new com.android.server.biometrics.sensors.face.aidl.FaceResetLockoutClient(
+ mContext, this::getSession, userId, mContext.getOpPackageName(),
+ mSensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
+ mBiometricContext, hardwareAuthToken, mLockoutTracker,
+ mLockoutResetDispatcher, mSensorProperties.sensorStrength);
+ mScheduler.scheduleClientMonitor(client);
+ }
+
+ private void scheduleResetLockoutHidl(int userId,
+ @NonNull byte[] hardwareAuthToken) {
+ final FaceResetLockoutClient client = new FaceResetLockoutClient(mContext,
+ mLazyDaemon,
+ userId, mContext.getOpPackageName(), mSensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
+ mBiometricContext, hardwareAuthToken);
+ mScheduler.scheduleClientMonitor(client);
+ }
+
@Override
public void scheduleSetFeature(int sensorId, @NonNull IBinder token, int userId, int feature,
boolean enabled, @NonNull byte[] hardwareAuthToken,
@NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
mHandler.post(() -> {
- final List<Face> faces = getEnrolledFaces(sensorId, userId);
- if (faces.isEmpty()) {
- Slog.w(TAG, "Ignoring setFeature, no templates enrolled for user: " + userId);
- return;
- }
-
scheduleUpdateActiveUserWithoutHandler(userId);
-
- final int faceId = faces.get(0).getBiometricId();
- final FaceSetFeatureClient client = new FaceSetFeatureClient(mContext,
- mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
- opPackageName, mSensorId, BiometricLogger.ofUnknown(mContext),
- mBiometricContext,
- feature, enabled, hardwareAuthToken, faceId);
- mScheduler.scheduleClientMonitor(client);
+ if (Flags.deHidl()) {
+ scheduleSetFeatureAidl(sensorId, token, userId, feature, enabled, hardwareAuthToken,
+ receiver, opPackageName);
+ } else {
+ scheduleSetFeatureHidl(sensorId, token, userId, feature, enabled, hardwareAuthToken,
+ receiver, opPackageName);
+ }
});
}
+ private void scheduleSetFeatureHidl(int sensorId, @NonNull IBinder token, int userId,
+ int feature, boolean enabled, @NonNull byte[] hardwareAuthToken,
+ @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
+ final List<Face> faces = getEnrolledFaces(sensorId, userId);
+ if (faces.isEmpty()) {
+ Slog.w(TAG, "Ignoring setFeature, no templates enrolled for user: " + userId);
+ return;
+ }
+ final int faceId = faces.get(0).getBiometricId();
+ final FaceSetFeatureClient client = new FaceSetFeatureClient(mContext, mLazyDaemon,
+ token, new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
+ mSensorId, BiometricLogger.ofUnknown(mContext), mBiometricContext, feature,
+ enabled, hardwareAuthToken, faceId);
+ mScheduler.scheduleClientMonitor(client);
+ }
+
+ private void scheduleSetFeatureAidl(int sensorId, @NonNull IBinder token, int userId,
+ int feature, boolean enabled, @NonNull byte[] hardwareAuthToken,
+ @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
+ final com.android.server.biometrics.sensors.face.aidl.FaceSetFeatureClient client =
+ new com.android.server.biometrics.sensors.face.aidl.FaceSetFeatureClient(
+ mContext, this::getSession, token,
+ new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
+ mSensorId, BiometricLogger.ofUnknown(mContext), mBiometricContext,
+ feature, enabled, hardwareAuthToken);
+ mScheduler.scheduleClientMonitor(client);
+ }
+
+
@Override
public void scheduleGetFeature(int sensorId, @NonNull IBinder token, int userId, int feature,
@Nullable ClientMonitorCallbackConverter listener, @NonNull String opPackageName) {
mHandler.post(() -> {
- final List<Face> faces = getEnrolledFaces(sensorId, userId);
- if (faces.isEmpty()) {
- Slog.w(TAG, "Ignoring getFeature, no templates enrolled for user: " + userId);
- return;
- }
-
scheduleUpdateActiveUserWithoutHandler(userId);
- final int faceId = faces.get(0).getBiometricId();
- final FaceGetFeatureClient client = new FaceGetFeatureClient(mContext, mLazyDaemon,
- token, listener, userId, opPackageName, mSensorId,
- BiometricLogger.ofUnknown(mContext), mBiometricContext,
- feature, faceId);
- mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
- @Override
- public void onClientFinished(
- @NonNull BaseClientMonitor clientMonitor, boolean success) {
- if (success && feature == BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION) {
- final int settingsValue = client.getValue() ? 1 : 0;
- Slog.d(TAG, "Updating attention value for user: " + userId
- + " to value: " + settingsValue);
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.FACE_UNLOCK_ATTENTION_REQUIRED,
- settingsValue, userId);
- }
- }
- });
+ if (Flags.deHidl()) {
+ scheduleGetFeatureAidl(token, userId, feature, listener,
+ opPackageName);
+ } else {
+ scheduleGetFeatureHidl(sensorId, token, userId, feature, listener,
+ opPackageName);
+ }
});
}
+ private void scheduleGetFeatureHidl(int sensorId, @NonNull IBinder token, int userId,
+ int feature, @Nullable ClientMonitorCallbackConverter listener,
+ @NonNull String opPackageName) {
+ final List<Face> faces = getEnrolledFaces(sensorId, userId);
+ if (faces.isEmpty()) {
+ Slog.w(TAG, "Ignoring getFeature, no templates enrolled for user: " + userId);
+ return;
+ }
+
+ final int faceId = faces.get(0).getBiometricId();
+ final FaceGetFeatureClient client = new FaceGetFeatureClient(mContext, mLazyDaemon,
+ token, listener, userId, opPackageName, mSensorId,
+ BiometricLogger.ofUnknown(mContext), mBiometricContext, feature, faceId);
+ mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
+ @Override
+ public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
+ boolean success) {
+ if (success
+ && feature == BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION) {
+ final int settingsValue = client.getValue() ? 1 : 0;
+ Slog.d(TAG,
+ "Updating attention value for user: " + userId + " to value: "
+ + settingsValue);
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.FACE_UNLOCK_ATTENTION_REQUIRED, settingsValue,
+ userId);
+ }
+ }
+ });
+ }
+
+ private void scheduleGetFeatureAidl(@NonNull IBinder token, int userId,
+ int feature, @Nullable ClientMonitorCallbackConverter listener,
+ @NonNull String opPackageName) {
+ final com.android.server.biometrics.sensors.face.aidl.FaceGetFeatureClient client =
+ new com.android.server.biometrics.sensors.face.aidl.FaceGetFeatureClient(
+ mContext, this::getSession, token, listener, userId, opPackageName,
+ mSensorId, BiometricLogger.ofUnknown(mContext), mBiometricContext,
+ feature);
+ mScheduler.scheduleClientMonitor(client);
+ }
+
private void scheduleInternalCleanup(int userId,
@Nullable ClientMonitorCallback callback) {
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
-
- final FaceInternalCleanupClient client = new FaceInternalCleanupClient(mContext,
- mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId,
- createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
- BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext,
- FaceUtils.getLegacyInstance(mSensorId), mAuthenticatorIds);
- mScheduler.scheduleClientMonitor(client, new ClientMonitorCompositeCallback(callback,
- mBiometricStateCallback));
+ if (Flags.deHidl()) {
+ scheduleInternalCleanupAidl(userId, callback);
+ } else {
+ scheduleInternalCleanupHidl(userId, callback);
+ }
});
}
+ private void scheduleInternalCleanupHidl(int userId,
+ @Nullable ClientMonitorCallback callback) {
+ final FaceInternalCleanupClient client = new FaceInternalCleanupClient(mContext,
+ mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId,
+ createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
+ mBiometricContext, FaceUtils.getLegacyInstance(mSensorId),
+ mAuthenticatorIds);
+ mScheduler.scheduleClientMonitor(client,
+ new ClientMonitorCompositeCallback(callback, mBiometricStateCallback));
+ }
+
+ private void scheduleInternalCleanupAidl(int userId,
+ @Nullable ClientMonitorCallback callback) {
+ final com.android.server.biometrics.sensors.face.aidl.FaceInternalCleanupClient
+ client =
+ new com.android.server.biometrics.sensors.face.aidl.FaceInternalCleanupClient(
+ mContext, this::getSession, userId, mContext.getOpPackageName(),
+ mSensorId, createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
+ mBiometricContext, FaceUtils.getLegacyInstance(mSensorId),
+ mAuthenticatorIds);
+ mScheduler.scheduleClientMonitor(client,
+ new ClientMonitorCompositeCallback(callback, mBiometricStateCallback));
+ }
+
@Override
public void scheduleInternalCleanup(int sensorId, int userId,
@Nullable ClientMonitorCallback callback) {
@@ -970,6 +1248,10 @@
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
boolean success) {
if (success) {
+ if (mCurrentUserId != targetUserId) {
+ // Create new session with updated user ID
+ mSession = null;
+ }
mCurrentUserId = targetUserId;
} else {
Slog.w(TAG, "Failed to change user, still: " + mCurrentUserId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlCallbackConverter.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlCallbackConverter.java
new file mode 100644
index 0000000..36a9790
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlCallbackConverter.java
@@ -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.server.biometrics.sensors.face.hidl;
+
+import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback;
+
+import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.sensors.face.aidl.AidlResponseHandler;
+
+import java.util.ArrayList;
+
+/**
+ * Convert HIDL-specific callback interface {@link IBiometricsFaceClientCallback} to AIDL
+ * response handler.
+ */
+public class HidlToAidlCallbackConverter extends IBiometricsFaceClientCallback.Stub {
+
+ private final AidlResponseHandler mAidlResponseHandler;
+
+ public HidlToAidlCallbackConverter(AidlResponseHandler aidlResponseHandler) {
+ mAidlResponseHandler = aidlResponseHandler;
+ }
+
+ @Override
+ public void onEnrollResult(
+ long deviceId, int faceId, int userId, int remaining) {
+ mAidlResponseHandler.onEnrollmentProgress(faceId, remaining);
+ }
+
+ @Override
+ public void onAuthenticated(long deviceId, int faceId, int userId,
+ ArrayList<Byte> token) {
+ final boolean authenticated = faceId != 0;
+ byte[] hardwareAuthToken = new byte[token.size()];
+
+ for (int i = 0; i < token.size(); i++) {
+ hardwareAuthToken[i] = token.get(i);
+ }
+
+ if (authenticated) {
+ mAidlResponseHandler.onAuthenticationSucceeded(faceId,
+ HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken));
+ } else {
+ mAidlResponseHandler.onAuthenticationFailed();
+ }
+ }
+
+ @Override
+ public void onAcquired(long deviceId, int userId, int acquiredInfo,
+ int vendorCode) {
+ mAidlResponseHandler.onAcquired(acquiredInfo, vendorCode);
+ }
+
+ @Override
+ public void onError(long deviceId, int userId, int error, int vendorCode) {
+ mAidlResponseHandler.onError(error, vendorCode);
+ }
+
+ @Override
+ public void onRemoved(long deviceId, ArrayList<Integer> removed, int userId) {
+ int[] enrollmentIds = new int[removed.size()];
+ for (int i = 0; i < removed.size(); i++) {
+ enrollmentIds[i] = removed.get(i);
+ }
+ mAidlResponseHandler.onEnrollmentsRemoved(enrollmentIds);
+ }
+
+ @Override
+ public void onEnumerate(long deviceId, ArrayList<Integer> faceIds, int userId) {
+ int[] enrollmentIds = new int[faceIds.size()];
+ for (int i = 0; i < faceIds.size(); i++) {
+ enrollmentIds[i] = faceIds.get(i);
+ }
+ mAidlResponseHandler.onEnrollmentsEnumerated(enrollmentIds);
+ }
+
+ @Override
+ public void onLockoutChanged(long duration) {
+ mAidlResponseHandler.onLockoutChanged(duration);
+ }
+
+ void onChallengeGenerated(long challenge) {
+ mAidlResponseHandler.onChallengeGenerated(challenge);
+ }
+
+ void onChallengeRevoked(long challenge) {
+ mAidlResponseHandler.onChallengeRevoked(challenge);
+ }
+
+ void onFeatureGet(byte[] features) {
+ mAidlResponseHandler.onFeaturesRetrieved(features);
+ }
+
+ void onFeatureSet(byte feature) {
+ mAidlResponseHandler.onFeatureSet(feature);
+ }
+
+ void onAuthenticatorIdRetrieved(long authenticatorId) {
+ mAidlResponseHandler.onAuthenticatorIdRetrieved(authenticatorId);
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlResponseHandler.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlResponseHandler.java
new file mode 100644
index 0000000..4a01943
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlResponseHandler.java
@@ -0,0 +1,274 @@
+/*
+ * 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.biometrics.sensors.fingerprint.aidl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.biometrics.face.Error;
+import android.hardware.biometrics.fingerprint.ISessionCallback;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.keymaster.HardwareAuthToken;
+import android.util.Slog;
+
+import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.sensors.AcquisitionClient;
+import com.android.server.biometrics.sensors.AuthSessionCoordinator;
+import com.android.server.biometrics.sensors.AuthenticationConsumer;
+import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.EnumerateConsumer;
+import com.android.server.biometrics.sensors.ErrorConsumer;
+import com.android.server.biometrics.sensors.LockoutCache;
+import com.android.server.biometrics.sensors.LockoutConsumer;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.RemovalConsumer;
+import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
+
+import java.util.ArrayList;
+import java.util.function.Consumer;
+
+
+/**
+ * Response handler for the {@link ISessionCallback} HAL AIDL interface.
+ */
+public class AidlResponseHandler extends ISessionCallback.Stub {
+
+ /**
+ * Interface to send results to the AidlResponseHandler's owner.
+ */
+ public interface HardwareUnavailableCallback {
+ /**
+ * Invoked when the HAL sends ERROR_HW_UNAVAILABLE.
+ */
+ void onHardwareUnavailable();
+ }
+
+ private static final String TAG = "AidlResponseHandler";
+
+ @NonNull
+ private final Context mContext;
+ @NonNull
+ private final BiometricScheduler mScheduler;
+ private final int mSensorId;
+ private final int mUserId;
+ @NonNull
+ private final LockoutCache mLockoutCache;
+ @NonNull
+ private final LockoutResetDispatcher mLockoutResetDispatcher;
+ @NonNull
+ private final AuthSessionCoordinator mAuthSessionCoordinator;
+ @NonNull
+ private final HardwareUnavailableCallback mHardwareUnavailableCallback;
+
+ public AidlResponseHandler(@NonNull Context context,
+ @NonNull BiometricScheduler scheduler, int sensorId, int userId,
+ @NonNull LockoutCache lockoutTracker,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull AuthSessionCoordinator authSessionCoordinator,
+ @NonNull HardwareUnavailableCallback hardwareUnavailableCallback) {
+ mContext = context;
+ mScheduler = scheduler;
+ mSensorId = sensorId;
+ mUserId = userId;
+ mLockoutCache = lockoutTracker;
+ mLockoutResetDispatcher = lockoutResetDispatcher;
+ mAuthSessionCoordinator = authSessionCoordinator;
+ mHardwareUnavailableCallback = hardwareUnavailableCallback;
+ }
+
+ @Override
+ public int getInterfaceVersion() {
+ return this.VERSION;
+ }
+
+ @Override
+ public String getInterfaceHash() {
+ return this.HASH;
+ }
+
+ @Override
+ public void onChallengeGenerated(long challenge) {
+ handleResponse(FingerprintGenerateChallengeClient.class, (c) -> c.onChallengeGenerated(
+ mSensorId, mUserId, challenge), null);
+ }
+
+ @Override
+ public void onChallengeRevoked(long challenge) {
+ handleResponse(FingerprintRevokeChallengeClient.class, (c) -> c.onChallengeRevoked(
+ challenge), null);
+ }
+
+ /**
+ * Handles acquired messages sent by the HAL (specifically for HIDL HAL).
+ */
+ public void onAcquired(int acquiredInfo, int vendorCode) {
+ handleResponse(AcquisitionClient.class, (c) -> c.onAcquired(acquiredInfo, vendorCode),
+ null);
+ }
+
+ @Override
+ public void onAcquired(byte info, int vendorCode) {
+ handleResponse(AcquisitionClient.class, (c) -> c.onAcquired(
+ AidlConversionUtils.toFrameworkAcquiredInfo(info), vendorCode), null);
+ }
+
+ /**
+ * Handle error messages from the HAL.
+ */
+ public void onError(int error, int vendorCode) {
+ handleResponse(ErrorConsumer.class, (c) -> {
+ c.onError(error, vendorCode);
+ if (error == Error.HW_UNAVAILABLE) {
+ mHardwareUnavailableCallback.onHardwareUnavailable();
+ }
+ }, null);
+ }
+
+ @Override
+ public void onError(byte error, int vendorCode) {
+ onError(AidlConversionUtils.toFrameworkError(error), vendorCode);
+ }
+
+ @Override
+ public void onEnrollmentProgress(int enrollmentId, int remaining) {
+ BaseClientMonitor client = mScheduler.getCurrentClient();
+ final int currentUserId;
+ if (client == null) {
+ return;
+ } else {
+ currentUserId = client.getTargetUserId();
+ }
+ final CharSequence name = FingerprintUtils.getInstance(mSensorId)
+ .getUniqueName(mContext, currentUserId);
+ final Fingerprint fingerprint = new Fingerprint(name, currentUserId,
+ enrollmentId, mSensorId);
+ handleResponse(FingerprintEnrollClient.class, (c) -> c.onEnrollResult(fingerprint,
+ remaining), null);
+ }
+
+ @Override
+ public void onAuthenticationSucceeded(int enrollmentId, HardwareAuthToken hat) {
+ final Fingerprint fp = new Fingerprint("", enrollmentId, mSensorId);
+ final byte[] byteArray = HardwareAuthTokenUtils.toByteArray(hat);
+ final ArrayList<Byte> byteList = new ArrayList<>();
+ for (byte b : byteArray) {
+ byteList.add(b);
+ }
+ handleResponse(AuthenticationConsumer.class, (c) -> c.onAuthenticated(fp,
+ true /* authenticated */, byteList), (c) -> onInteractionDetected());
+ }
+
+ @Override
+ public void onAuthenticationFailed() {
+ final Fingerprint fp = new Fingerprint("", 0 /* enrollmentId */, mSensorId);
+ handleResponse(AuthenticationConsumer.class, (c) -> c.onAuthenticated(fp,
+ false /* authenticated */, null /* hardwareAuthToken */),
+ (c) -> onInteractionDetected());
+ }
+
+ @Override
+ public void onLockoutTimed(long durationMillis) {
+ handleResponse(LockoutConsumer.class, (c) -> c.onLockoutTimed(durationMillis),
+ null);
+ }
+
+ @Override
+ public void onLockoutPermanent() {
+ handleResponse(LockoutConsumer.class, LockoutConsumer::onLockoutPermanent, null);
+ }
+
+ @Override
+ public void onLockoutCleared() {
+ handleResponse(FingerprintResetLockoutClient.class,
+ FingerprintResetLockoutClient::onLockoutCleared,
+ (c) -> FingerprintResetLockoutClient.resetLocalLockoutStateToNone(
+ mSensorId, mUserId, mLockoutCache, mLockoutResetDispatcher,
+ mAuthSessionCoordinator, Utils.getCurrentStrength(mSensorId),
+ -1 /* requestId */));
+ }
+
+ @Override
+ public void onInteractionDetected() {
+ handleResponse(FingerprintDetectClient.class,
+ FingerprintDetectClient::onInteractionDetected, null);
+ }
+
+ @Override
+ public void onEnrollmentsEnumerated(int[] enrollmentIds) {
+ if (enrollmentIds.length > 0) {
+ for (int i = 0; i < enrollmentIds.length; i++) {
+ final Fingerprint fp = new Fingerprint("", enrollmentIds[i], mSensorId);
+ int finalI = i;
+ handleResponse(EnumerateConsumer.class, (c) -> c.onEnumerationResult(fp,
+ enrollmentIds.length - finalI - 1), null);
+ }
+ } else {
+ handleResponse(EnumerateConsumer.class, (c) -> c.onEnumerationResult(null,
+ 0), null);
+ }
+ }
+
+ @Override
+ public void onEnrollmentsRemoved(int[] enrollmentIds) {
+ if (enrollmentIds.length > 0) {
+ for (int i = 0; i < enrollmentIds.length; i++) {
+ final Fingerprint fp = new Fingerprint("", enrollmentIds[i], mSensorId);
+ int finalI = i;
+ handleResponse(RemovalConsumer.class, (c) -> c.onRemoved(fp,
+ enrollmentIds.length - finalI - 1), null);
+ }
+ } else {
+ handleResponse(RemovalConsumer.class, (c) -> c.onRemoved(null, 0),
+ null);
+ }
+ }
+
+ @Override
+ public void onAuthenticatorIdRetrieved(long authenticatorId) {
+ handleResponse(FingerprintGetAuthenticatorIdClient.class,
+ (c) -> c.onAuthenticatorIdRetrieved(authenticatorId), null);
+ }
+
+ @Override
+ public void onAuthenticatorIdInvalidated(long newAuthenticatorId) {
+ handleResponse(FingerprintInvalidationClient.class, (c) -> c.onAuthenticatorIdInvalidated(
+ newAuthenticatorId), null);
+ }
+
+ private <T> void handleResponse(@NonNull Class<T> className,
+ @NonNull Consumer<T> action,
+ @Nullable Consumer<BaseClientMonitor> alternateAction) {
+ mScheduler.getHandler().post(() -> {
+ final BaseClientMonitor client = mScheduler.getCurrentClient();
+ if (className.isInstance(client)) {
+ action.accept((T) client);
+ } else {
+ Slog.e(TAG, "Client monitor is not an instance of " + className.getName());
+ if (alternateAction != null) {
+ alternateAction.accept(client);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onSessionClosed() {
+ mScheduler.getHandler().post(mScheduler::onUserStopped);
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java
index 55861bb..299a310 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java
@@ -16,10 +16,13 @@
package com.android.server.biometrics.sensors.fingerprint.aidl;
-import static com.android.server.biometrics.sensors.fingerprint.aidl.Sensor.HalSessionCallback;
-
import android.annotation.NonNull;
import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+
+import com.android.server.biometrics.sensors.fingerprint.hidl.AidlToHidlAdapter;
+
+import java.util.function.Supplier;
/**
* A holder for an AIDL {@link ISession} with additional metadata about the current user
@@ -30,14 +33,22 @@
private final int mHalInterfaceVersion;
@NonNull private final ISession mSession;
private final int mUserId;
- @NonNull private final HalSessionCallback mHalSessionCallback;
+ @NonNull private final AidlResponseHandler mAidlResponseHandler;
public AidlSession(int halInterfaceVersion, @NonNull ISession session, int userId,
- HalSessionCallback halSessionCallback) {
+ AidlResponseHandler aidlResponseHandler) {
mHalInterfaceVersion = halInterfaceVersion;
mSession = session;
mUserId = userId;
- mHalSessionCallback = halSessionCallback;
+ mAidlResponseHandler = aidlResponseHandler;
+ }
+
+ public AidlSession(@NonNull Supplier<IBiometricsFingerprint> session,
+ int userId, AidlResponseHandler aidlResponseHandler) {
+ mSession = new AidlToHidlAdapter(session, userId, aidlResponseHandler);
+ mHalInterfaceVersion = 0;
+ mUserId = userId;
+ mAidlResponseHandler = aidlResponseHandler;
}
/** The underlying {@link ISession}. */
@@ -51,8 +62,8 @@
}
/** The HAL callback, which should only be used in tests {@See BiometricTestSessionImpl}. */
- HalSessionCallback getHalSessionCallback() {
- return mHalSessionCallback;
+ AidlResponseHandler getHalSessionCallback() {
+ return mAidlResponseHandler;
}
/**
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index 54d1faa..337c3c2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -21,6 +21,7 @@
import android.app.TaskStackListener;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricFingerprintConstants.FingerprintAcquired;
import android.hardware.biometrics.BiometricManager.Authenticators;
@@ -51,8 +52,8 @@
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
-import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutConsumer;
+import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.PerformanceTracker;
import com.android.server.biometrics.sensors.SensorOverlays;
import com.android.server.biometrics.sensors.fingerprint.PowerPressHandler;
@@ -66,7 +67,7 @@
* Fingerprint-specific authentication client supporting the {@link
* android.hardware.biometrics.fingerprint.IFingerprint} AIDL interface.
*/
-class FingerprintAuthenticationClient
+public class FingerprintAuthenticationClient
extends AuthenticationClient<AidlSession, FingerprintAuthenticateOptions>
implements Udfps, LockoutConsumer, PowerPressHandler {
private static final String TAG = "FingerprintAuthenticationClient";
@@ -93,7 +94,7 @@
private Runnable mAuthSuccessRunnable;
private final Clock mClock;
- FingerprintAuthenticationClient(
+ public FingerprintAuthenticationClient(
@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon,
@NonNull IBinder token,
@@ -108,14 +109,14 @@
@NonNull BiometricContext biometricContext,
boolean isStrongBiometric,
@Nullable TaskStackListener taskStackListener,
- @NonNull LockoutCache lockoutCache,
@Nullable IUdfpsOverlayController udfpsOverlayController,
@Nullable ISidefpsController sidefpsController,
boolean allowBackgroundAuthentication,
@NonNull FingerprintSensorPropertiesInternal sensorProps,
@NonNull Handler handler,
@Authenticators.Types int biometricStrength,
- @NonNull Clock clock) {
+ @NonNull Clock clock,
+ @Nullable LockoutTracker lockoutTracker) {
super(
context,
lazyDaemon,
@@ -130,7 +131,7 @@
biometricContext,
isStrongBiometric,
taskStackListener,
- null /* lockoutCache */,
+ lockoutTracker,
allowBackgroundAuthentication,
false /* shouldVibrate */,
biometricStrength);
@@ -211,6 +212,7 @@
boolean authenticated,
ArrayList<Byte> token) {
super.onAuthenticated(identifier, authenticated, token);
+ handleLockout(authenticated);
if (authenticated) {
mState = STATE_STOPPED;
mSensorOverlays.hide(getSensorId());
@@ -219,6 +221,32 @@
}
}
+ private void handleLockout(boolean authenticated) {
+ if (getLockoutTracker() == null) {
+ Slog.d(TAG, "Lockout is implemented by the HAL");
+ return;
+ }
+ if (authenticated) {
+ getLockoutTracker().resetFailedAttemptsForUser(true /* clearAttemptCounter */,
+ getTargetUserId());
+ } else {
+ @LockoutTracker.LockoutMode final int lockoutMode =
+ getLockoutTracker().getLockoutModeForUser(getTargetUserId());
+ if (lockoutMode != LockoutTracker.LOCKOUT_NONE) {
+ Slog.w(TAG, "Fingerprint locked out, lockoutMode(" + lockoutMode + ")");
+ final int errorCode = lockoutMode == LockoutTracker.LOCKOUT_TIMED
+ ? BiometricConstants.BIOMETRIC_ERROR_LOCKOUT
+ : BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
+ // Send the error, but do not invoke the FinishCallback yet. Since lockout is not
+ // controlled by the HAL, the framework must stop the sensor before finishing the
+ // client.
+ mSensorOverlays.hide(getSensorId());
+ onErrorInternal(errorCode, 0 /* vendorCode */, false /* finish */);
+ cancel();
+ }
+ }
+ }
+
@Override
public void onAcquired(@FingerprintAcquired int acquiredInfo, int vendorCode) {
// For UDFPS, notify SysUI with acquiredInfo, so that the illumination can be turned off
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
index 4502e5d..e2413ee 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
@@ -43,7 +43,8 @@
* Performs fingerprint detection without exposing any matching information (e.g. accept/reject
* have the same haptic, lockout counter is not increased).
*/
-class FingerprintDetectClient extends AcquisitionClient<AidlSession> implements DetectionConsumer {
+public class FingerprintDetectClient extends AcquisitionClient<AidlSession>
+ implements DetectionConsumer {
private static final String TAG = "FingerprintDetectClient";
@@ -52,7 +53,8 @@
@NonNull private final SensorOverlays mSensorOverlays;
@Nullable private ICancellationSignal mCancellationSignal;
- FingerprintDetectClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon,
+ public FingerprintDetectClient(@NonNull Context context,
+ @NonNull Supplier<AidlSession> lazyDaemon,
@NonNull IBinder token, long requestId,
@NonNull ClientMonitorCallbackConverter listener,
@NonNull FingerprintAuthenticateOptions options,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index 46ff6b4..06550d8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -49,14 +49,13 @@
import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
import com.android.server.biometrics.sensors.EnrollClient;
import com.android.server.biometrics.sensors.SensorOverlays;
-import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
import com.android.server.biometrics.sensors.fingerprint.PowerPressHandler;
import com.android.server.biometrics.sensors.fingerprint.Udfps;
import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
import java.util.function.Supplier;
-class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps,
+public class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps,
PowerPressHandler {
private static final String TAG = "FingerprintEnrollClient";
@@ -72,12 +71,16 @@
private static boolean shouldVibrateFor(Context context,
FingerprintSensorPropertiesInternal sensorProps) {
- final AccessibilityManager am = context.getSystemService(AccessibilityManager.class);
- final boolean isAccessbilityEnabled = am.isTouchExplorationEnabled();
- return !sensorProps.isAnyUdfpsType() || isAccessbilityEnabled;
+ if (sensorProps != null) {
+ final AccessibilityManager am = context.getSystemService(AccessibilityManager.class);
+ final boolean isAccessbilityEnabled = am.isTouchExplorationEnabled();
+ return !sensorProps.isAnyUdfpsType() || isAccessbilityEnabled;
+ } else {
+ return true;
+ }
}
- FingerprintEnrollClient(@NonNull Context context,
+ public FingerprintEnrollClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token, long requestId,
@NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull byte[] hardwareAuthToken, @NonNull String owner,
@@ -89,8 +92,8 @@
int maxTemplatesPerUser, @FingerprintManager.EnrollReason int enrollReason) {
// UDFPS haptics occur when an image is acquired (instead of when the result is known)
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
- 0 /* timeoutSec */, sensorId, shouldVibrateFor(context, sensorProps), logger,
- biometricContext);
+ 0 /* timeoutSec */, sensorId, shouldVibrateFor(context, sensorProps),
+ logger, biometricContext);
setRequestId(requestId);
mSensorProps = sensorProps;
mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
@@ -136,7 +139,7 @@
acquiredInfo == BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD;
// For UDFPS, notify SysUI that the illumination can be turned off.
// See AcquiredInfo#GOOD and AcquiredInfo#RETRYING_CAPTURE
- if (mSensorProps.isAnyUdfpsType()) {
+ if (mSensorProps != null && mSensorProps.isAnyUdfpsType()) {
if (acquiredGood && mShouldVibrate) {
vibrateSuccess();
}
@@ -162,8 +165,7 @@
@Override
protected boolean hasReachedEnrollmentLimit() {
- return FingerprintUtils.getInstance(getSensorId())
- .getBiometricsForUser(getContext(), getTargetUserId()).size()
+ return mBiometricUtils.getBiometricsForUser(getContext(), getTargetUserId()).size()
>= mMaxTemplatesPerUser;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
index ddae8be..ce693ff 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
@@ -33,10 +33,10 @@
/**
* Fingerprint-specific generateChallenge client for the {@link IFingerprint} AIDL HAL interface.
*/
-class FingerprintGenerateChallengeClient extends GenerateChallengeClient<AidlSession> {
+public class FingerprintGenerateChallengeClient extends GenerateChallengeClient<AidlSession> {
private static final String TAG = "FingerprintGenerateChallengeClient";
- FingerprintGenerateChallengeClient(@NonNull Context context,
+ public FingerprintGenerateChallengeClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon,
@NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter listener,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
index ff9127f..5edc2ca 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
@@ -39,9 +39,10 @@
* Fingerprint-specific internal cleanup client supporting the
* {@link android.hardware.biometrics.fingerprint.IFingerprint} AIDL interface.
*/
-class FingerprintInternalCleanupClient extends InternalCleanupClient<Fingerprint, AidlSession> {
+public class FingerprintInternalCleanupClient
+ extends InternalCleanupClient<Fingerprint, AidlSession> {
- FingerprintInternalCleanupClient(@NonNull Context context,
+ public FingerprintInternalCleanupClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon,
int userId, @NonNull String owner, int sensorId,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index f74b45c..9985b06 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -428,8 +428,8 @@
final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext,
mFingerprintSensors.get(sensorId).getLazySession(), token, id,
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
- opPackageName, FingerprintUtils.getInstance(sensorId), sensorId,
- createLogger(BiometricsProtoEnums.ACTION_ENROLL,
+ opPackageName, FingerprintUtils.getInstance(sensorId),
+ sensorId, createLogger(BiometricsProtoEnums.ACTION_ENROLL,
BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
mBiometricContext,
mFingerprintSensors.get(sensorId).getSensorProperties(),
@@ -496,12 +496,13 @@
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
mAuthenticationStatsCollector),
mBiometricContext, isStrongBiometric,
- mTaskStackListener, mFingerprintSensors.get(sensorId).getLockoutCache(),
+ mTaskStackListener,
mUdfpsOverlayController, mSidefpsController,
allowBackgroundAuthentication,
mFingerprintSensors.get(sensorId).getSensorProperties(), mHandler,
Utils.getCurrentStrength(sensorId),
- SystemClock.elapsedRealtimeClock());
+ SystemClock.elapsedRealtimeClock(),
+ null /* lockoutTracker */);
scheduleForSensor(sensorId, client, new ClientMonitorCallback() {
@Override
@@ -875,6 +876,10 @@
public void simulateVhalFingerDown(int userId, int sensorId) {
Slog.d(getTag(), "Simulate virtual HAL finger down event");
final AidlSession session = mFingerprintSensors.get(sensorId).getSessionForUser(userId);
+ if (session == null) {
+ Slog.e(getTag(), "no existing hal session found - aborting");
+ return;
+ }
final PointerContext pc = new PointerContext();
try {
session.getSession().onPointerDownWithContext(pc);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java
index d559bb1..4f08f6f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java
@@ -37,12 +37,12 @@
* Fingerprint-specific removal client supporting the
* {@link android.hardware.biometrics.fingerprint.IFingerprint} interface.
*/
-class FingerprintRemovalClient extends RemovalClient<Fingerprint, AidlSession> {
+public class FingerprintRemovalClient extends RemovalClient<Fingerprint, AidlSession> {
private static final String TAG = "FingerprintRemovalClient";
private final int[] mBiometricIds;
- FingerprintRemovalClient(@NonNull Context context,
+ public FingerprintRemovalClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token,
@Nullable ClientMonitorCallbackConverter listener, int[] biometricIds, int userId,
@NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
index 7a62034..ec225a6 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
@@ -32,7 +32,6 @@
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ErrorConsumer;
import com.android.server.biometrics.sensors.HalClientMonitor;
-import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
@@ -43,19 +42,20 @@
* Updates the framework's lockout cache and notifies clients such as Keyguard when lockout is
* cleared.
*/
-class FingerprintResetLockoutClient extends HalClientMonitor<AidlSession> implements ErrorConsumer {
+public class FingerprintResetLockoutClient extends HalClientMonitor<AidlSession>
+ implements ErrorConsumer {
private static final String TAG = "FingerprintResetLockoutClient";
private final HardwareAuthToken mHardwareAuthToken;
- private final LockoutCache mLockoutCache;
+ private final LockoutTracker mLockoutCache;
private final LockoutResetDispatcher mLockoutResetDispatcher;
private final int mBiometricStrength;
- FingerprintResetLockoutClient(@NonNull Context context,
+ public FingerprintResetLockoutClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, int userId, String owner, int sensorId,
@NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
- @NonNull byte[] hardwareAuthToken, @NonNull LockoutCache lockoutTracker,
+ @NonNull byte[] hardwareAuthToken, @NonNull LockoutTracker lockoutTracker,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@Authenticators.Types int biometricStrength) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
@@ -107,10 +107,11 @@
* be used instead.
*/
static void resetLocalLockoutStateToNone(int sensorId, int userId,
- @NonNull LockoutCache lockoutTracker,
+ @NonNull LockoutTracker lockoutTracker,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull AuthSessionCoordinator authSessionCoordinator,
@Authenticators.Types int biometricStrength, long requestId) {
+ lockoutTracker.resetFailedAttemptsForUser(true /* clearAttemptCounter */, userId);
lockoutTracker.setLockoutModeForUser(userId, LockoutTracker.LOCKOUT_NONE);
lockoutResetDispatcher.notifyLockoutResetCallbacks(sensorId);
authSessionCoordinator.resetLockoutFor(userId, biometricStrength, requestId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java
index afa62e2..23d8e1e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java
@@ -32,13 +32,13 @@
/**
* Fingerprint-specific revokeChallenge client for the {@link IFingerprint} AIDL HAL interface.
*/
-class FingerprintRevokeChallengeClient extends RevokeChallengeClient<AidlSession> {
+public class FingerprintRevokeChallengeClient extends RevokeChallengeClient<AidlSession> {
- private static final String TAG = "FingerpirntRevokeChallengeClient";
+ private static final String TAG = "FingerprintRevokeChallengeClient";
private final long mChallenge;
- FingerprintRevokeChallengeClient(@NonNull Context context,
+ public FingerprintRevokeChallengeClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token,
int userId, @NonNull String owner, int sensorId,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
@@ -57,7 +57,7 @@
}
}
- void onChallengeRevoked(int sensorId, int userId, long challenge) {
+ void onChallengeRevoked(long challenge) {
final boolean success = challenge == mChallenge;
mCallback.onClientFinished(FingerprintRevokeChallengeClient.this, success);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
index 56b85ce..893cb8f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
@@ -23,13 +23,9 @@
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
-import android.hardware.biometrics.fingerprint.Error;
import android.hardware.biometrics.fingerprint.ISession;
-import android.hardware.biometrics.fingerprint.ISessionCallback;
-import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
-import android.hardware.keymaster.HardwareAuthToken;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -39,34 +35,25 @@
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
-import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.SensorServiceStateProto;
import com.android.server.biometrics.SensorStateProto;
import com.android.server.biometrics.UserStateProto;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
-import com.android.server.biometrics.sensors.AcquisitionClient;
-import com.android.server.biometrics.sensors.AuthSessionCoordinator;
-import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.BiometricStateCallback;
-import com.android.server.biometrics.sensors.EnumerateConsumer;
import com.android.server.biometrics.sensors.ErrorConsumer;
import com.android.server.biometrics.sensors.LockoutCache;
-import com.android.server.biometrics.sensors.LockoutConsumer;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
-import com.android.server.biometrics.sensors.RemovalConsumer;
import com.android.server.biometrics.sensors.StartUserClient;
import com.android.server.biometrics.sensors.StopUserClient;
import com.android.server.biometrics.sensors.UserAwareBiometricScheduler;
import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
@@ -93,348 +80,6 @@
@Nullable AidlSession mCurrentSession;
@NonNull private final Supplier<AidlSession> mLazySession;
- @VisibleForTesting
- public static class HalSessionCallback extends ISessionCallback.Stub {
-
- /**
- * Interface to sends results to the HalSessionCallback's owner.
- */
- public interface Callback {
- /**
- * Invoked when the HAL sends ERROR_HW_UNAVAILABLE.
- */
- void onHardwareUnavailable();
- }
-
- @NonNull
- private final Context mContext;
- @NonNull
- private final Handler mHandler;
- @NonNull
- private final String mTag;
- @NonNull
- private final UserAwareBiometricScheduler mScheduler;
- private final int mSensorId;
- private final int mUserId;
- @NonNull
- private final LockoutCache mLockoutCache;
- @NonNull
- private final LockoutResetDispatcher mLockoutResetDispatcher;
- @NonNull
- private AuthSessionCoordinator mAuthSessionCoordinator;
- @NonNull
- private final Callback mCallback;
-
- HalSessionCallback(@NonNull Context context, @NonNull Handler handler, @NonNull String tag,
- @NonNull UserAwareBiometricScheduler scheduler, int sensorId, int userId,
- @NonNull LockoutCache lockoutTracker,
- @NonNull LockoutResetDispatcher lockoutResetDispatcher,
- @NonNull AuthSessionCoordinator authSessionCoordinator,
- @NonNull Callback callback) {
- mContext = context;
- mHandler = handler;
- mTag = tag;
- mScheduler = scheduler;
- mSensorId = sensorId;
- mUserId = userId;
- mLockoutCache = lockoutTracker;
- mLockoutResetDispatcher = lockoutResetDispatcher;
- mAuthSessionCoordinator = authSessionCoordinator;
- mCallback = callback;
- }
-
- @Override
- public int getInterfaceVersion() {
- return this.VERSION;
- }
-
- @Override
- public String getInterfaceHash() {
- return this.HASH;
- }
-
- @Override
- public void onChallengeGenerated(long challenge) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FingerprintGenerateChallengeClient)) {
- Slog.e(mTag, "onChallengeGenerated for wrong client: "
- + Utils.getClientName(client));
- return;
- }
-
- final FingerprintGenerateChallengeClient generateChallengeClient =
- (FingerprintGenerateChallengeClient) client;
- generateChallengeClient.onChallengeGenerated(mSensorId, mUserId, challenge);
- });
- }
-
- @Override
- public void onChallengeRevoked(long challenge) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FingerprintRevokeChallengeClient)) {
- Slog.e(mTag, "onChallengeRevoked for wrong client: "
- + Utils.getClientName(client));
- return;
- }
-
- final FingerprintRevokeChallengeClient revokeChallengeClient =
- (FingerprintRevokeChallengeClient) client;
- revokeChallengeClient.onChallengeRevoked(mSensorId, mUserId, challenge);
- });
- }
-
- @Override
- public void onAcquired(byte info, int vendorCode) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof AcquisitionClient)) {
- Slog.e(mTag, "onAcquired for non-acquisition client: "
- + Utils.getClientName(client));
- return;
- }
-
- final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client;
- acquisitionClient.onAcquired(AidlConversionUtils.toFrameworkAcquiredInfo(info),
- vendorCode);
- });
- }
-
- @Override
- public void onError(byte error, int vendorCode) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- Slog.d(mTag, "onError"
- + ", client: " + Utils.getClientName(client)
- + ", error: " + error
- + ", vendorCode: " + vendorCode);
- if (!(client instanceof ErrorConsumer)) {
- Slog.e(mTag, "onError for non-error consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final ErrorConsumer errorConsumer = (ErrorConsumer) client;
- errorConsumer.onError(AidlConversionUtils.toFrameworkError(error), vendorCode);
-
- if (error == Error.HW_UNAVAILABLE) {
- mCallback.onHardwareUnavailable();
- }
- });
- }
-
- @Override
- public void onEnrollmentProgress(int enrollmentId, int remaining) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FingerprintEnrollClient)) {
- Slog.e(mTag, "onEnrollmentProgress for non-enroll client: "
- + Utils.getClientName(client));
- return;
- }
-
- final int currentUserId = client.getTargetUserId();
- final CharSequence name = FingerprintUtils.getInstance(mSensorId)
- .getUniqueName(mContext, currentUserId);
- final Fingerprint fingerprint = new Fingerprint(name, enrollmentId, mSensorId);
-
- final FingerprintEnrollClient enrollClient = (FingerprintEnrollClient) client;
- enrollClient.onEnrollResult(fingerprint, remaining);
- });
- }
-
- @Override
- public void onAuthenticationSucceeded(int enrollmentId, HardwareAuthToken hat) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof AuthenticationConsumer)) {
- Slog.e(mTag, "onAuthenticationSucceeded for non-authentication consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final AuthenticationConsumer authenticationConsumer =
- (AuthenticationConsumer) client;
- final Fingerprint fp = new Fingerprint("", enrollmentId, mSensorId);
- final byte[] byteArray = HardwareAuthTokenUtils.toByteArray(hat);
- final ArrayList<Byte> byteList = new ArrayList<>();
- for (byte b : byteArray) {
- byteList.add(b);
- }
-
- authenticationConsumer.onAuthenticated(fp, true /* authenticated */, byteList);
- });
- }
-
- @Override
- public void onAuthenticationFailed() {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof AuthenticationConsumer)) {
- Slog.e(mTag, "onAuthenticationFailed for non-authentication consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final AuthenticationConsumer authenticationConsumer =
- (AuthenticationConsumer) client;
- final Fingerprint fp = new Fingerprint("", 0 /* enrollmentId */, mSensorId);
- authenticationConsumer
- .onAuthenticated(fp, false /* authenticated */, null /* hat */);
- });
- }
-
- @Override
- public void onLockoutTimed(long durationMillis) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof LockoutConsumer)) {
- Slog.e(mTag, "onLockoutTimed for non-lockout consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final LockoutConsumer lockoutConsumer = (LockoutConsumer) client;
- lockoutConsumer.onLockoutTimed(durationMillis);
- });
- }
-
- @Override
- public void onLockoutPermanent() {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof LockoutConsumer)) {
- Slog.e(mTag, "onLockoutPermanent for non-lockout consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final LockoutConsumer lockoutConsumer = (LockoutConsumer) client;
- lockoutConsumer.onLockoutPermanent();
- });
- }
-
- @Override
- public void onLockoutCleared() {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FingerprintResetLockoutClient)) {
- Slog.d(mTag, "onLockoutCleared outside of resetLockout by HAL");
- // Given that onLockoutCleared() can happen at any time, and is not necessarily
- // coming from a specific client, set this to -1 to indicate it wasn't for a
- // specific request.
- FingerprintResetLockoutClient.resetLocalLockoutStateToNone(mSensorId, mUserId,
- mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator,
- Utils.getCurrentStrength(mSensorId), -1 /* requestId */);
- } else {
- Slog.d(mTag, "onLockoutCleared after resetLockout");
- final FingerprintResetLockoutClient resetLockoutClient =
- (FingerprintResetLockoutClient) client;
- resetLockoutClient.onLockoutCleared();
- }
- });
- }
-
- @Override
- public void onInteractionDetected() {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FingerprintDetectClient)) {
- Slog.e(mTag, "onInteractionDetected for non-detect client: "
- + Utils.getClientName(client));
- return;
- }
-
- final FingerprintDetectClient fingerprintDetectClient =
- (FingerprintDetectClient) client;
- fingerprintDetectClient.onInteractionDetected();
- });
- }
-
- @Override
- public void onEnrollmentsEnumerated(int[] enrollmentIds) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof EnumerateConsumer)) {
- Slog.e(mTag, "onEnrollmentsEnumerated for non-enumerate consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final EnumerateConsumer enumerateConsumer =
- (EnumerateConsumer) client;
- if (enrollmentIds.length > 0) {
- for (int i = 0; i < enrollmentIds.length; i++) {
- final Fingerprint fp = new Fingerprint("", enrollmentIds[i], mSensorId);
- enumerateConsumer.onEnumerationResult(fp, enrollmentIds.length - i - 1);
- }
- } else {
- enumerateConsumer.onEnumerationResult(null /* identifier */, 0);
- }
- });
- }
-
- @Override
- public void onEnrollmentsRemoved(int[] enrollmentIds) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof RemovalConsumer)) {
- Slog.e(mTag, "onRemoved for non-removal consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final RemovalConsumer removalConsumer = (RemovalConsumer) client;
- if (enrollmentIds.length > 0) {
- for (int i = 0; i < enrollmentIds.length; i++) {
- final Fingerprint fp = new Fingerprint("", enrollmentIds[i], mSensorId);
- removalConsumer.onRemoved(fp, enrollmentIds.length - i - 1);
- }
- } else {
- removalConsumer.onRemoved(null, 0);
- }
- });
- }
-
- @Override
- public void onAuthenticatorIdRetrieved(long authenticatorId) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FingerprintGetAuthenticatorIdClient)) {
- Slog.e(mTag, "onAuthenticatorIdRetrieved for wrong consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final FingerprintGetAuthenticatorIdClient getAuthenticatorIdClient =
- (FingerprintGetAuthenticatorIdClient) client;
- getAuthenticatorIdClient.onAuthenticatorIdRetrieved(authenticatorId);
- });
- }
-
- @Override
- public void onAuthenticatorIdInvalidated(long newAuthenticatorId) {
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof FingerprintInvalidationClient)) {
- Slog.e(mTag, "onAuthenticatorIdInvalidated for wrong consumer: "
- + Utils.getClientName(client));
- return;
- }
-
- final FingerprintInvalidationClient invalidationClient =
- (FingerprintInvalidationClient) client;
- invalidationClient.onAuthenticatorIdInvalidated(newAuthenticatorId);
- });
- }
-
- @Override
- public void onSessionClosed() {
- mHandler.post(mScheduler::onUserStopped);
- }
- }
-
Sensor(@NonNull String tag, @NonNull FingerprintProvider provider, @NonNull Context context,
@NonNull Handler handler, @NonNull FingerprintSensorPropertiesInternal sensorProperties,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@@ -466,9 +111,9 @@
public StartUserClient<?, ?> getStartUserClient(int newUserId) {
final int sensorId = mSensorProperties.sensorId;
- final HalSessionCallback resultController = new HalSessionCallback(mContext,
- mHandler, mTag, mScheduler, sensorId, newUserId, mLockoutCache,
- lockoutResetDispatcher,
+ final AidlResponseHandler resultController = new AidlResponseHandler(
+ mContext, mScheduler, sensorId, newUserId,
+ mLockoutCache, lockoutResetDispatcher,
biometricContext.getAuthSessionCoordinator(), () -> {
Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE");
mCurrentSession = null;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/AidlToHidlAdapter.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/AidlToHidlAdapter.java
new file mode 100644
index 0000000..b48d232
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/AidlToHidlAdapter.java
@@ -0,0 +1,231 @@
+/*
+ * 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.biometrics.sensors.fingerprint.hidl;
+
+import android.annotation.NonNull;
+import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.biometrics.common.OperationContext;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.biometrics.fingerprint.PointerContext;
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.keymaster.HardwareAuthToken;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
+import com.android.server.biometrics.sensors.fingerprint.aidl.AidlResponseHandler;
+
+import java.util.function.Supplier;
+
+/**
+ * Adapter to convert AIDL-specific interface {@link ISession} methods to HIDL implementation.
+ */
+public class AidlToHidlAdapter implements ISession {
+ private final String TAG = "AidlToHidlAdapter";
+ @VisibleForTesting
+ static final int ENROLL_TIMEOUT_SEC = 60;
+ @NonNull
+ private final Supplier<IBiometricsFingerprint> mSession;
+ private final int mUserId;
+ private HidlToAidlCallbackConverter mHidlToAidlCallbackConverter;
+
+ public AidlToHidlAdapter(Supplier<IBiometricsFingerprint> session, int userId,
+ AidlResponseHandler aidlResponseHandler) {
+ mSession = session;
+ mUserId = userId;
+ setCallback(aidlResponseHandler);
+ }
+
+ private void setCallback(AidlResponseHandler aidlResponseHandler) {
+ mHidlToAidlCallbackConverter = new HidlToAidlCallbackConverter(aidlResponseHandler);
+ try {
+ mSession.get().setNotify(mHidlToAidlCallbackConverter);
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Failed to set callback");
+ }
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return null;
+ }
+
+ @Override
+ public void generateChallenge() throws RemoteException {
+ long challenge = mSession.get().preEnroll();
+ mHidlToAidlCallbackConverter.onChallengeGenerated(challenge);
+ }
+
+ @Override
+ public void revokeChallenge(long challenge) throws RemoteException {
+ mSession.get().postEnroll();
+ mHidlToAidlCallbackConverter.onChallengeRevoked(0L);
+ }
+
+ @Override
+ public ICancellationSignal enroll(HardwareAuthToken hat) throws RemoteException {
+ mSession.get().enroll(HardwareAuthTokenUtils.toByteArray(hat), mUserId,
+ ENROLL_TIMEOUT_SEC);
+ return new Cancellation();
+ }
+
+ @Override
+ public ICancellationSignal authenticate(long operationId) throws RemoteException {
+ mSession.get().authenticate(operationId, mUserId);
+ return new Cancellation();
+ }
+
+ @Override
+ public ICancellationSignal detectInteraction() throws RemoteException {
+ mSession.get().authenticate(0, mUserId);
+ return new Cancellation();
+ }
+
+ @Override
+ public void enumerateEnrollments() throws RemoteException {
+ mSession.get().enumerate();
+ }
+
+ @Override
+ public void removeEnrollments(int[] enrollmentIds) throws RemoteException {
+ if (enrollmentIds.length > 1) {
+ mSession.get().remove(mUserId, 0);
+ } else {
+ mSession.get().remove(mUserId, enrollmentIds[0]);
+ }
+ }
+
+ @Override
+ public void onPointerDown(int pointerId, int x, int y, float minor, float major)
+ throws RemoteException {
+ UdfpsHelper.onFingerDown(mSession.get(), x, y, minor, major);
+ }
+
+ @Override
+ public void onPointerUp(int pointerId) throws RemoteException {
+ UdfpsHelper.onFingerUp(mSession.get());
+ }
+
+ @Override
+ public void getAuthenticatorId() throws RemoteException {
+ //Unsupported in HIDL
+ }
+
+ @Override
+ public void invalidateAuthenticatorId() throws RemoteException {
+ //Unsupported in HIDL
+ }
+
+ @Override
+ public void resetLockout(HardwareAuthToken hat) throws RemoteException {
+ mHidlToAidlCallbackConverter.onResetLockout();
+ }
+
+ @Override
+ public void close() throws RemoteException {
+ //Unsupported in HIDL
+ }
+
+ @Override
+ public void onUiReady() throws RemoteException {
+ //Unsupported in HIDL
+ }
+
+ @Override
+ public ICancellationSignal authenticateWithContext(long operationId, OperationContext context)
+ throws RemoteException {
+ //Unsupported in HIDL
+ return null;
+ }
+
+ @Override
+ public ICancellationSignal enrollWithContext(HardwareAuthToken hat, OperationContext context)
+ throws RemoteException {
+ //Unsupported in HIDL
+ return null;
+ }
+
+ @Override
+ public ICancellationSignal detectInteractionWithContext(OperationContext context)
+ throws RemoteException {
+ //Unsupported in HIDL
+ return null;
+ }
+
+ @Override
+ public void onPointerDownWithContext(PointerContext context) throws RemoteException {
+ //Unsupported in HIDL
+ }
+
+ @Override
+ public void onPointerUpWithContext(PointerContext context) throws RemoteException {
+ //Unsupported in HIDL
+ }
+
+ @Override
+ public void onContextChanged(OperationContext context) throws RemoteException {
+ //Unsupported in HIDL
+ }
+
+ @Override
+ public void onPointerCancelWithContext(PointerContext context) throws RemoteException {
+ //Unsupported in HIDL
+ }
+
+ @Override
+ public void setIgnoreDisplayTouches(boolean shouldIgnore) throws RemoteException {
+ //Unsupported in HIDL
+ }
+
+ @Override
+ public int getInterfaceVersion() throws RemoteException {
+ //Unsupported in HIDL
+ return 0;
+ }
+
+ @Override
+ public String getInterfaceHash() throws RemoteException {
+ //Unsupported in HIDL
+ return null;
+ }
+
+ private class Cancellation extends ICancellationSignal.Stub {
+
+ Cancellation() {}
+ @Override
+ public void cancel() throws RemoteException {
+ try {
+ mSession.get().cancel();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when requesting cancel", e);
+ }
+ }
+
+ @Override
+ public int getInterfaceVersion() throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public String getInterfaceHash() throws RemoteException {
+ return null;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index a655f360..8bfa560 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -54,6 +54,7 @@
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.AuthenticationStatsBroadcastReceiver;
import com.android.server.biometrics.AuthenticationStatsCollector;
+import com.android.server.biometrics.Flags;
import com.android.server.biometrics.SensorServiceStateProto;
import com.android.server.biometrics.SensorStateProto;
import com.android.server.biometrics.UserStateProto;
@@ -64,6 +65,7 @@
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AcquisitionClient;
+import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.BaseClientMonitor;
@@ -74,6 +76,7 @@
import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
import com.android.server.biometrics.sensors.EnumerateConsumer;
import com.android.server.biometrics.sensors.ErrorConsumer;
+import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.PerformanceTracker;
@@ -82,6 +85,8 @@
import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
import com.android.server.biometrics.sensors.fingerprint.ServiceProvider;
import com.android.server.biometrics.sensors.fingerprint.Udfps;
+import com.android.server.biometrics.sensors.fingerprint.aidl.AidlResponseHandler;
+import com.android.server.biometrics.sensors.fingerprint.aidl.AidlSession;
import org.json.JSONArray;
import org.json.JSONException;
@@ -132,6 +137,7 @@
private final boolean mIsUdfps;
private final int mSensorId;
private final boolean mIsPowerbuttonFps;
+ private AidlSession mSession;
private final class BiometricTaskStackListener extends TaskStackListener {
@Override
@@ -413,6 +419,20 @@
});
}
+ synchronized AidlSession getSession() {
+ if (mDaemon != null && mSession != null) {
+ return mSession;
+ } else {
+ return mSession = new AidlSession(this::getDaemon,
+ mCurrentUserId, new AidlResponseHandler(mContext,
+ mScheduler, mSensorId, mCurrentUserId, new LockoutCache(),
+ mLockoutResetDispatcher, new AuthSessionCoordinator(), () -> {
+ mDaemon = null;
+ mCurrentUserId = UserHandle.USER_NULL;
+ }));
+ }
+ }
+
@VisibleForTesting
synchronized IBiometricsFingerprint getDaemon() {
if (mTestHalEnabled) {
@@ -518,6 +538,10 @@
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
boolean success) {
if (success) {
+ if (mCurrentUserId != targetUserId) {
+ // Create new session with updated user ID
+ mSession = null;
+ }
mCurrentUserId = targetUserId;
} else {
Slog.w(TAG, "Failed to change user, still: " + mCurrentUserId);
@@ -554,47 +578,116 @@
// Fingerprint2.1 keeps track of lockout in the framework. Let's just do it on the handler
// thread.
mHandler.post(() -> {
- final FingerprintResetLockoutClient client = new FingerprintResetLockoutClient(mContext,
- userId, mContext.getOpPackageName(), sensorId,
- createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN,
- mAuthenticationStatsCollector),
- mBiometricContext, mLockoutTracker);
- mScheduler.scheduleClientMonitor(client);
+ if (Flags.deHidl()) {
+ scheduleResetLockoutAidl(sensorId, userId, hardwareAuthToken);
+ } else {
+ scheduleResetLockoutHidl(sensorId, userId);
+ }
});
}
+ private void scheduleResetLockoutAidl(int sensorId, int userId,
+ @Nullable byte[] hardwareAuthToken) {
+ final com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintResetLockoutClient client =
+ new com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintResetLockoutClient(
+ mContext, this::getSession, userId, mContext.getOpPackageName(),
+ sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
+ mBiometricContext, hardwareAuthToken, mLockoutTracker,
+ mLockoutResetDispatcher,
+ Utils.getCurrentStrength(sensorId));
+ mScheduler.scheduleClientMonitor(client);
+ }
+
+ private void scheduleResetLockoutHidl(int sensorId, int userId) {
+ final FingerprintResetLockoutClient client = new FingerprintResetLockoutClient(mContext,
+ userId, mContext.getOpPackageName(), sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
+ mBiometricContext, mLockoutTracker);
+ mScheduler.scheduleClientMonitor(client);
+ }
+
@Override
public void scheduleGenerateChallenge(int sensorId, int userId, @NonNull IBinder token,
@NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName) {
mHandler.post(() -> {
- final FingerprintGenerateChallengeClient client =
- new FingerprintGenerateChallengeClient(mContext, mLazyDaemon, token,
- new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
- mSensorProperties.sensorId,
- createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN,
- mAuthenticationStatsCollector),
- mBiometricContext);
- mScheduler.scheduleClientMonitor(client);
+ if (Flags.deHidl()) {
+ scheduleGenerateChallengeAidl(userId, token, receiver, opPackageName);
+ } else {
+ scheduleGenerateChallengeHidl(userId, token, receiver, opPackageName);
+ }
});
}
+ private void scheduleGenerateChallengeAidl(int userId, @NonNull IBinder token,
+ @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName) {
+ final com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintGenerateChallengeClient client =
+ new com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintGenerateChallengeClient(
+ mContext, this::getSession, token,
+ new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
+ mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
+ mBiometricContext);
+ mScheduler.scheduleClientMonitor(client);
+ }
+
+ private void scheduleGenerateChallengeHidl(int userId, @NonNull IBinder token,
+ @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName) {
+ final FingerprintGenerateChallengeClient client =
+ new FingerprintGenerateChallengeClient(mContext, mLazyDaemon, token,
+ new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
+ mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
+ mBiometricContext);
+ mScheduler.scheduleClientMonitor(client);
+ }
+
@Override
public void scheduleRevokeChallenge(int sensorId, int userId, @NonNull IBinder token,
@NonNull String opPackageName, long challenge) {
mHandler.post(() -> {
- final FingerprintRevokeChallengeClient client = new FingerprintRevokeChallengeClient(
- mContext, mLazyDaemon, token, userId, opPackageName,
- mSensorProperties.sensorId,
- createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
- BiometricsProtoEnums.CLIENT_UNKNOWN,
- mAuthenticationStatsCollector),
- mBiometricContext);
- mScheduler.scheduleClientMonitor(client);
+ if (Flags.deHidl()) {
+ scheduleRevokeChallengeAidl(userId, token, opPackageName);
+ } else {
+ scheduleRevokeChallengeHidl(userId, token, opPackageName);
+ }
});
}
+ private void scheduleRevokeChallengeAidl(int userId, @NonNull IBinder token,
+ @NonNull String opPackageName) {
+ final com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintRevokeChallengeClient client =
+ new com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintRevokeChallengeClient(
+ mContext, this::getSession,
+ token, userId, opPackageName,
+ mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
+ mBiometricContext, 0L);
+ mScheduler.scheduleClientMonitor(client);
+ }
+
+ private void scheduleRevokeChallengeHidl(int userId, @NonNull IBinder token,
+ @NonNull String opPackageName) {
+ final FingerprintRevokeChallengeClient client = new FingerprintRevokeChallengeClient(
+ mContext, mLazyDaemon, token, userId, opPackageName,
+ mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
+ mBiometricContext);
+ mScheduler.scheduleClientMonitor(client);
+ }
+
@Override
public long scheduleEnroll(int sensorId, @NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId,
@@ -604,40 +697,98 @@
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
- final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext,
- mLazyDaemon, token, id, new ClientMonitorCallbackConverter(receiver),
- userId, hardwareAuthToken, opPackageName,
- FingerprintUtils.getLegacyInstance(mSensorId), ENROLL_TIMEOUT_SEC,
- mSensorProperties.sensorId,
- createLogger(BiometricsProtoEnums.ACTION_ENROLL,
- BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext, mUdfpsOverlayController, mSidefpsController, enrollReason);
- mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
- @Override
- public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
- mBiometricStateCallback.onClientStarted(clientMonitor);
- }
-
- @Override
- public void onBiometricAction(int action) {
- mBiometricStateCallback.onBiometricAction(action);
- }
-
- @Override
- public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
- boolean success) {
- mBiometricStateCallback.onClientFinished(clientMonitor, success);
- if (success) {
- // Update authenticatorIds
- scheduleUpdateActiveUserWithoutHandler(clientMonitor.getTargetUserId(),
- true /* force */);
- }
- }
- });
+ if (Flags.deHidl()) {
+ scheduleEnrollAidl(token, hardwareAuthToken, userId, receiver,
+ opPackageName, enrollReason, id);
+ } else {
+ scheduleEnrollHidl(token, hardwareAuthToken, userId, receiver,
+ opPackageName, enrollReason, id);
+ }
});
return id;
}
+ private void scheduleEnrollHidl(@NonNull IBinder token,
+ @NonNull byte[] hardwareAuthToken, int userId,
+ @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
+ @FingerprintManager.EnrollReason int enrollReason, long id) {
+ final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext,
+ mLazyDaemon, token, id, new ClientMonitorCallbackConverter(receiver),
+ userId, hardwareAuthToken, opPackageName,
+ FingerprintUtils.getLegacyInstance(mSensorId), ENROLL_TIMEOUT_SEC,
+ mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_ENROLL,
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
+ mBiometricContext, mUdfpsOverlayController, mSidefpsController, enrollReason);
+ mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
+ @Override
+ public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
+ mBiometricStateCallback.onClientStarted(clientMonitor);
+ }
+
+ @Override
+ public void onBiometricAction(int action) {
+ mBiometricStateCallback.onBiometricAction(action);
+ }
+
+ @Override
+ public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
+ boolean success) {
+ mBiometricStateCallback.onClientFinished(clientMonitor, success);
+ if (success) {
+ // Update authenticatorIds
+ scheduleUpdateActiveUserWithoutHandler(clientMonitor.getTargetUserId(),
+ true /* force */);
+ }
+ }
+ });
+ }
+
+ private void scheduleEnrollAidl(@NonNull IBinder token,
+ @NonNull byte[] hardwareAuthToken, int userId,
+ @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
+ @FingerprintManager.EnrollReason int enrollReason, long id) {
+ final com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintEnrollClient
+ client =
+ new com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintEnrollClient(
+ mContext,
+ this::getSession, token, id,
+ new ClientMonitorCallbackConverter(receiver),
+ userId, hardwareAuthToken, opPackageName,
+ FingerprintUtils.getLegacyInstance(mSensorId),
+ mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_ENROLL,
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
+ mBiometricContext, null /* sensorProps */,
+ mUdfpsOverlayController, mSidefpsController,
+ mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser),
+ enrollReason);
+ mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
+ @Override
+ public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
+ mBiometricStateCallback.onClientStarted(clientMonitor);
+ }
+
+ @Override
+ public void onBiometricAction(int action) {
+ mBiometricStateCallback.onBiometricAction(action);
+ }
+
+ @Override
+ public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
+ boolean success) {
+ mBiometricStateCallback.onClientFinished(clientMonitor, success);
+ if (success) {
+ // Update authenticatorIds
+ scheduleUpdateActiveUserWithoutHandler(clientMonitor.getTargetUserId(),
+ true /* force */);
+ }
+ }
+ });
+ }
+
@Override
public void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId) {
mHandler.post(() -> mScheduler.cancelEnrollment(token, requestId));
@@ -653,17 +804,46 @@
scheduleUpdateActiveUserWithoutHandler(options.getUserId());
final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorProperties.sensorId);
- final FingerprintDetectClient client = new FingerprintDetectClient(mContext,
- mLazyDaemon, token, id, listener, options,
- createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
- mAuthenticationStatsCollector),
- mBiometricContext, mUdfpsOverlayController, isStrongBiometric);
- mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
+
+ if (Flags.deHidl()) {
+ scheduleFingerDetectAidl(token, listener, options, statsClient, id,
+ isStrongBiometric);
+ } else {
+ scheduleFingerDetectHidl(token, listener, options, statsClient, id,
+ isStrongBiometric);
+ }
});
return id;
}
+ private void scheduleFingerDetectHidl(@NonNull IBinder token,
+ @NonNull ClientMonitorCallbackConverter listener,
+ @NonNull FingerprintAuthenticateOptions options,
+ int statsClient, long id, boolean isStrongBiometric) {
+ final FingerprintDetectClient client = new FingerprintDetectClient(mContext,
+ mLazyDaemon, token, id, listener, options,
+ createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
+ mAuthenticationStatsCollector),
+ mBiometricContext, mUdfpsOverlayController, isStrongBiometric);
+ mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
+ }
+
+ private void scheduleFingerDetectAidl(@NonNull IBinder token,
+ @NonNull ClientMonitorCallbackConverter listener,
+ @NonNull FingerprintAuthenticateOptions options,
+ int statsClient, long id, boolean isStrongBiometric) {
+ final com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintDetectClient
+ client =
+ new com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintDetectClient(
+ mContext,
+ this::getSession, token, id, listener, options,
+ createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
+ mAuthenticationStatsCollector),
+ mBiometricContext, mUdfpsOverlayController, isStrongBiometric);
+ mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
+ }
+
@Override
public void scheduleAuthenticate(@NonNull IBinder token, long operationId,
int cookie, @NonNull ClientMonitorCallbackConverter listener,
@@ -674,20 +854,55 @@
scheduleUpdateActiveUserWithoutHandler(options.getUserId());
final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorProperties.sensorId);
- final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient(
- mContext, mLazyDaemon, token, requestId, listener, operationId,
- restricted, options, cookie, false /* requireConfirmation */,
- createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
- mAuthenticationStatsCollector),
- mBiometricContext, isStrongBiometric,
- mTaskStackListener, mLockoutTracker,
- mUdfpsOverlayController, mSidefpsController,
- allowBackgroundAuthentication, mSensorProperties,
- Utils.getCurrentStrength(mSensorId));
- mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
+
+ if (Flags.deHidl()) {
+ scheduleAuthenticateAidl(token, operationId, cookie, listener, options, requestId,
+ restricted, statsClient, allowBackgroundAuthentication, isStrongBiometric);
+ } else {
+ scheduleAuthenticateHidl(token, operationId, cookie, listener, options, requestId,
+ restricted, statsClient, allowBackgroundAuthentication, isStrongBiometric);
+ }
});
}
+ private void scheduleAuthenticateAidl(@NonNull IBinder token, long operationId,
+ int cookie, @NonNull ClientMonitorCallbackConverter listener,
+ @NonNull FingerprintAuthenticateOptions options,
+ long requestId, boolean restricted, int statsClient,
+ boolean allowBackgroundAuthentication, boolean isStrongBiometric) {
+ final com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintAuthenticationClient
+ client =
+ new com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintAuthenticationClient(
+ mContext, this::getSession, token, requestId, listener, operationId,
+ restricted, options, cookie, false /* requireConfirmation */,
+ createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
+ mAuthenticationStatsCollector),
+ mBiometricContext, isStrongBiometric,
+ mTaskStackListener,
+ mUdfpsOverlayController, mSidefpsController,
+ allowBackgroundAuthentication, mSensorProperties, mHandler,
+ Utils.getCurrentStrength(mSensorId), null /* clock */, mLockoutTracker);
+ mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
+ }
+
+ private void scheduleAuthenticateHidl(@NonNull IBinder token, long operationId,
+ int cookie, @NonNull ClientMonitorCallbackConverter listener,
+ @NonNull FingerprintAuthenticateOptions options,
+ long requestId, boolean restricted, int statsClient,
+ boolean allowBackgroundAuthentication, boolean isStrongBiometric) {
+ final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient(
+ mContext, mLazyDaemon, token, requestId, listener, operationId,
+ restricted, options, cookie, false /* requireConfirmation */,
+ createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient,
+ mAuthenticationStatsCollector),
+ mBiometricContext, isStrongBiometric,
+ mTaskStackListener, mLockoutTracker,
+ mUdfpsOverlayController, mSidefpsController,
+ allowBackgroundAuthentication, mSensorProperties,
+ Utils.getCurrentStrength(mSensorId));
+ mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
+ }
+
@Override
public long scheduleAuthenticate(@NonNull IBinder token, long operationId,
int cookie, @NonNull ClientMonitorCallbackConverter listener,
@@ -719,17 +934,41 @@
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
- final FingerprintRemovalClient client = new FingerprintRemovalClient(mContext,
- mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), fingerId,
- userId, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId),
- mSensorProperties.sensorId,
- createLogger(BiometricsProtoEnums.ACTION_REMOVE,
- BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext, mAuthenticatorIds);
- mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
+ if (Flags.deHidl()) {
+ scheduleRemoveAidl(token, receiver, fingerId, userId, opPackageName);
+ } else {
+ scheduleRemoveHidl(token, receiver, fingerId, userId, opPackageName);
+ }
});
}
+ private void scheduleRemoveHidl(@NonNull IBinder token,
+ @NonNull IFingerprintServiceReceiver receiver, int fingerId, int userId,
+ @NonNull String opPackageName) {
+ final FingerprintRemovalClient client = new FingerprintRemovalClient(mContext,
+ mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), fingerId,
+ userId, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId),
+ mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_REMOVE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
+ mBiometricContext, mAuthenticatorIds);
+ mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
+ }
+
+ private void scheduleRemoveAidl(@NonNull IBinder token,
+ @NonNull IFingerprintServiceReceiver receiver, int fingerId, int userId,
+ @NonNull String opPackageName) {
+ final com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintRemovalClient client =
+ new com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintRemovalClient(
+ mContext, this::getSession, token,
+ new ClientMonitorCallbackConverter(receiver), new int[]{fingerId}, userId,
+ opPackageName, FingerprintUtils.getLegacyInstance(mSensorId), mSensorId,
+ createLogger(BiometricsProtoEnums.ACTION_REMOVE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
+ mBiometricContext, mAuthenticatorIds);
+ mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
+ }
+
@Override
public void scheduleRemoveAll(int sensorId, @NonNull IBinder token,
@NonNull IFingerprintServiceReceiver receiver, int userId,
@@ -738,15 +977,11 @@
scheduleUpdateActiveUserWithoutHandler(userId);
// For IBiometricsFingerprint@2.1, remove(0) means remove all enrollments
- final FingerprintRemovalClient client = new FingerprintRemovalClient(mContext,
- mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver),
- 0 /* fingerprintId */, userId, opPackageName,
- FingerprintUtils.getLegacyInstance(mSensorId),
- mSensorProperties.sensorId,
- createLogger(BiometricsProtoEnums.ACTION_REMOVE,
- BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext, mAuthenticatorIds);
- mScheduler.scheduleClientMonitor(client, mBiometricStateCallback);
+ if (Flags.deHidl()) {
+ scheduleRemoveAidl(token, receiver, 0 /* fingerId */, userId, opPackageName);
+ } else {
+ scheduleRemoveHidl(token, receiver, 0 /* fingerId */, userId, opPackageName);
+ }
});
}
@@ -755,17 +990,41 @@
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
- final FingerprintInternalCleanupClient client = new FingerprintInternalCleanupClient(
- mContext, mLazyDaemon, userId, mContext.getOpPackageName(),
- mSensorProperties.sensorId,
- createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
- BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext,
- FingerprintUtils.getLegacyInstance(mSensorId), mAuthenticatorIds);
- mScheduler.scheduleClientMonitor(client, callback);
+ if (Flags.deHidl()) {
+ scheduleInternalCleanupAidl(userId, callback);
+ } else {
+ scheduleInternalCleanupHidl(userId, callback);
+ }
});
}
+ private void scheduleInternalCleanupHidl(int userId,
+ @Nullable ClientMonitorCallback callback) {
+ final FingerprintInternalCleanupClient client = new FingerprintInternalCleanupClient(
+ mContext, mLazyDaemon, userId, mContext.getOpPackageName(),
+ mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
+ mBiometricContext,
+ FingerprintUtils.getLegacyInstance(mSensorId), mAuthenticatorIds);
+ mScheduler.scheduleClientMonitor(client, callback);
+ }
+
+ private void scheduleInternalCleanupAidl(int userId,
+ @Nullable ClientMonitorCallback callback) {
+ final com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintInternalCleanupClient
+ client =
+ new com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintInternalCleanupClient(
+ mContext, this::getSession, userId, mContext.getOpPackageName(),
+ mSensorProperties.sensorId,
+ createLogger(BiometricsProtoEnums.ACTION_ENUMERATE,
+ BiometricsProtoEnums.CLIENT_UNKNOWN,
+ mAuthenticationStatsCollector),
+ mBiometricContext,
+ FingerprintUtils.getLegacyInstance(mSensorId), mAuthenticatorIds);
+ mScheduler.scheduleClientMonitor(client, callback);
+ }
+
@Override
public void scheduleInternalCleanup(int sensorId, int userId,
@Nullable ClientMonitorCallback callback) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlCallbackConverter.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlCallbackConverter.java
new file mode 100644
index 0000000..c3e5cbe
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlCallbackConverter.java
@@ -0,0 +1,95 @@
+/*
+ * 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.biometrics.sensors.fingerprint.hidl;
+
+import android.annotation.NonNull;
+import android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprintClientCallback;
+
+import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.sensors.fingerprint.aidl.AidlResponseHandler;
+
+import java.util.ArrayList;
+
+/**
+ * Convert HIDL-specific callback interface {@link IBiometricsFingerprintClientCallback} to AIDL
+ * response handler.
+ */
+public class HidlToAidlCallbackConverter extends IBiometricsFingerprintClientCallback.Stub {
+
+ final AidlResponseHandler mAidlResponseHandler;
+
+ public HidlToAidlCallbackConverter(@NonNull AidlResponseHandler aidlResponseHandler) {
+ mAidlResponseHandler = aidlResponseHandler;
+ }
+
+ @Override
+ public void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
+ mAidlResponseHandler.onEnrollmentProgress(fingerId, remaining);
+ }
+
+ @Override
+ public void onAcquired(long deviceId, int acquiredInfo, int vendorCode) {
+ onAcquired_2_2(deviceId, acquiredInfo, vendorCode);
+ }
+
+ @Override
+ public void onAcquired_2_2(long deviceId, int acquiredInfo, int vendorCode) {
+ mAidlResponseHandler.onAcquired(acquiredInfo, vendorCode);
+ }
+
+ @Override
+ public void onAuthenticated(long deviceId, int fingerId, int groupId,
+ ArrayList<Byte> token) {
+ if (fingerId != 0) {
+ byte[] hardwareAuthToken = new byte[token.size()];
+ for (int i = 0; i < token.size(); i++) {
+ hardwareAuthToken[i] = token.get(i);
+ }
+ mAidlResponseHandler.onAuthenticationSucceeded(fingerId,
+ HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken));
+ } else {
+ mAidlResponseHandler.onAuthenticationFailed();
+ }
+ }
+
+ @Override
+ public void onError(long deviceId, int error, int vendorCode) {
+ mAidlResponseHandler.onError(error, vendorCode);
+ }
+
+ @Override
+ public void onRemoved(long deviceId, int fingerId, int groupId, int remaining) {
+ mAidlResponseHandler.onEnrollmentsRemoved(new int[]{fingerId});
+ }
+
+ @Override
+ public void onEnumerate(long deviceId, int fingerId, int groupId, int remaining) {
+ mAidlResponseHandler.onEnrollmentsEnumerated(new int[]{fingerId});
+ }
+
+ void onChallengeGenerated(long challenge) {
+ mAidlResponseHandler.onChallengeGenerated(challenge);
+ }
+
+ void onChallengeRevoked(long challenge) {
+ mAidlResponseHandler.onChallengeRevoked(challenge);
+ }
+
+ void onResetLockout() {
+ mAidlResponseHandler.onLockoutCleared();
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java
index 36d56c8..0730c67 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java
@@ -89,7 +89,8 @@
// Attempt counter should only be cleared when Keyguard goes away or when
// a biometric is successfully authenticated. Lockout should eventually be done below the HAL.
// See AuthenticationClient#shouldFrameworkHandleLockout().
- void resetFailedAttemptsForUser(boolean clearAttemptCounter, int userId) {
+ @Override
+ public void resetFailedAttemptsForUser(boolean clearAttemptCounter, int userId) {
if (getLockoutModeForUser(userId) != LOCKOUT_NONE) {
Slog.v(TAG, "Reset biometric lockout for user: " + userId
+ ", clearAttemptCounter: " + clearAttemptCounter);
@@ -104,7 +105,8 @@
mLockoutResetCallback.onLockoutReset(userId);
}
- void addFailedAttemptForUser(int userId) {
+ @Override
+ public void addFailedAttemptForUser(int userId) {
mFailedAttempts.put(userId, mFailedAttempts.get(userId, 0) + 1);
mTimedLockoutCleared.put(userId, false);
@@ -114,7 +116,8 @@
}
@Override
- public @LockoutMode int getLockoutModeForUser(int userId) {
+ @LockoutMode
+ public int getLockoutModeForUser(int userId) {
final int failedAttempts = mFailedAttempts.get(userId, 0);
if (failedAttempts >= MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT) {
return LOCKOUT_PERMANENT;
@@ -126,6 +129,19 @@
return LOCKOUT_NONE;
}
+ /**
+ * Clears lockout for Fingerprint HIDL HAL
+ */
+ @Override
+ public void setLockoutModeForUser(int userId, int mode) {
+ mFailedAttempts.put(userId, 0);
+ mTimedLockoutCleared.put(userId, true);
+ // If we're asked to reset failed attempts externally (i.e. from Keyguard),
+ // the alarm might still be pending; remove it.
+ cancelLockoutResetForUser(userId);
+ mLockoutResetCallback.onLockoutReset(userId);
+ }
+
private void cancelLockoutResetForUser(int userId) {
mAlarmManager.cancel(getLockoutResetIntentForUser(userId));
}
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index 9805fd3..333f62a 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -1845,7 +1845,7 @@
private void parseListenForTickles(TypedXmlPullParser parser) {
int userId = 0;
try {
- parser.getAttributeInt(null, XML_ATTR_USER);
+ userId = parser.getAttributeInt(null, XML_ATTR_USER);
} catch (XmlPullParserException e) {
Slog.e(TAG, "error parsing the user for listen-for-tickles", e);
}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 9e92c8d..cfbe0c6 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -60,6 +60,9 @@
import com.android.server.display.config.NitsMap;
import com.android.server.display.config.NonNegativeFloatToFloatPoint;
import com.android.server.display.config.Point;
+import com.android.server.display.config.PowerThrottlingConfig;
+import com.android.server.display.config.PowerThrottlingMap;
+import com.android.server.display.config.PowerThrottlingPoint;
import com.android.server.display.config.PredefinedBrightnessLimitNames;
import com.android.server.display.config.RefreshRateConfigs;
import com.android.server.display.config.RefreshRateRange;
@@ -139,6 +142,30 @@
* </screenBrightnessMap>
*
* <screenBrightnessDefault>0.65</screenBrightnessDefault>
+ * <powerThrottlingConfig>
+ * <brightnessLowestCapAllowed>0.1</brightnessLowestCapAllowed>
+ * <pollingWindowMillis>15</pollingWindowMillis>
+ * <powerThrottlingMap>
+ * <powerThrottlingPoint>
+ * <thermalStatus>severe</thermalStatus>
+ * <powerQuotaMilliWatts>200.6</powerQuotaMilliWatts>
+ * </powerThrottlingPoint>
+ * <powerThrottlingPoint>
+ * <thermalStatus>critical</thermalStatus>
+ * <powerQuotaMilliWatts>300</powerQuotaMilliWatts>
+ * </powerThrottlingPoint>
+ * </powerThrottlingMap>
+ * <powerThrottlingMap id="id_2"> // optional attribute, leave blank for default
+ * <powerThrottlingPoint>
+ * <thermalStatus>moderate</thermalStatus>
+ * <powerQuotaMilliWatts>400</powerQuotaMilliWatts>
+ * </powerThrottlingPoint>
+ * <powerThrottlingPoint>
+ * <thermalStatus>severe</thermalStatus>
+ * <powerQuotaMilliWatts>250</powerQuotaMilliWatts>
+ * </powerThrottlingPoint>
+ * </powerThrottlingMap>
+ * </powerThrottlingConfig>
*
* <thermalThrottling>
* <brightnessThrottlingMap>
@@ -669,6 +696,8 @@
private List<String> mQuirks;
private boolean mIsHighBrightnessModeEnabled = false;
private HighBrightnessModeData mHbmData;
+ @Nullable
+ private PowerThrottlingConfigData mPowerThrottlingConfigData;
private DensityMapping mDensityMapping;
private String mLoadedFrom = null;
private Spline mSdrToHdrRatioSpline;
@@ -781,6 +810,9 @@
private final HashMap<String, ThermalBrightnessThrottlingData>
mThermalBrightnessThrottlingDataMapByThrottlingId = new HashMap<>();
+ private final HashMap<String, PowerThrottlingData>
+ mPowerThrottlingDataMapByThrottlingId = new HashMap<>();
+
private final Map<String, SparseArray<SurfaceControl.RefreshRateRange>>
mRefreshRateThrottlingMap = new HashMap<>();
@@ -1458,6 +1490,14 @@
return hbmData;
}
+ /**
+ * @return Power throttling configuration data for the display.
+ */
+ @Nullable
+ public PowerThrottlingConfigData getPowerThrottlingConfigData() {
+ return mPowerThrottlingConfigData;
+ }
+
@NonNull
public Map<BrightnessLimitMapType, Map<Float, Float>> getLuxThrottlingData() {
return mLuxThrottlingData;
@@ -1491,6 +1531,14 @@
}
/**
+ * @return power throttling configuration data for this display, for each throttling id.
+ **/
+ public HashMap<String, PowerThrottlingData>
+ getPowerThrottlingDataMapByThrottlingId() {
+ return mPowerThrottlingDataMapByThrottlingId;
+ }
+
+ /**
* @return Auto brightness darkening light debounce
*/
public long getAutoBrightnessDarkeningLightDebounce() {
@@ -1702,6 +1750,9 @@
+ ", mThermalBrightnessThrottlingDataMapByThrottlingId="
+ mThermalBrightnessThrottlingDataMapByThrottlingId
+ "\n"
+ + ", mPowerThrottlingDataMapByThrottlingId="
+ + mPowerThrottlingDataMapByThrottlingId
+ + "\n"
+ "mBrightnessRampFastDecrease=" + mBrightnessRampFastDecrease
+ ", mBrightnessRampFastIncrease=" + mBrightnessRampFastIncrease
+ ", mBrightnessRampSlowDecrease=" + mBrightnessRampSlowDecrease
@@ -1853,6 +1904,7 @@
loadBrightnessConstraintsFromConfigXml();
loadBrightnessMap(config);
loadThermalThrottlingConfig(config);
+ loadPowerThrottlingConfigData(config);
loadHighBrightnessModeData(config);
loadLuxThrottling(config);
loadQuirks(config);
@@ -2171,6 +2223,59 @@
}
}
+ private boolean loadPowerThrottlingMaps(PowerThrottlingConfig throttlingConfig) {
+ final List<PowerThrottlingMap> maps = throttlingConfig.getPowerThrottlingMap();
+ if (maps == null || maps.isEmpty()) {
+ Slog.i(TAG, "No power throttling map found");
+ return false;
+ }
+
+ for (PowerThrottlingMap map : maps) {
+ final List<PowerThrottlingPoint> points = map.getPowerThrottlingPoint();
+ // At least 1 point is guaranteed by the display device config schema
+ List<PowerThrottlingData.ThrottlingLevel> throttlingLevels =
+ new ArrayList<>(points.size());
+
+ boolean badConfig = false;
+ for (PowerThrottlingPoint point : points) {
+ ThermalStatus status = point.getThermalStatus();
+ if (!thermalStatusIsValid(status)) {
+ badConfig = true;
+ break;
+ }
+
+ throttlingLevels.add(new PowerThrottlingData.ThrottlingLevel(
+ convertThermalStatus(status),
+ point.getPowerQuotaMilliWatts().floatValue()));
+ }
+
+ if (!badConfig) {
+ String id = map.getId() == null ? DEFAULT_ID : map.getId();
+ if (mPowerThrottlingDataMapByThrottlingId.containsKey(id)) {
+ throw new RuntimeException("Power throttling data with ID " + id
+ + " already exists");
+ }
+ mPowerThrottlingDataMapByThrottlingId.put(id,
+ PowerThrottlingData.create(throttlingLevels));
+ }
+ }
+ return true;
+ }
+
+ private void loadPowerThrottlingConfigData(DisplayConfiguration config) {
+ final PowerThrottlingConfig powerThrottlingCfg = config.getPowerThrottlingConfig();
+ if (powerThrottlingCfg == null) {
+ return;
+ }
+ if (!loadPowerThrottlingMaps(powerThrottlingCfg)) {
+ return;
+ }
+ float lowestBrightnessCap = powerThrottlingCfg.getBrightnessLowestCapAllowed().floatValue();
+ int pollingWindowMillis = powerThrottlingCfg.getPollingWindowMillis().intValue();
+ mPowerThrottlingConfigData = new PowerThrottlingConfigData(lowestBrightnessCap,
+ pollingWindowMillis);
+ }
+
private void loadRefreshRateSetting(DisplayConfiguration config) {
final RefreshRateConfigs refreshRateConfigs =
(config == null) ? null : config.getRefreshRate();
@@ -3379,6 +3484,148 @@
}
/**
+ * Container for Power throttling configuration data.
+ * TODO(b/302814899): extract to separate class.
+ */
+ public static class PowerThrottlingConfigData {
+ /** Lowest brightness cap allowed for this device. */
+ public final float brightnessLowestCapAllowed;
+ /** Time window for polling power in seconds. */
+ public final int pollingWindowMillis;
+ public PowerThrottlingConfigData(float brightnessLowestCapAllowed,
+ int pollingWindowMillis) {
+ this.brightnessLowestCapAllowed = brightnessLowestCapAllowed;
+ this.pollingWindowMillis = pollingWindowMillis;
+ }
+
+ @Override
+ public String toString() {
+ return "PowerThrottlingConfigData{"
+ + "brightnessLowestCapAllowed: "
+ + brightnessLowestCapAllowed
+ + ", pollingWindowMillis: " + pollingWindowMillis
+ + "} ";
+ }
+ }
+
+ /**
+ * Container for power throttling data.
+ * TODO(b/302814899): extract to separate class and unify with ThermalBrightnessThrottlingData.
+ */
+ public static class PowerThrottlingData {
+ public List<ThrottlingLevel> throttlingLevels;
+
+ /**
+ * thermal status to power quota mapping.
+ */
+ public static class ThrottlingLevel {
+ public @PowerManager.ThermalStatus int thermalStatus;
+ public float powerQuotaMilliWatts;
+
+ public ThrottlingLevel(
+ @PowerManager.ThermalStatus int thermalStatus, float powerQuotaMilliWatts) {
+ this.thermalStatus = thermalStatus;
+ this.powerQuotaMilliWatts = powerQuotaMilliWatts;
+ }
+
+ @Override
+ public String toString() {
+ return "[" + thermalStatus + "," + powerQuotaMilliWatts + "]";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof ThrottlingLevel)) {
+ return false;
+ }
+ ThrottlingLevel otherThrottlingLevel = (ThrottlingLevel) obj;
+
+ return otherThrottlingLevel.thermalStatus == this.thermalStatus
+ && otherThrottlingLevel.powerQuotaMilliWatts == this.powerQuotaMilliWatts;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 1;
+ result = 31 * result + thermalStatus;
+ result = 31 * result + Float.hashCode(powerQuotaMilliWatts);
+ return result;
+ }
+ }
+
+
+ /**
+ * Creates multiple temperature based throttling levels of power quota.
+ */
+ public static PowerThrottlingData create(
+ List<ThrottlingLevel> throttlingLevels) {
+ if (throttlingLevels == null || throttlingLevels.size() == 0) {
+ Slog.e(TAG, "PowerThrottlingData received null or empty throttling levels");
+ return null;
+ }
+
+ ThrottlingLevel prevLevel = throttlingLevels.get(0);
+ final int numLevels = throttlingLevels.size();
+ for (int i = 1; i < numLevels; i++) {
+ ThrottlingLevel thisLevel = throttlingLevels.get(i);
+
+ if (thisLevel.thermalStatus <= prevLevel.thermalStatus) {
+ Slog.e(TAG, "powerThrottlingMap must be strictly increasing, ignoring "
+ + "configuration. ThermalStatus " + thisLevel.thermalStatus + " <= "
+ + prevLevel.thermalStatus);
+ return null;
+ }
+
+ if (thisLevel.powerQuotaMilliWatts >= prevLevel.powerQuotaMilliWatts) {
+ Slog.e(TAG, "powerThrottlingMap must be strictly decreasing, ignoring "
+ + "configuration. powerQuotaMilliWatts "
+ + thisLevel.powerQuotaMilliWatts + " >= "
+ + prevLevel.powerQuotaMilliWatts);
+ return null;
+ }
+
+ prevLevel = thisLevel;
+ }
+ return new PowerThrottlingData(throttlingLevels);
+ }
+
+ @Override
+ public String toString() {
+ return "PowerThrottlingData{"
+ + "throttlingLevels:" + throttlingLevels
+ + "} ";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (!(obj instanceof PowerThrottlingData)) {
+ return false;
+ }
+
+ PowerThrottlingData otherData = (PowerThrottlingData) obj;
+ return throttlingLevels.equals(otherData.throttlingLevels);
+ }
+
+ @Override
+ public int hashCode() {
+ return throttlingLevels.hashCode();
+ }
+
+ @VisibleForTesting
+ PowerThrottlingData(List<ThrottlingLevel> inLevels) {
+ throttlingLevels = new ArrayList<>(inLevels.size());
+ for (ThrottlingLevel level : inLevels) {
+ throttlingLevels.add(new ThrottlingLevel(level.thermalStatus,
+ level.powerQuotaMilliWatts));
+ }
+ }
+ }
+
+ /**
* Container for brightness throttling data.
*/
public static class ThermalBrightnessThrottlingData {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index d372f30..0689478 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -160,6 +160,7 @@
import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.layout.Layout;
import com.android.server.display.mode.DisplayModeDirector;
+import com.android.server.display.notifications.DisplayNotificationManager;
import com.android.server.display.utils.SensorUtils;
import com.android.server.input.InputManagerInternal;
import com.android.server.utils.FoldSettingProvider;
@@ -522,6 +523,8 @@
private final DisplayManagerFlags mFlags;
+ private final DisplayNotificationManager mDisplayNotificationManager;
+
/**
* Applications use {@link android.view.Display#getRefreshRate} and
* {@link android.view.Display.Mode#getRefreshRate} to know what is the display refresh rate.
@@ -555,6 +558,7 @@
mInjector = injector;
mContext = context;
mFlags = injector.getFlags();
+ mDisplayNotificationManager = new DisplayNotificationManager(mFlags, mContext);
mHandler = new DisplayManagerHandler(DisplayThread.get().getLooper());
mUiHandler = UiThread.getHandler();
mDisplayDeviceRepo = new DisplayDeviceRepository(mSyncRoot, mPersistentDataStore);
@@ -650,6 +654,7 @@
}
mDisplayModeDirector.onBootCompleted();
mLogicalDisplayMapper.onBootCompleted();
+ mDisplayNotificationManager.onBootCompleted();
}
}
@@ -784,6 +789,10 @@
}
}
+ DisplayNotificationManager getDisplayNotificationManager() {
+ return mDisplayNotificationManager;
+ }
+
private void loadStableDisplayValuesLocked() {
final Point size = mPersistentDataStore.getStableDisplaySize();
if (size.x > 0 && size.y > 0) {
@@ -1776,7 +1785,8 @@
synchronized (mSyncRoot) {
// main display adapter
registerDisplayAdapterLocked(mInjector.getLocalDisplayAdapter(mSyncRoot, mContext,
- mHandler, mDisplayDeviceRepo, mFlags));
+ mHandler, mDisplayDeviceRepo, mFlags,
+ mDisplayNotificationManager));
// Standalone VR devices rely on a virtual display as their primary display for
// 2D UI. We register virtual display adapter along side the main display adapter
@@ -3191,9 +3201,10 @@
LocalDisplayAdapter getLocalDisplayAdapter(SyncRoot syncRoot, Context context,
Handler handler, DisplayAdapter.Listener displayAdapterListener,
- DisplayManagerFlags flags) {
+ DisplayManagerFlags flags,
+ DisplayNotificationManager displayNotificationManager) {
return new LocalDisplayAdapter(syncRoot, context, handler, displayAdapterListener,
- flags);
+ flags, displayNotificationManager);
}
long getDefaultDisplayDelayTimeout() {
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index 9b022d8..5e6ff46 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -30,6 +30,7 @@
class DisplayManagerShellCommand extends ShellCommand {
private static final String TAG = "DisplayManagerShellCommand";
+ private static final String NOTIFICATION_TYPES = "on-hotplug-error";
private final DisplayManagerService mService;
private final DisplayManagerFlags mFlags;
@@ -46,6 +47,10 @@
}
final PrintWriter pw = getOutPrintWriter();
switch(cmd) {
+ case "show-notification":
+ return showNotification();
+ case "cancel-notifications":
+ return cancelNotifications();
case "set-brightness":
return setBrightness();
case "reset-brightness-configuration":
@@ -102,6 +107,10 @@
pw.println(" help");
pw.println(" Print this help text.");
pw.println();
+ pw.println(" show-notification NOTIFICATION_TYPE");
+ pw.println(" Show notification for one of the following types: " + NOTIFICATION_TYPES);
+ pw.println(" cancel-notifications");
+ pw.println(" Cancel notifications.");
pw.println(" set-brightness BRIGHTNESS");
pw.println(" Sets the current brightness to BRIGHTNESS (a number between 0 and 1).");
pw.println(" reset-brightness-configuration");
@@ -172,6 +181,33 @@
return 0;
}
+ private int showNotification() {
+ final String notificationType = getNextArg();
+ if (notificationType == null) {
+ getErrPrintWriter().println("Error: no notificationType specified, use one of: "
+ + NOTIFICATION_TYPES);
+ return 1;
+ }
+
+ switch(notificationType) {
+ case "on-hotplug-error":
+ mService.getDisplayNotificationManager().onHotplugConnectionError();
+ break;
+ default:
+ getErrPrintWriter().println(
+ "Error: unexpected notification type=" + notificationType + ", use one of: "
+ + NOTIFICATION_TYPES);
+ return 1;
+ }
+
+ return 0;
+ }
+
+ private int cancelNotifications() {
+ mService.getDisplayNotificationManager().cancelNotifications();
+ return 0;
+ }
+
private int setBrightness() {
String brightnessText = getNextArg();
if (brightnessText == null) {
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 0a1f316..e5d38cb 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -22,9 +22,8 @@
import android.app.ActivityThread;
import android.content.Context;
import android.content.res.Resources;
-import android.hardware.display.DisplayManagerInternal;
-import android.hardware.display.DisplayManagerInternal.DisplayOffloader;
import android.hardware.display.DisplayManagerInternal.DisplayOffloadSession;
+import android.hardware.display.DisplayManagerInternal.DisplayOffloader;
import android.hardware.sidekick.SidekickInternal;
import android.os.Build;
import android.os.Handler;
@@ -52,6 +51,7 @@
import com.android.server.LocalServices;
import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.mode.DisplayModeDirector;
+import com.android.server.display.notifications.DisplayNotificationManager;
import com.android.server.lights.LightsManager;
import com.android.server.lights.LogicalLight;
@@ -86,18 +86,25 @@
private final DisplayManagerFlags mFlags;
+ private final DisplayNotificationManager mDisplayNotificationManager;
+
private Context mOverlayContext;
// Called with SyncRoot lock held.
LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context,
- Handler handler, Listener listener, DisplayManagerFlags flags) {
- this(syncRoot, context, handler, listener, flags, new Injector());
+ Handler handler, Listener listener, DisplayManagerFlags flags,
+ DisplayNotificationManager displayNotificationManager) {
+ this(syncRoot, context, handler, listener, flags, displayNotificationManager,
+ new Injector());
}
@VisibleForTesting
LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler,
- Listener listener, DisplayManagerFlags flags, Injector injector) {
+ Listener listener, DisplayManagerFlags flags,
+ DisplayNotificationManager displayNotificationManager,
+ Injector injector) {
super(syncRoot, context, handler, listener, TAG);
+ mDisplayNotificationManager = displayNotificationManager;
mInjector = injector;
mSurfaceControlProxy = mInjector.getSurfaceControlProxy();
mIsBootDisplayModeSupported = mSurfaceControlProxy.getBootDisplayModeSupport();
@@ -1454,6 +1461,8 @@
+ "timestampNanos=" + timestampNanos
+ ", connectionError=" + connectionError + ")");
}
+
+ mDisplayNotificationManager.onHotplugConnectionError();
}
@Override
diff --git a/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java b/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
index 652e6cf..39f0b13 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
@@ -105,16 +105,21 @@
public void resetHdrConfig(HdrBrightnessData data, int width, int height,
float minimumHdrPercentOfScreen, IBinder displayToken) {
mHdrBrightnessData = data;
- mHdrListener.mHdrMinPixels = (float) (width * height) * minimumHdrPercentOfScreen;
+ mHdrListener.mHdrMinPixels = minimumHdrPercentOfScreen <= 0 ? -1
+ : (float) (width * height) * minimumHdrPercentOfScreen;
if (displayToken != mRegisteredDisplayToken) { // token changed, resubscribe
if (mRegisteredDisplayToken != null) { // previous token not null, unsubscribe
mHdrListener.unregister(mRegisteredDisplayToken);
mHdrVisible = false;
+ mRegisteredDisplayToken = null;
}
- if (displayToken != null) { // new token not null, subscribe
+ // new token not null and hdr min % of the screen is set, subscribe.
+ // e.g. for virtual display, HBM data will be missing and HdrListener
+ // should not be registered
+ if (displayToken != null && mHdrListener.mHdrMinPixels > 0) {
mHdrListener.register(displayToken);
+ mRegisteredDisplayToken = displayToken;
}
- mRegisteredDisplayToken = displayToken;
}
recalculateBrightnessCap(data, mAmbientLux, mHdrVisible);
}
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..e3aa161 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 =
@@ -231,7 +234,9 @@
}
}
- @VisibleForTesting void onUserChanged(int userHandle) {
+ // should be called in handler thread (same thread that started animation)
+ @VisibleForTesting
+ void onUserChanged(int userHandle) {
final ContentResolver cr = getContext().getContentResolver();
if (mCurrentUser != UserHandle.USER_NULL) {
@@ -470,6 +475,15 @@
}
}
+ // should be called in handler thread (same thread that started animation)
+ @VisibleForTesting
+ void cancelAllAnimators() {
+ mNightDisplayTintController.cancelAnimator();
+ mGlobalSaturationTintController.cancelAnimator();
+ mReduceBrightColorsTintController.cancelAnimator();
+ mDisplayWhiteBalanceTintController.cancelAnimator();
+ }
+
private boolean resetReduceBrightColors() {
if (mCurrentUser == UserHandle.USER_NULL) {
return false;
@@ -716,15 +730,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 b6273e1..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,25 +47,25 @@
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);
- private final FlagState mDisplayResolutionRangeVotingState = new FlagState(
- Flags.FLAG_ENABLE_DISPLAY_RESOLUTION_RANGE_VOTING,
- Flags::enableDisplayResolutionRangeVoting);
-
- private final FlagState mUserPreferredModeVoteState = new FlagState(
- Flags.FLAG_ENABLE_USER_PREFERRED_MODE_VOTE,
- Flags::enableUserPreferredModeVote);
-
private final FlagState mExternalDisplayLimitModeState = new FlagState(
Flags.FLAG_ENABLE_MODE_LIMIT_FOR_EXTERNAL_DISPLAY,
Flags::enableModeLimitForExternalDisplay);
- private final FlagState mDisplaysRefreshRatesSynchronizationState = new FlagState(
- Flags.FLAG_ENABLE_DISPLAYS_REFRESH_RATES_SYNCHRONIZATION,
- Flags::enableDisplaysRefreshRatesSynchronization);
+ private final FlagState mConnectedDisplayErrorHandlingFlagState = new FlagState(
+ Flags.FLAG_ENABLE_CONNECTED_DISPLAY_ERROR_HANDLING,
+ Flags::enableConnectedDisplayErrorHandling);
+
+ private final FlagState mBackUpSmoothDisplayAndForcePeakRefreshRateFlagState = new FlagState(
+ Flags.FLAG_BACK_UP_SMOOTH_DISPLAY_AND_FORCE_PEAK_REFRESH_RATE,
+ Flags::backUpSmoothDisplayAndForcePeakRefreshRate);
/** Returns whether connected display management is enabled or not. */
public boolean isConnectedDisplayManagementEnabled() {
@@ -88,9 +88,16 @@
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 mDisplayResolutionRangeVotingState.isEnabled();
+ return isExternalDisplayLimitModeEnabled();
}
/**
@@ -98,7 +105,7 @@
* {@link com.android.server.display.mode.DisplayModeDirector}
*/
public boolean isUserPreferredModeVoteEnabled() {
- return mUserPreferredModeVoteState.isEnabled();
+ return isExternalDisplayLimitModeEnabled();
}
/**
@@ -112,7 +119,7 @@
* @return Whether displays refresh rate synchronization is enabled.
*/
public boolean isDisplaysRefreshRatesSynchronizationEnabled() {
- return mDisplaysRefreshRatesSynchronizationState.isEnabled();
+ return isExternalDisplayLimitModeEnabled();
}
/** Returns whether displayoffload is enabled on not */
@@ -120,6 +127,15 @@
return mDisplayOffloadFlagState.isEnabled();
}
+ /** Returns whether error notifications for connected displays are enabled on not */
+ public boolean isConnectedDisplayErrorHandlingEnabled() {
+ return mConnectedDisplayErrorHandlingFlagState.isEnabled();
+ }
+
+ public boolean isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled() {
+ return mBackUpSmoothDisplayAndForcePeakRefreshRateFlagState.isEnabled();
+ }
+
private static class FlagState {
private final String mName;
@@ -133,7 +149,6 @@
mFlagFunction = flagFunction;
}
- // TODO(b/297159910): Simplify using READ-ONLY flags when available.
private boolean isEnabled() {
if (mEnabledSet) {
if (DEBUG) {
@@ -150,19 +165,13 @@
}
private boolean flagOrSystemProperty(Supplier<Boolean> flagFunction, String flagName) {
+ boolean flagValue = flagFunction.get();
// TODO(b/299462337) Remove when the infrastructure is ready.
- if ((Build.IS_ENG || Build.IS_USERDEBUG)
- && SystemProperties.getBoolean("persist.sys." + flagName, false)) {
- return true;
+ if (Build.IS_ENG || Build.IS_USERDEBUG) {
+ return SystemProperties.getBoolean("persist.sys." + flagName + "-override",
+ flagValue);
}
- try {
- return flagFunction.get();
- } catch (Throwable ex) {
- if (DEBUG) {
- Slog.i(TAG, "Flags not ready yet. Return false for " + flagName, ex);
- }
- return false;
- }
+ return flagValue;
}
}
}
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 542f26c..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"
@@ -72,3 +80,19 @@
bug: "299521647"
is_fixed_read_only: true
}
+
+flag {
+ name: "enable_connected_display_error_handling"
+ namespace: "display_manager"
+ description: "Feature flag for connected display error handling"
+ bug: "283461472"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "back_up_smooth_display_and_force_peak_refresh_rate"
+ namespace: "display_manager"
+ description: "Feature flag for backing up Smooth Display and Force Peak Refresh Rate"
+ bug: "211737588"
+ is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index 71ea8cc..ca23844 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -22,6 +22,7 @@
import static android.view.Display.Mode.INVALID_MODE_ID;
import static com.android.server.display.DisplayDeviceConfig.DEFAULT_LOW_REFRESH_RATE;
+import static com.android.internal.display.RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay;
import android.annotation.IntegerRes;
import android.annotation.NonNull;
@@ -176,6 +177,8 @@
private final boolean mIsDisplaysRefreshRatesSynchronizationEnabled;
+ private final boolean mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled;
+
public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler,
@NonNull DisplayManagerFlags displayManagerFlags) {
this(context, handler, new RealInjector(context), displayManagerFlags);
@@ -191,6 +194,8 @@
.isExternalDisplayLimitModeEnabled();
mIsDisplaysRefreshRatesSynchronizationEnabled = displayManagerFlags
.isDisplaysRefreshRatesSynchronizationEnabled();
+ mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled = displayManagerFlags
+ .isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled();
mContext = context;
mHandler = new DisplayModeDirectorHandler(handler.getLooper());
mInjector = injector;
@@ -1193,8 +1198,7 @@
public void observe() {
final ContentResolver cr = mContext.getContentResolver();
mInjector.registerPeakRefreshRateObserver(cr, this);
- cr.registerContentObserver(mMinRefreshRateSetting, false /*notifyDescendants*/, this,
- UserHandle.USER_SYSTEM);
+ mInjector.registerMinRefreshRateObserver(cr, this);
cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this,
UserHandle.USER_SYSTEM);
cr.registerContentObserver(mMatchContentFrameRateSetting, false /*notifyDescendants*/,
@@ -1292,10 +1296,34 @@
private void updateRefreshRateSettingLocked() {
final ContentResolver cr = mContext.getContentResolver();
+ float highestRefreshRate = findHighestRefreshRateForDefaultDisplay(mContext);
+
float minRefreshRate = Settings.System.getFloatForUser(cr,
Settings.System.MIN_REFRESH_RATE, 0f, cr.getUserId());
+ if (Float.isInfinite(minRefreshRate)) {
+ // Infinity means that we want the highest possible refresh rate
+ minRefreshRate = highestRefreshRate;
+
+ if (!mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled) {
+ // The flag had been turned off, we need to restore the original value
+ Settings.System.putFloatForUser(cr,
+ Settings.System.MIN_REFRESH_RATE, minRefreshRate, cr.getUserId());
+ }
+ }
+
float peakRefreshRate = Settings.System.getFloatForUser(cr,
Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate, cr.getUserId());
+ if (Float.isInfinite(peakRefreshRate)) {
+ // Infinity means that we want the highest possible refresh rate
+ peakRefreshRate = highestRefreshRate;
+
+ if (!mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled) {
+ // The flag had been turned off, we need to restore the original value
+ Settings.System.putFloatForUser(cr,
+ Settings.System.PEAK_REFRESH_RATE, peakRefreshRate, cr.getUserId());
+ }
+ }
+
updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate);
}
@@ -3082,6 +3110,7 @@
interface Injector {
Uri PEAK_REFRESH_RATE_URI = Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
+ Uri MIN_REFRESH_RATE_URI = Settings.System.getUriFor(Settings.System.MIN_REFRESH_RATE);
@NonNull
DeviceConfigInterface getDeviceConfig();
@@ -3089,6 +3118,9 @@
void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
@NonNull ContentObserver observer);
+ void registerMinRefreshRateObserver(@NonNull ContentResolver cr,
+ @NonNull ContentObserver observer);
+
void registerDisplayListener(@NonNull DisplayManager.DisplayListener listener,
Handler handler);
@@ -3140,6 +3172,13 @@
}
@Override
+ public void registerMinRefreshRateObserver(@NonNull ContentResolver cr,
+ @NonNull ContentObserver observer) {
+ cr.registerContentObserver(MIN_REFRESH_RATE_URI, false /*notifyDescendants*/,
+ observer, UserHandle.USER_SYSTEM);
+ }
+
+ @Override
public void registerDisplayListener(DisplayManager.DisplayListener listener,
Handler handler) {
getDisplayManager().registerDisplayListener(listener, handler);
diff --git a/services/core/java/com/android/server/display/notifications/DisplayNotificationManager.java b/services/core/java/com/android/server/display/notifications/DisplayNotificationManager.java
new file mode 100644
index 0000000..7d83e90
--- /dev/null
+++ b/services/core/java/com/android/server/display/notifications/DisplayNotificationManager.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.notifications;
+
+import static android.app.Notification.COLOR_DEFAULT;
+import static com.android.internal.notification.SystemNotificationChannels.ALERTS;
+
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.feature.DisplayManagerFlags;
+
+/**
+ * Manages notifications for {@link com.android.server.display.DisplayManagerService}.
+ */
+public class DisplayNotificationManager {
+ /** Dependency injection interface for {@link DisplayNotificationManager} */
+ public interface Injector {
+ /** Get {@link NotificationManager} service or null if not available. */
+ @Nullable
+ NotificationManager getNotificationManager();
+ }
+
+ private static final String TAG = "DisplayNotificationManager";
+ private static final String NOTIFICATION_GROUP_NAME = TAG;
+ private static final String DISPLAY_NOTIFICATION_TAG = TAG;
+ private static final int DISPLAY_NOTIFICATION_ID = 1;
+ private static final long NOTIFICATION_TIMEOUT_MILLISEC = 30000L;
+
+ private final Injector mInjector;
+ private final Context mContext;
+ private final boolean mConnectedDisplayErrorHandlingEnabled;
+ private NotificationManager mNotificationManager;
+
+ public DisplayNotificationManager(final DisplayManagerFlags flags, final Context context) {
+ this(flags, context, () -> context.getSystemService(NotificationManager.class));
+ }
+
+ @VisibleForTesting
+ DisplayNotificationManager(final DisplayManagerFlags flags, final Context context,
+ final Injector injector) {
+ mConnectedDisplayErrorHandlingEnabled = flags.isConnectedDisplayErrorHandlingEnabled();
+ mContext = context;
+ mInjector = injector;
+ }
+
+ /**
+ * Initialize services, which may be not yet published during boot.
+ * see {@link android.os.ServiceManager.ServiceNotFoundException}.
+ */
+ public void onBootCompleted() {
+ mNotificationManager = mInjector.getNotificationManager();
+ if (mNotificationManager == null) {
+ Slog.e(TAG, "onBootCompleted: NotificationManager is null");
+ return;
+ }
+ }
+
+ /**
+ * Send notification about hotplug connection error.
+ */
+ public void onHotplugConnectionError() {
+ if (!mConnectedDisplayErrorHandlingEnabled) {
+ Slog.d(TAG, "onHotplugConnectionError:"
+ + " mConnectedDisplayErrorHandlingEnabled is false");
+ return;
+ }
+
+ sendErrorNotification(createErrorNotification(
+ R.string.connected_display_unavailable_notification_title,
+ R.string.connected_display_unavailable_notification_content));
+ }
+
+ /**
+ * Cancel sent notifications.
+ */
+ public void cancelNotifications() {
+ if (mNotificationManager == null) {
+ Slog.e(TAG, "Can't cancelNotifications: NotificationManager is null");
+ return;
+ }
+
+ mNotificationManager.cancel(DISPLAY_NOTIFICATION_TAG, DISPLAY_NOTIFICATION_ID);
+ }
+
+ /**
+ * Send generic error notification.
+ */
+ @SuppressLint("AndroidFrameworkRequiresPermission")
+ private void sendErrorNotification(final Notification notification) {
+ if (mNotificationManager == null) {
+ Slog.e(TAG, "Can't sendErrorNotification: NotificationManager is null");
+ return;
+ }
+
+ mNotificationManager.notify(DISPLAY_NOTIFICATION_TAG, DISPLAY_NOTIFICATION_ID,
+ notification);
+ }
+
+ /**
+ * @return a newly built notification about an issue with connected display.
+ */
+ private Notification createErrorNotification(final int titleId, final int messageId) {
+ final Resources resources = mContext.getResources();
+ final CharSequence title = resources.getText(titleId);
+ final CharSequence message = resources.getText(messageId);
+
+ int color = COLOR_DEFAULT;
+ try (var attrs = mContext.obtainStyledAttributes(new int[]{R.attr.colorError})) {
+ color = attrs.getColor(0, color);
+ } catch (Resources.NotFoundException e) {
+ Slog.e(TAG, "colorError attribute is not found: " + e.getMessage());
+ }
+
+ return new Notification.Builder(mContext, ALERTS)
+ .setGroup(NOTIFICATION_GROUP_NAME)
+ .setSmallIcon(R.drawable.usb_cable_unknown_issue)
+ .setWhen(0)
+ .setTimeoutAfter(NOTIFICATION_TIMEOUT_MILLISEC)
+ .setOngoing(false)
+ .setTicker(title)
+ .setColor(color)
+ .setContentTitle(title)
+ .setContentText(message)
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
+ .setCategory(Notification.CATEGORY_ERROR)
+ .build();
+ }
+}
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/graphics/fonts/UpdatableFontDir.java b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
index cd3d2f0..0f40ca0 100644
--- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
+++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
@@ -619,8 +619,8 @@
}
return new FontConfig(
- config.getFontFamilies(), config.getAliases(), mergedFamilies, mLastModifiedMillis,
- mConfigVersion);
+ config.getFontFamilies(), config.getAliases(), mergedFamilies,
+ config.getLocaleFallbackCustomizations(), mLastModifiedMillis, mConfigVersion);
}
private PersistentSystemFontConfig.Config readPersistentConfig() {
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/FocusEventDebugView.java b/services/core/java/com/android/server/input/FocusEventDebugView.java
deleted file mode 100644
index 4b8fabde..0000000
--- a/services/core/java/com/android/server/input/FocusEventDebugView.java
+++ /dev/null
@@ -1,848 +0,0 @@
-/*
- * Copyright 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.input;
-
-import static android.util.TypedValue.COMPLEX_UNIT_DIP;
-import static android.util.TypedValue.COMPLEX_UNIT_SP;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-
-import android.animation.LayoutTransition;
-import android.annotation.AnyThread;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.ColorMatrixColorFilter;
-import android.graphics.Paint;
-import android.graphics.Typeface;
-import android.util.DisplayMetrics;
-import android.util.Pair;
-import android.util.Slog;
-import android.util.TypedValue;
-import android.view.Gravity;
-import android.view.InputDevice;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.RoundedCorner;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.WindowInsets;
-import android.view.animation.AccelerateInterpolator;
-import android.widget.HorizontalScrollView;
-import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
-
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Locale;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Supplier;
-
-/**
- * Displays focus events, such as physical keyboard KeyEvents and non-pointer MotionEvents on
- * the screen.
- */
-class FocusEventDebugView extends RelativeLayout {
-
- private static final String TAG = FocusEventDebugView.class.getSimpleName();
-
- private static final int KEY_FADEOUT_DURATION_MILLIS = 1000;
- private static final int KEY_TRANSITION_DURATION_MILLIS = 100;
-
- private static final int OUTER_PADDING_DP = 16;
- private static final int KEY_SEPARATION_MARGIN_DP = 16;
- private static final int KEY_VIEW_SIDE_PADDING_DP = 16;
- private static final int KEY_VIEW_VERTICAL_PADDING_DP = 8;
- private static final int KEY_VIEW_MIN_WIDTH_DP = 32;
- private static final int KEY_VIEW_TEXT_SIZE_SP = 12;
- private static final double ROTATY_GRAPH_HEIGHT_FRACTION = 0.5;
-
- private final InputManagerService mService;
- private final int mOuterPadding;
- private final DisplayMetrics mDm;
-
- // Tracks all keys that are currently pressed/down.
- private final Map<Pair<Integer /*deviceId*/, Integer /*scanCode*/>, PressedKeyView>
- mPressedKeys = new HashMap<>();
-
- @Nullable
- private FocusEventDebugGlobalMonitor mFocusEventDebugGlobalMonitor;
- @Nullable
- private PressedKeyContainer mPressedKeyContainer;
- @Nullable
- private PressedKeyContainer mPressedModifierContainer;
- private final Supplier<RotaryInputValueView> mRotaryInputValueViewFactory;
- @Nullable
- private RotaryInputValueView mRotaryInputValueView;
- private final Supplier<RotaryInputGraphView> mRotaryInputGraphViewFactory;
- @Nullable
- private RotaryInputGraphView mRotaryInputGraphView;
-
- @VisibleForTesting
- FocusEventDebugView(Context c, InputManagerService service,
- Supplier<RotaryInputValueView> rotaryInputValueViewFactory,
- Supplier<RotaryInputGraphView> rotaryInputGraphViewFactory) {
- super(c);
- setFocusableInTouchMode(true);
-
- mService = service;
- mRotaryInputValueViewFactory = rotaryInputValueViewFactory;
- mRotaryInputGraphViewFactory = rotaryInputGraphViewFactory;
- mDm = mContext.getResources().getDisplayMetrics();
- mOuterPadding = (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, OUTER_PADDING_DP, mDm);
- }
-
- FocusEventDebugView(Context c, InputManagerService service) {
- this(c, service, () -> new RotaryInputValueView(c), () -> new RotaryInputGraphView(c));
- }
-
- @Override
- public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- int paddingBottom = 0;
-
- final RoundedCorner bottomLeft =
- insets.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT);
- if (bottomLeft != null && !insets.isRound()) {
- paddingBottom = bottomLeft.getRadius();
- }
-
- final RoundedCorner bottomRight =
- insets.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT);
- if (bottomRight != null && !insets.isRound()) {
- paddingBottom = Math.max(paddingBottom, bottomRight.getRadius());
- }
-
- if (insets.getDisplayCutout() != null) {
- paddingBottom =
- Math.max(paddingBottom, insets.getDisplayCutout().getSafeInsetBottom());
- }
-
- setPadding(mOuterPadding, mOuterPadding, mOuterPadding, mOuterPadding + paddingBottom);
- setClipToPadding(false);
- invalidate();
- return super.onApplyWindowInsets(insets);
- }
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- handleKeyEvent(event);
- return super.dispatchKeyEvent(event);
- }
-
- @AnyThread
- public void updateShowKeyPresses(boolean enabled) {
- post(() -> handleUpdateShowKeyPresses(enabled));
- }
-
- @AnyThread
- public void updateShowRotaryInput(boolean enabled) {
- post(() -> handleUpdateShowRotaryInput(enabled));
- }
-
- private void handleUpdateShowKeyPresses(boolean enabled) {
- if (enabled == showKeyPresses()) {
- return;
- }
-
- if (!enabled) {
- removeView(mPressedKeyContainer);
- mPressedKeyContainer = null;
- removeView(mPressedModifierContainer);
- mPressedModifierContainer = null;
- return;
- }
-
- mPressedKeyContainer = new PressedKeyContainer(mContext);
- mPressedKeyContainer.setOrientation(LinearLayout.HORIZONTAL);
- mPressedKeyContainer.setGravity(Gravity.RIGHT | Gravity.BOTTOM);
- mPressedKeyContainer.setLayoutDirection(LAYOUT_DIRECTION_LTR);
- final var scroller = new HorizontalScrollView(mContext);
- scroller.addView(mPressedKeyContainer);
- scroller.setHorizontalScrollBarEnabled(false);
- scroller.addOnLayoutChangeListener(
- (view, l, t, r, b, ol, ot, or, ob) -> scroller.fullScroll(View.FOCUS_RIGHT));
- scroller.setHorizontalFadingEdgeEnabled(true);
- LayoutParams scrollerLayoutParams = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
- scrollerLayoutParams.addRule(ALIGN_PARENT_BOTTOM);
- scrollerLayoutParams.addRule(ALIGN_PARENT_RIGHT);
- addView(scroller, scrollerLayoutParams);
-
- mPressedModifierContainer = new PressedKeyContainer(mContext);
- mPressedModifierContainer.setOrientation(LinearLayout.VERTICAL);
- mPressedModifierContainer.setGravity(Gravity.LEFT | Gravity.BOTTOM);
- LayoutParams modifierLayoutParams = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
- modifierLayoutParams.addRule(ALIGN_PARENT_BOTTOM);
- modifierLayoutParams.addRule(ALIGN_PARENT_LEFT);
- modifierLayoutParams.addRule(LEFT_OF, scroller.getId());
- addView(mPressedModifierContainer, modifierLayoutParams);
- }
-
- @VisibleForTesting
- void handleUpdateShowRotaryInput(boolean enabled) {
- if (enabled == showRotaryInput()) {
- return;
- }
-
- if (!enabled) {
- mFocusEventDebugGlobalMonitor.dispose();
- mFocusEventDebugGlobalMonitor = null;
- removeView(mRotaryInputValueView);
- mRotaryInputValueView = null;
- removeView(mRotaryInputGraphView);
- mRotaryInputGraphView = null;
- return;
- }
-
- mFocusEventDebugGlobalMonitor = new FocusEventDebugGlobalMonitor(this, mService);
-
- mRotaryInputValueView = mRotaryInputValueViewFactory.get();
- LayoutParams valueLayoutParams = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
- valueLayoutParams.addRule(CENTER_HORIZONTAL);
- valueLayoutParams.addRule(ALIGN_PARENT_BOTTOM);
- addView(mRotaryInputValueView, valueLayoutParams);
-
- mRotaryInputGraphView = mRotaryInputGraphViewFactory.get();
- LayoutParams graphLayoutParams = new LayoutParams(LayoutParams.MATCH_PARENT,
- (int) (ROTATY_GRAPH_HEIGHT_FRACTION * mDm.heightPixels));
- graphLayoutParams.addRule(CENTER_IN_PARENT);
- addView(mRotaryInputGraphView, graphLayoutParams);
- }
-
- /** Report a key event to the debug view. */
- @AnyThread
- public void reportKeyEvent(KeyEvent event) {
- post(() -> handleKeyEvent(KeyEvent.obtain((KeyEvent) event)));
- }
-
- /** Report a motion event to the debug view. */
- @AnyThread
- public void reportMotionEvent(MotionEvent event) {
- if (event.getSource() != InputDevice.SOURCE_ROTARY_ENCODER) {
- return;
- }
-
- post(() -> handleRotaryInput(MotionEvent.obtain((MotionEvent) event)));
- }
-
- private void handleKeyEvent(KeyEvent keyEvent) {
- if (!showKeyPresses()) {
- return;
- }
-
- final var identifier = new Pair<>(keyEvent.getDeviceId(), keyEvent.getScanCode());
- final var container = KeyEvent.isModifierKey(keyEvent.getKeyCode())
- ? mPressedModifierContainer
- : mPressedKeyContainer;
- PressedKeyView pressedKeyView = mPressedKeys.get(identifier);
- switch (keyEvent.getAction()) {
- case KeyEvent.ACTION_DOWN: {
- if (pressedKeyView != null) {
- if (keyEvent.getRepeatCount() == 0) {
- Slog.w(TAG, "Got key down for "
- + KeyEvent.keyCodeToString(keyEvent.getKeyCode())
- + " that was already tracked as being down.");
- break;
- }
- container.handleKeyRepeat(pressedKeyView);
- break;
- }
-
- pressedKeyView = new PressedKeyView(mContext, getLabel(keyEvent));
- mPressedKeys.put(identifier, pressedKeyView);
- container.handleKeyPressed(pressedKeyView);
- break;
- }
- case KeyEvent.ACTION_UP: {
- if (pressedKeyView == null) {
- Slog.w(TAG, "Got key up for " + KeyEvent.keyCodeToString(keyEvent.getKeyCode())
- + " that was not tracked as being down.");
- break;
- }
- mPressedKeys.remove(identifier);
- container.handleKeyRelease(pressedKeyView);
- break;
- }
- default:
- break;
- }
- keyEvent.recycle();
- }
-
- @VisibleForTesting
- void handleRotaryInput(MotionEvent motionEvent) {
- if (!showRotaryInput()) {
- return;
- }
-
- float scrollAxisValue = motionEvent.getAxisValue(MotionEvent.AXIS_SCROLL);
- mRotaryInputValueView.updateValue(scrollAxisValue);
- mRotaryInputGraphView.addValue(scrollAxisValue, motionEvent.getEventTime());
-
- motionEvent.recycle();
- }
-
- private static String getLabel(KeyEvent event) {
- switch (event.getKeyCode()) {
- case KeyEvent.KEYCODE_SPACE:
- return "\u2423";
- case KeyEvent.KEYCODE_TAB:
- return "\u21e5";
- case KeyEvent.KEYCODE_ENTER:
- case KeyEvent.KEYCODE_NUMPAD_ENTER:
- return "\u23CE";
- case KeyEvent.KEYCODE_DEL:
- return "\u232B";
- case KeyEvent.KEYCODE_FORWARD_DEL:
- return "\u2326";
- case KeyEvent.KEYCODE_ESCAPE:
- return "ESC";
- case KeyEvent.KEYCODE_DPAD_UP:
- return "\u2191";
- case KeyEvent.KEYCODE_DPAD_DOWN:
- return "\u2193";
- case KeyEvent.KEYCODE_DPAD_LEFT:
- return "\u2190";
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- return "\u2192";
- case KeyEvent.KEYCODE_DPAD_UP_RIGHT:
- return "\u2197";
- case KeyEvent.KEYCODE_DPAD_UP_LEFT:
- return "\u2196";
- case KeyEvent.KEYCODE_DPAD_DOWN_RIGHT:
- return "\u2198";
- case KeyEvent.KEYCODE_DPAD_DOWN_LEFT:
- return "\u2199";
- default:
- break;
- }
-
- final int unicodeChar = event.getUnicodeChar();
- if (unicodeChar != 0) {
- return new String(Character.toChars(unicodeChar));
- }
-
- final var label = KeyEvent.keyCodeToString(event.getKeyCode());
- if (label.startsWith("KEYCODE_")) {
- return label.substring(8);
- }
- return label;
- }
-
- /** Determine whether to show key presses by checking one of the key-related objects. */
- private boolean showKeyPresses() {
- return mPressedKeyContainer != null;
- }
-
- /** Determine whether to show rotary input by checking one of the rotary-related objects. */
- private boolean showRotaryInput() {
- return mRotaryInputValueView != null;
- }
-
- /**
- * Converts a dimension in scaled pixel units to integer display pixels.
- */
- private static int applyDimensionSp(int dimensionSp, DisplayMetrics dm) {
- return (int) TypedValue.applyDimension(COMPLEX_UNIT_SP, dimensionSp, dm);
- }
-
- private static class PressedKeyView extends TextView {
-
- private static final ColorFilter sInvertColors = new ColorMatrixColorFilter(new float[]{
- -1.0f, 0, 0, 0, 255, // red
- 0, -1.0f, 0, 0, 255, // green
- 0, 0, -1.0f, 0, 255, // blue
- 0, 0, 0, 1.0f, 0 // alpha
- });
-
- PressedKeyView(Context c, String label) {
- super(c);
-
- final var dm = c.getResources().getDisplayMetrics();
- final int keyViewSidePadding =
- (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, KEY_VIEW_SIDE_PADDING_DP, dm);
- final int keyViewVerticalPadding =
- (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, KEY_VIEW_VERTICAL_PADDING_DP,
- dm);
- final int keyViewMinWidth =
- (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, KEY_VIEW_MIN_WIDTH_DP, dm);
- final int textSize =
- (int) TypedValue.applyDimension(COMPLEX_UNIT_SP, KEY_VIEW_TEXT_SIZE_SP, dm);
-
- setText(label);
- setGravity(Gravity.CENTER);
- setMinimumWidth(keyViewMinWidth);
- setTextSize(textSize);
- setTypeface(Typeface.SANS_SERIF);
- setBackgroundResource(R.drawable.focus_event_pressed_key_background);
- setPaddingRelative(keyViewSidePadding, keyViewVerticalPadding, keyViewSidePadding,
- keyViewVerticalPadding);
-
- setHighlighted(true);
- }
-
- void setHighlighted(boolean isHighlighted) {
- if (isHighlighted) {
- setTextColor(Color.BLACK);
- getBackground().setColorFilter(sInvertColors);
- } else {
- setTextColor(Color.WHITE);
- getBackground().clearColorFilter();
- }
- invalidate();
- }
- }
-
- private static class PressedKeyContainer extends LinearLayout {
-
- private final MarginLayoutParams mPressedKeyLayoutParams;
-
- PressedKeyContainer(Context c) {
- super(c);
-
- final var dm = c.getResources().getDisplayMetrics();
- final int keySeparationMargin =
- (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, KEY_SEPARATION_MARGIN_DP, dm);
-
- final var transition = new LayoutTransition();
- transition.disableTransitionType(LayoutTransition.APPEARING);
- transition.disableTransitionType(LayoutTransition.DISAPPEARING);
- transition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
- transition.setDuration(KEY_TRANSITION_DURATION_MILLIS);
- setLayoutTransition(transition);
-
- mPressedKeyLayoutParams = new MarginLayoutParams(WRAP_CONTENT, WRAP_CONTENT);
- if (getOrientation() == VERTICAL) {
- mPressedKeyLayoutParams.setMargins(0, keySeparationMargin, 0, 0);
- } else {
- mPressedKeyLayoutParams.setMargins(keySeparationMargin, 0, 0, 0);
- }
- }
-
- public void handleKeyPressed(PressedKeyView pressedKeyView) {
- addView(pressedKeyView, getChildCount(), mPressedKeyLayoutParams);
- invalidate();
- }
-
- public void handleKeyRepeat(PressedKeyView repeatedKeyView) {
- // Do nothing for now.
- }
-
- public void handleKeyRelease(PressedKeyView releasedKeyView) {
- releasedKeyView.setHighlighted(false);
- releasedKeyView.clearAnimation();
- releasedKeyView.animate()
- .alpha(0)
- .setDuration(KEY_FADEOUT_DURATION_MILLIS)
- .setInterpolator(new AccelerateInterpolator())
- .withEndAction(this::cleanUpPressedKeyViews)
- .start();
- }
-
- private void cleanUpPressedKeyViews() {
- int numChildrenToRemove = 0;
- for (int i = 0; i < getChildCount(); i++) {
- final View child = getChildAt(i);
- if (child.getAlpha() != 0) {
- break;
- }
- child.setVisibility(View.GONE);
- child.clearAnimation();
- numChildrenToRemove++;
- }
- removeViews(0, numChildrenToRemove);
- invalidate();
- }
- }
-
- // TODO(b/286086154): move RotaryInputGraphView and RotaryInputValueView to a subpackage.
-
- /** Draws the most recent rotary input value and indicates whether the source is active. */
- @VisibleForTesting
- static class RotaryInputValueView extends TextView {
-
- private static final int INACTIVE_TEXT_COLOR = 0xffff00ff;
- private static final int ACTIVE_TEXT_COLOR = 0xff420f28;
- private static final int TEXT_SIZE_SP = 8;
- private static final int SIDE_PADDING_SP = 4;
- /** Determines how long the active status lasts. */
- private static final int ACTIVE_STATUS_DURATION = 250 /* milliseconds */;
- private static final ColorFilter ACTIVE_BACKGROUND_FILTER =
- new ColorMatrixColorFilter(new float[]{
- 0, 0, 0, 0, 255, // red
- 0, 0, 0, 0, 0, // green
- 0, 0, 0, 0, 255, // blue
- 0, 0, 0, 0, 200 // alpha
- });
-
- private final Runnable mUpdateActivityStatusCallback = () -> updateActivityStatus(false);
- private final float mScaledVerticalScrollFactor;
-
- @VisibleForTesting
- RotaryInputValueView(Context c) {
- super(c);
-
- DisplayMetrics dm = mContext.getResources().getDisplayMetrics();
- mScaledVerticalScrollFactor = ViewConfiguration.get(c).getScaledVerticalScrollFactor();
-
- setText(getFormattedValue(0));
- setTextColor(INACTIVE_TEXT_COLOR);
- setTextSize(applyDimensionSp(TEXT_SIZE_SP, dm));
- setPaddingRelative(applyDimensionSp(SIDE_PADDING_SP, dm), 0,
- applyDimensionSp(SIDE_PADDING_SP, dm), 0);
- setTypeface(null, Typeface.BOLD);
- setBackgroundResource(R.drawable.focus_event_rotary_input_background);
- }
-
- void updateValue(float value) {
- removeCallbacks(mUpdateActivityStatusCallback);
-
- setText(getFormattedValue(value * mScaledVerticalScrollFactor));
-
- updateActivityStatus(true);
- postDelayed(mUpdateActivityStatusCallback, ACTIVE_STATUS_DURATION);
- }
-
- @VisibleForTesting
- void updateActivityStatus(boolean active) {
- if (active) {
- setTextColor(ACTIVE_TEXT_COLOR);
- getBackground().setColorFilter(ACTIVE_BACKGROUND_FILTER);
- } else {
- setTextColor(INACTIVE_TEXT_COLOR);
- getBackground().clearColorFilter();
- }
- }
-
- private static String getFormattedValue(float value) {
- return String.format("%s%.1f", value < 0 ? "-" : "+", Math.abs(value));
- }
- }
-
- /**
- * Shows a graph with the rotary input values as a function of time.
- * The graph gets reset if no action is received for a certain amount of time.
- */
- @VisibleForTesting
- static class RotaryInputGraphView extends View {
-
- private static final int FRAME_COLOR = 0xbf741b47;
- private static final int FRAME_WIDTH_SP = 2;
- private static final int FRAME_BORDER_GAP_SP = 10;
- private static final int FRAME_TEXT_SIZE_SP = 10;
- private static final int FRAME_TEXT_OFFSET_SP = 2;
- private static final int GRAPH_COLOR = 0xffff00ff;
- private static final int GRAPH_LINE_WIDTH_SP = 1;
- private static final int GRAPH_POINT_RADIUS_SP = 4;
- private static final long MAX_SHOWN_TIME_INTERVAL = TimeUnit.SECONDS.toMillis(5);
- private static final float DEFAULT_FRAME_CENTER_POSITION = 0;
- private static final int MAX_GRAPH_VALUES_SIZE = 400;
- /** Maximum time between values so that they are considered part of the same gesture. */
- private static final long MAX_GESTURE_TIME = TimeUnit.SECONDS.toMillis(1);
-
- private final DisplayMetrics mDm;
- /**
- * Distance in position units (amount scrolled in display pixels) from the center to the
- * top/bottom frame lines.
- */
- private final float mFrameCenterToBorderDistance;
- private final float mScaledVerticalScrollFactor;
- private final Locale mDefaultLocale;
- private final Paint mFramePaint = new Paint();
- private final Paint mFrameTextPaint = new Paint();
- private final Paint mGraphLinePaint = new Paint();
- private final Paint mGraphPointPaint = new Paint();
-
- private final CyclicBuffer mGraphValues = new CyclicBuffer(MAX_GRAPH_VALUES_SIZE);
- /** Position at which graph values are placed at the center of the graph. */
- private float mFrameCenterPosition = DEFAULT_FRAME_CENTER_POSITION;
-
- @VisibleForTesting
- RotaryInputGraphView(Context c) {
- super(c);
-
- mDm = mContext.getResources().getDisplayMetrics();
- // This makes the center-to-border distance equivalent to the display height, meaning
- // that the total height of the graph is equivalent to 2x the display height.
- mFrameCenterToBorderDistance = mDm.heightPixels;
- mScaledVerticalScrollFactor = ViewConfiguration.get(c).getScaledVerticalScrollFactor();
- mDefaultLocale = Locale.getDefault();
-
- mFramePaint.setColor(FRAME_COLOR);
- mFramePaint.setStrokeWidth(applyDimensionSp(FRAME_WIDTH_SP, mDm));
-
- mFrameTextPaint.setColor(GRAPH_COLOR);
- mFrameTextPaint.setTextSize(applyDimensionSp(FRAME_TEXT_SIZE_SP, mDm));
-
- mGraphLinePaint.setColor(GRAPH_COLOR);
- mGraphLinePaint.setStrokeWidth(applyDimensionSp(GRAPH_LINE_WIDTH_SP, mDm));
- mGraphLinePaint.setStrokeCap(Paint.Cap.ROUND);
- mGraphLinePaint.setStrokeJoin(Paint.Join.ROUND);
-
- mGraphPointPaint.setColor(GRAPH_COLOR);
- mGraphPointPaint.setStrokeWidth(applyDimensionSp(GRAPH_POINT_RADIUS_SP, mDm));
- mGraphPointPaint.setStrokeCap(Paint.Cap.ROUND);
- mGraphPointPaint.setStrokeJoin(Paint.Join.ROUND);
- }
-
- /**
- * Reads new scroll axis value and updates the list accordingly. Old positions are
- * kept at the front (what you would get with getFirst), while the recent positions are
- * kept at the back (what you would get with getLast). Also updates the frame center
- * position to handle out-of-bounds cases.
- */
- void addValue(float scrollAxisValue, long eventTime) {
- // Remove values that are too old.
- while (mGraphValues.getSize() > 0
- && (eventTime - mGraphValues.getFirst().mTime) > MAX_SHOWN_TIME_INTERVAL) {
- mGraphValues.removeFirst();
- }
-
- // If there are no recent values, reset the frame center.
- if (mGraphValues.getSize() == 0) {
- mFrameCenterPosition = DEFAULT_FRAME_CENTER_POSITION;
- }
-
- // Handle new value. We multiply the scroll axis value by the scaled scroll factor to
- // get the amount of pixels to be scrolled. We also compute the accumulated position
- // by adding the current value to the last one (if not empty).
- final float displacement = scrollAxisValue * mScaledVerticalScrollFactor;
- final float prevPos = (mGraphValues.getSize() == 0 ? 0 : mGraphValues.getLast().mPos);
- final float pos = prevPos + displacement;
-
- mGraphValues.add(pos, eventTime);
-
- // The difference between the distance of the most recent position from the center
- // frame (pos - mFrameCenterPosition) and the maximum allowed distance from the center
- // frame (mFrameCenterToBorderDistance).
- final float verticalDiff = Math.abs(pos - mFrameCenterPosition)
- - mFrameCenterToBorderDistance;
- // If needed, translate frame.
- if (verticalDiff > 0) {
- final int sign = pos - mFrameCenterPosition < 0 ? -1 : 1;
- // Here, we update the center frame position by the exact amount needed for us to
- // stay within the maximum allowed distance from the center frame.
- mFrameCenterPosition += sign * verticalDiff;
- }
-
- // Redraw canvas.
- invalidate();
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- // Note: vertical coordinates in Canvas go from top to bottom,
- // that is bottomY > middleY > topY.
- final int verticalMargin = applyDimensionSp(FRAME_BORDER_GAP_SP, mDm);
- final int topY = verticalMargin;
- final int bottomY = getHeight() - verticalMargin;
- final int middleY = (topY + bottomY) / 2;
-
- // Note: horizontal coordinates in Canvas go from left to right,
- // that is rightX > leftX.
- final int leftX = 0;
- final int rightX = getWidth();
-
- // Draw the frame, which includes 3 lines that show the maximum,
- // minimum and middle positions of the graph.
- canvas.drawLine(leftX, topY, rightX, topY, mFramePaint);
- canvas.drawLine(leftX, middleY, rightX, middleY, mFramePaint);
- canvas.drawLine(leftX, bottomY, rightX, bottomY, mFramePaint);
-
- // Draw the position that each frame line corresponds to.
- final int frameTextOffset = applyDimensionSp(FRAME_TEXT_OFFSET_SP, mDm);
- canvas.drawText(
- String.format(mDefaultLocale, "%.1f",
- mFrameCenterPosition + mFrameCenterToBorderDistance),
- leftX,
- topY - frameTextOffset, mFrameTextPaint
- );
- canvas.drawText(
- String.format(mDefaultLocale, "%.1f", mFrameCenterPosition),
- leftX,
- middleY - frameTextOffset, mFrameTextPaint
- );
- canvas.drawText(
- String.format(mDefaultLocale, "%.1f",
- mFrameCenterPosition - mFrameCenterToBorderDistance),
- leftX,
- bottomY - frameTextOffset, mFrameTextPaint
- );
-
- // If there are no graph values to be drawn, stop here.
- if (mGraphValues.getSize() == 0) {
- return;
- }
-
- // Draw the graph using the times and positions.
- // We start at the most recent value (which should be drawn at the right) and move
- // to the older values (which should be drawn to the left of more recent ones). Negative
- // indices are handled by circuling back to the end of the buffer.
- final long mostRecentTime = mGraphValues.getLast().mTime;
- float prevCoordX = 0;
- float prevCoordY = 0;
- float prevAge = 0;
- for (Iterator<GraphValue> iter = mGraphValues.reverseIterator(); iter.hasNext();) {
- final GraphValue value = iter.next();
-
- final int age = (int) (mostRecentTime - value.mTime);
- final float pos = value.mPos;
-
- // We get the horizontal coordinate in time units from left to right with
- // (MAX_SHOWN_TIME_INTERVAL - age). Then, we rescale it to match the canvas
- // units by dividing it by the time-domain length (MAX_SHOWN_TIME_INTERVAL)
- // and by multiplying it by the canvas length (rightX - leftX). Finally, we
- // offset the coordinate by adding it to leftX.
- final float coordX = leftX + ((float) (MAX_SHOWN_TIME_INTERVAL - age)
- / MAX_SHOWN_TIME_INTERVAL) * (rightX - leftX);
-
- // We get the vertical coordinate in position units from middle to top with
- // (pos - mFrameCenterPosition). Then, we rescale it to match the canvas
- // units by dividing it by half of the position-domain length
- // (mFrameCenterToBorderDistance) and by multiplying it by half of the canvas
- // length (middleY - topY). Finally, we offset the coordinate by subtracting
- // it from middleY (we can't "add" here because the coordinate grows from top
- // to bottom).
- final float coordY = middleY - ((pos - mFrameCenterPosition)
- / mFrameCenterToBorderDistance) * (middleY - topY);
-
- // Draw a point for this value.
- canvas.drawPoint(coordX, coordY, mGraphPointPaint);
-
- // If this value is part of the same gesture as the previous one, draw a line
- // between them. We ignore the first value (with age = 0).
- if (age != 0 && (age - prevAge) <= MAX_GESTURE_TIME) {
- canvas.drawLine(prevCoordX, prevCoordY, coordX, coordY, mGraphLinePaint);
- }
-
- prevCoordX = coordX;
- prevCoordY = coordY;
- prevAge = age;
- }
- }
-
- @VisibleForTesting
- float getFrameCenterPosition() {
- return mFrameCenterPosition;
- }
-
- /**
- * Holds data needed to draw each entry in the graph.
- */
- private static class GraphValue {
- /** Position. */
- float mPos;
- /** Time when this value was added. */
- long mTime;
-
- GraphValue(float pos, long time) {
- this.mPos = pos;
- this.mTime = time;
- }
- }
-
- /**
- * Holds the graph values as a cyclic buffer. It has a fixed capacity, and it replaces the
- * old values with new ones to avoid creating new objects.
- */
- private static class CyclicBuffer {
- private final GraphValue[] mValues;
- private final int mCapacity;
- private int mSize = 0;
- private int mLastIndex = 0;
-
- // The iteration index and counter are here to make it easier to reset them.
- /** Determines the value currently pointed by the iterator. */
- private int mIteratorIndex;
- /** Counts how many values have been iterated through. */
- private int mIteratorCount;
-
- /** Used traverse the values in reverse order. */
- private final Iterator<GraphValue> mReverseIterator = new Iterator<GraphValue>() {
- @Override
- public boolean hasNext() {
- return mIteratorCount <= mSize;
- }
-
- @Override
- public GraphValue next() {
- // Returns the value currently pointed by the iterator and moves the iterator to
- // the previous one.
- mIteratorCount++;
- return mValues[(mIteratorIndex-- + mCapacity) % mCapacity];
- }
- };
-
- CyclicBuffer(int capacity) {
- mCapacity = capacity;
- mValues = new GraphValue[capacity];
- }
-
- /**
- * Add new graph value. If there is an existing object, we replace its data with the
- * new one. With this, we re-use old objects instead of creating new ones.
- */
- void add(float pos, long time) {
- mLastIndex = (mLastIndex + 1) % mCapacity;
- if (mValues[mLastIndex] == null) {
- mValues[mLastIndex] = new GraphValue(pos, time);
- } else {
- final GraphValue oldValue = mValues[mLastIndex];
- oldValue.mPos = pos;
- oldValue.mTime = time;
- }
-
- // If needed, account for new value in the buffer size.
- if (mSize != mCapacity) {
- mSize++;
- }
- }
-
- int getSize() {
- return mSize;
- }
-
- GraphValue getFirst() {
- final int distanceBetweenLastAndFirst = (mCapacity - mSize) + 1;
- final int firstIndex = (mLastIndex + distanceBetweenLastAndFirst) % mCapacity;
- return mValues[firstIndex];
- }
-
- GraphValue getLast() {
- return mValues[mLastIndex];
- }
-
- void removeFirst() {
- mSize--;
- }
-
- /** Returns an iterator pointing at the last value. */
- Iterator<GraphValue> reverseIterator() {
- mIteratorIndex = mLastIndex;
- mIteratorCount = 1;
- return mReverseIterator;
- }
- }
- }
-}
diff --git a/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java b/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
index 2ede56d..a2c8748 100644
--- a/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
+++ b/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
@@ -62,10 +62,10 @@
mWindowHandle.ownerUid = uid;
mWindowHandle.scaleFactor = 1.0f;
mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface's bounds */);
- mWindowHandle.inputConfig =
- InputConfig.NOT_FOCUSABLE | InputConfig.SPY | InputConfig.TRUSTED_OVERLAY;
+ mWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE | InputConfig.SPY;
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ mWindowHandle.setTrustedOverlay(t, mInputSurface, true);
t.setInputWindowInfo(mInputSurface, mWindowHandle);
t.setLayer(mInputSurface, InputManagerService.INPUT_OVERLAY_LAYER_GESTURE_MONITOR);
t.setPosition(mInputSurface, 0, 0);
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 6b399de..2533e02 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -117,6 +117,7 @@
import com.android.server.LocalServices;
import com.android.server.Watchdog;
import com.android.server.input.InputManagerInternal.LidSwitchCallback;
+import com.android.server.input.debug.FocusEventDebugView;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.policy.WindowManagerPolicy;
diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
index 0eb620f..8580b96 100644
--- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java
+++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
@@ -71,6 +71,7 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.inputmethod.InputMethodSubtypeHandle;
import com.android.internal.messages.nano.SystemMessageProto;
import com.android.internal.notification.SystemNotificationChannels;
@@ -99,7 +100,7 @@
*
* @hide
*/
-final class KeyboardLayoutManager implements InputManager.InputDeviceListener {
+class KeyboardLayoutManager implements InputManager.InputDeviceListener {
private static final String TAG = "KeyboardLayoutManager";
@@ -1245,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());
}
@@ -1295,7 +1302,8 @@
}
@SuppressLint("MissingPermission")
- private List<ImeInfo> getImeInfoListForLayoutMapping() {
+ @VisibleForTesting
+ public List<ImeInfo> getImeInfoListForLayoutMapping() {
List<ImeInfo> imeInfoList = new ArrayList<>();
UserManager userManager = Objects.requireNonNull(
mContext.getSystemService(UserManager.class));
@@ -1402,7 +1410,8 @@
}
}
- private static class ImeInfo {
+ @VisibleForTesting
+ public static class ImeInfo {
@UserIdInt int mUserId;
@NonNull InputMethodSubtypeHandle mImeSubtypeHandle;
@Nullable InputMethodSubtype mImeSubtype;
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/input/FocusEventDebugGlobalMonitor.java b/services/core/java/com/android/server/input/debug/FocusEventDebugGlobalMonitor.java
similarity index 94%
rename from services/core/java/com/android/server/input/FocusEventDebugGlobalMonitor.java
rename to services/core/java/com/android/server/input/debug/FocusEventDebugGlobalMonitor.java
index 67c221f..2b21e49 100644
--- a/services/core/java/com/android/server/input/FocusEventDebugGlobalMonitor.java
+++ b/services/core/java/com/android/server/input/debug/FocusEventDebugGlobalMonitor.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.input;
+package com.android.server.input.debug;
import android.view.Display;
import android.view.InputEvent;
@@ -22,6 +22,7 @@
import android.view.MotionEvent;
import com.android.server.UiThread;
+import com.android.server.input.InputManagerService;
/**
* Receives input events before they are dispatched and reports them to FocusEventDebugView.
diff --git a/services/core/java/com/android/server/input/debug/FocusEventDebugView.java b/services/core/java/com/android/server/input/debug/FocusEventDebugView.java
new file mode 100644
index 0000000..6eec0de
--- /dev/null
+++ b/services/core/java/com/android/server/input/debug/FocusEventDebugView.java
@@ -0,0 +1,466 @@
+/*
+ * Copyright 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.input.debug;
+
+import static android.util.TypedValue.COMPLEX_UNIT_DIP;
+import static android.util.TypedValue.COMPLEX_UNIT_SP;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
+import android.animation.LayoutTransition;
+import android.annotation.AnyThread;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Typeface;
+import android.util.DisplayMetrics;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.InputDevice;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.RoundedCorner;
+import android.view.View;
+import android.view.WindowInsets;
+import android.view.animation.AccelerateInterpolator;
+import android.widget.HorizontalScrollView;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.input.InputManagerService;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Supplier;
+
+/**
+ * Displays focus events, such as physical keyboard KeyEvents and non-pointer MotionEvents on
+ * the screen.
+ */
+public class FocusEventDebugView extends RelativeLayout {
+
+ private static final String TAG = FocusEventDebugView.class.getSimpleName();
+
+ private static final int KEY_FADEOUT_DURATION_MILLIS = 1000;
+ private static final int KEY_TRANSITION_DURATION_MILLIS = 100;
+
+ private static final int OUTER_PADDING_DP = 16;
+ private static final int KEY_SEPARATION_MARGIN_DP = 16;
+ private static final int KEY_VIEW_SIDE_PADDING_DP = 16;
+ private static final int KEY_VIEW_VERTICAL_PADDING_DP = 8;
+ private static final int KEY_VIEW_MIN_WIDTH_DP = 32;
+ private static final int KEY_VIEW_TEXT_SIZE_SP = 12;
+ private static final double ROTATY_GRAPH_HEIGHT_FRACTION = 0.5;
+
+ private final InputManagerService mService;
+ private final int mOuterPadding;
+ private final DisplayMetrics mDm;
+
+ // Tracks all keys that are currently pressed/down.
+ private final Map<Pair<Integer /*deviceId*/, Integer /*scanCode*/>, PressedKeyView>
+ mPressedKeys = new HashMap<>();
+
+ @Nullable
+ private FocusEventDebugGlobalMonitor mFocusEventDebugGlobalMonitor;
+ @Nullable
+ private PressedKeyContainer mPressedKeyContainer;
+ @Nullable
+ private PressedKeyContainer mPressedModifierContainer;
+ private final Supplier<RotaryInputValueView> mRotaryInputValueViewFactory;
+ @Nullable
+ private RotaryInputValueView mRotaryInputValueView;
+ private final Supplier<RotaryInputGraphView> mRotaryInputGraphViewFactory;
+ @Nullable
+ private RotaryInputGraphView mRotaryInputGraphView;
+
+ @VisibleForTesting
+ FocusEventDebugView(Context c, InputManagerService service,
+ Supplier<RotaryInputValueView> rotaryInputValueViewFactory,
+ Supplier<RotaryInputGraphView> rotaryInputGraphViewFactory) {
+ super(c);
+ setFocusableInTouchMode(true);
+
+ mService = service;
+ mRotaryInputValueViewFactory = rotaryInputValueViewFactory;
+ mRotaryInputGraphViewFactory = rotaryInputGraphViewFactory;
+ mDm = mContext.getResources().getDisplayMetrics();
+ mOuterPadding = (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, OUTER_PADDING_DP, mDm);
+ }
+
+ public FocusEventDebugView(Context c, InputManagerService service) {
+ this(c, service, () -> new RotaryInputValueView(c), () -> new RotaryInputGraphView(c));
+ }
+
+ @Override
+ public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ int paddingBottom = 0;
+
+ final RoundedCorner bottomLeft =
+ insets.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT);
+ if (bottomLeft != null && !insets.isRound()) {
+ paddingBottom = bottomLeft.getRadius();
+ }
+
+ final RoundedCorner bottomRight =
+ insets.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT);
+ if (bottomRight != null && !insets.isRound()) {
+ paddingBottom = Math.max(paddingBottom, bottomRight.getRadius());
+ }
+
+ if (insets.getDisplayCutout() != null) {
+ paddingBottom =
+ Math.max(paddingBottom, insets.getDisplayCutout().getSafeInsetBottom());
+ }
+
+ setPadding(mOuterPadding, mOuterPadding, mOuterPadding, mOuterPadding + paddingBottom);
+ setClipToPadding(false);
+ invalidate();
+ return super.onApplyWindowInsets(insets);
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ handleKeyEvent(event);
+ return super.dispatchKeyEvent(event);
+ }
+
+ /** Determines whether to show the key presses visualization. */
+ @AnyThread
+ public void updateShowKeyPresses(boolean enabled) {
+ post(() -> handleUpdateShowKeyPresses(enabled));
+ }
+
+ /** Determines whether to show the rotary input visualization. */
+ @AnyThread
+ public void updateShowRotaryInput(boolean enabled) {
+ post(() -> handleUpdateShowRotaryInput(enabled));
+ }
+
+ private void handleUpdateShowKeyPresses(boolean enabled) {
+ if (enabled == showKeyPresses()) {
+ return;
+ }
+
+ if (!enabled) {
+ removeView(mPressedKeyContainer);
+ mPressedKeyContainer = null;
+ removeView(mPressedModifierContainer);
+ mPressedModifierContainer = null;
+ return;
+ }
+
+ mPressedKeyContainer = new PressedKeyContainer(mContext);
+ mPressedKeyContainer.setOrientation(LinearLayout.HORIZONTAL);
+ mPressedKeyContainer.setGravity(Gravity.RIGHT | Gravity.BOTTOM);
+ mPressedKeyContainer.setLayoutDirection(LAYOUT_DIRECTION_LTR);
+ final var scroller = new HorizontalScrollView(mContext);
+ scroller.addView(mPressedKeyContainer);
+ scroller.setHorizontalScrollBarEnabled(false);
+ scroller.addOnLayoutChangeListener(
+ (view, l, t, r, b, ol, ot, or, ob) -> scroller.fullScroll(View.FOCUS_RIGHT));
+ scroller.setHorizontalFadingEdgeEnabled(true);
+ LayoutParams scrollerLayoutParams = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
+ scrollerLayoutParams.addRule(ALIGN_PARENT_BOTTOM);
+ scrollerLayoutParams.addRule(ALIGN_PARENT_RIGHT);
+ addView(scroller, scrollerLayoutParams);
+
+ mPressedModifierContainer = new PressedKeyContainer(mContext);
+ mPressedModifierContainer.setOrientation(LinearLayout.VERTICAL);
+ mPressedModifierContainer.setGravity(Gravity.LEFT | Gravity.BOTTOM);
+ LayoutParams modifierLayoutParams = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
+ modifierLayoutParams.addRule(ALIGN_PARENT_BOTTOM);
+ modifierLayoutParams.addRule(ALIGN_PARENT_LEFT);
+ modifierLayoutParams.addRule(LEFT_OF, scroller.getId());
+ addView(mPressedModifierContainer, modifierLayoutParams);
+ }
+
+ @VisibleForTesting
+ void handleUpdateShowRotaryInput(boolean enabled) {
+ if (enabled == showRotaryInput()) {
+ return;
+ }
+
+ if (!enabled) {
+ mFocusEventDebugGlobalMonitor.dispose();
+ mFocusEventDebugGlobalMonitor = null;
+ removeView(mRotaryInputValueView);
+ mRotaryInputValueView = null;
+ removeView(mRotaryInputGraphView);
+ mRotaryInputGraphView = null;
+ return;
+ }
+
+ mFocusEventDebugGlobalMonitor = new FocusEventDebugGlobalMonitor(this, mService);
+
+ mRotaryInputValueView = mRotaryInputValueViewFactory.get();
+ LayoutParams valueLayoutParams = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
+ valueLayoutParams.addRule(CENTER_HORIZONTAL);
+ valueLayoutParams.addRule(ALIGN_PARENT_BOTTOM);
+ addView(mRotaryInputValueView, valueLayoutParams);
+
+ mRotaryInputGraphView = mRotaryInputGraphViewFactory.get();
+ LayoutParams graphLayoutParams = new LayoutParams(LayoutParams.MATCH_PARENT,
+ (int) (ROTATY_GRAPH_HEIGHT_FRACTION * mDm.heightPixels));
+ graphLayoutParams.addRule(CENTER_IN_PARENT);
+ addView(mRotaryInputGraphView, graphLayoutParams);
+ }
+
+ /** Report a key event to the debug view. */
+ @AnyThread
+ public void reportKeyEvent(KeyEvent event) {
+ post(() -> handleKeyEvent(KeyEvent.obtain((KeyEvent) event)));
+ }
+
+ /** Report a motion event to the debug view. */
+ @AnyThread
+ public void reportMotionEvent(MotionEvent event) {
+ if (event.getSource() != InputDevice.SOURCE_ROTARY_ENCODER) {
+ return;
+ }
+
+ post(() -> handleRotaryInput(MotionEvent.obtain((MotionEvent) event)));
+ }
+
+ private void handleKeyEvent(KeyEvent keyEvent) {
+ if (!showKeyPresses()) {
+ return;
+ }
+
+ final var identifier = new Pair<>(keyEvent.getDeviceId(), keyEvent.getScanCode());
+ final var container = KeyEvent.isModifierKey(keyEvent.getKeyCode())
+ ? mPressedModifierContainer
+ : mPressedKeyContainer;
+ PressedKeyView pressedKeyView = mPressedKeys.get(identifier);
+ switch (keyEvent.getAction()) {
+ case KeyEvent.ACTION_DOWN: {
+ if (pressedKeyView != null) {
+ if (keyEvent.getRepeatCount() == 0) {
+ Slog.w(TAG, "Got key down for "
+ + KeyEvent.keyCodeToString(keyEvent.getKeyCode())
+ + " that was already tracked as being down.");
+ break;
+ }
+ container.handleKeyRepeat(pressedKeyView);
+ break;
+ }
+
+ pressedKeyView = new PressedKeyView(mContext, getLabel(keyEvent));
+ mPressedKeys.put(identifier, pressedKeyView);
+ container.handleKeyPressed(pressedKeyView);
+ break;
+ }
+ case KeyEvent.ACTION_UP: {
+ if (pressedKeyView == null) {
+ Slog.w(TAG, "Got key up for " + KeyEvent.keyCodeToString(keyEvent.getKeyCode())
+ + " that was not tracked as being down.");
+ break;
+ }
+ mPressedKeys.remove(identifier);
+ container.handleKeyRelease(pressedKeyView);
+ break;
+ }
+ default:
+ break;
+ }
+ keyEvent.recycle();
+ }
+
+ @VisibleForTesting
+ void handleRotaryInput(MotionEvent motionEvent) {
+ if (!showRotaryInput()) {
+ return;
+ }
+
+ float scrollAxisValue = motionEvent.getAxisValue(MotionEvent.AXIS_SCROLL);
+ mRotaryInputValueView.updateValue(scrollAxisValue);
+ mRotaryInputGraphView.addValue(scrollAxisValue, motionEvent.getEventTime());
+
+ motionEvent.recycle();
+ }
+
+ private static String getLabel(KeyEvent event) {
+ switch (event.getKeyCode()) {
+ case KeyEvent.KEYCODE_SPACE:
+ return "\u2423";
+ case KeyEvent.KEYCODE_TAB:
+ return "\u21e5";
+ case KeyEvent.KEYCODE_ENTER:
+ case KeyEvent.KEYCODE_NUMPAD_ENTER:
+ return "\u23CE";
+ case KeyEvent.KEYCODE_DEL:
+ return "\u232B";
+ case KeyEvent.KEYCODE_FORWARD_DEL:
+ return "\u2326";
+ case KeyEvent.KEYCODE_ESCAPE:
+ return "ESC";
+ case KeyEvent.KEYCODE_DPAD_UP:
+ return "\u2191";
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ return "\u2193";
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ return "\u2190";
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ return "\u2192";
+ case KeyEvent.KEYCODE_DPAD_UP_RIGHT:
+ return "\u2197";
+ case KeyEvent.KEYCODE_DPAD_UP_LEFT:
+ return "\u2196";
+ case KeyEvent.KEYCODE_DPAD_DOWN_RIGHT:
+ return "\u2198";
+ case KeyEvent.KEYCODE_DPAD_DOWN_LEFT:
+ return "\u2199";
+ default:
+ break;
+ }
+
+ final int unicodeChar = event.getUnicodeChar();
+ if (unicodeChar != 0) {
+ return new String(Character.toChars(unicodeChar));
+ }
+
+ final var label = KeyEvent.keyCodeToString(event.getKeyCode());
+ if (label.startsWith("KEYCODE_")) {
+ return label.substring(8);
+ }
+ return label;
+ }
+
+ /** Determine whether to show key presses by checking one of the key-related objects. */
+ private boolean showKeyPresses() {
+ return mPressedKeyContainer != null;
+ }
+
+ /** Determine whether to show rotary input by checking one of the rotary-related objects. */
+ private boolean showRotaryInput() {
+ return mRotaryInputValueView != null;
+ }
+
+ private static class PressedKeyView extends TextView {
+
+ private static final ColorFilter sInvertColors = new ColorMatrixColorFilter(new float[]{
+ -1.0f, 0, 0, 0, 255, // red
+ 0, -1.0f, 0, 0, 255, // green
+ 0, 0, -1.0f, 0, 255, // blue
+ 0, 0, 0, 1.0f, 0 // alpha
+ });
+
+ PressedKeyView(Context c, String label) {
+ super(c);
+
+ final var dm = c.getResources().getDisplayMetrics();
+ final int keyViewSidePadding =
+ (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, KEY_VIEW_SIDE_PADDING_DP, dm);
+ final int keyViewVerticalPadding =
+ (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, KEY_VIEW_VERTICAL_PADDING_DP,
+ dm);
+ final int keyViewMinWidth =
+ (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, KEY_VIEW_MIN_WIDTH_DP, dm);
+ final int textSize =
+ (int) TypedValue.applyDimension(COMPLEX_UNIT_SP, KEY_VIEW_TEXT_SIZE_SP, dm);
+
+ setText(label);
+ setGravity(Gravity.CENTER);
+ setMinimumWidth(keyViewMinWidth);
+ setTextSize(textSize);
+ setTypeface(Typeface.SANS_SERIF);
+ setBackgroundResource(R.drawable.focus_event_pressed_key_background);
+ setPaddingRelative(keyViewSidePadding, keyViewVerticalPadding, keyViewSidePadding,
+ keyViewVerticalPadding);
+
+ setHighlighted(true);
+ }
+
+ void setHighlighted(boolean isHighlighted) {
+ if (isHighlighted) {
+ setTextColor(Color.BLACK);
+ getBackground().setColorFilter(sInvertColors);
+ } else {
+ setTextColor(Color.WHITE);
+ getBackground().clearColorFilter();
+ }
+ invalidate();
+ }
+ }
+
+ private static class PressedKeyContainer extends LinearLayout {
+
+ private final MarginLayoutParams mPressedKeyLayoutParams;
+
+ PressedKeyContainer(Context c) {
+ super(c);
+
+ final var dm = c.getResources().getDisplayMetrics();
+ final int keySeparationMargin =
+ (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, KEY_SEPARATION_MARGIN_DP, dm);
+
+ final var transition = new LayoutTransition();
+ transition.disableTransitionType(LayoutTransition.APPEARING);
+ transition.disableTransitionType(LayoutTransition.DISAPPEARING);
+ transition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
+ transition.setDuration(KEY_TRANSITION_DURATION_MILLIS);
+ setLayoutTransition(transition);
+
+ mPressedKeyLayoutParams = new MarginLayoutParams(WRAP_CONTENT, WRAP_CONTENT);
+ if (getOrientation() == VERTICAL) {
+ mPressedKeyLayoutParams.setMargins(0, keySeparationMargin, 0, 0);
+ } else {
+ mPressedKeyLayoutParams.setMargins(keySeparationMargin, 0, 0, 0);
+ }
+ }
+
+ public void handleKeyPressed(PressedKeyView pressedKeyView) {
+ addView(pressedKeyView, getChildCount(), mPressedKeyLayoutParams);
+ invalidate();
+ }
+
+ public void handleKeyRepeat(PressedKeyView repeatedKeyView) {
+ // Do nothing for now.
+ }
+
+ public void handleKeyRelease(PressedKeyView releasedKeyView) {
+ releasedKeyView.setHighlighted(false);
+ releasedKeyView.clearAnimation();
+ releasedKeyView.animate()
+ .alpha(0)
+ .setDuration(KEY_FADEOUT_DURATION_MILLIS)
+ .setInterpolator(new AccelerateInterpolator())
+ .withEndAction(this::cleanUpPressedKeyViews)
+ .start();
+ }
+
+ private void cleanUpPressedKeyViews() {
+ int numChildrenToRemove = 0;
+ for (int i = 0; i < getChildCount(); i++) {
+ final View child = getChildAt(i);
+ if (child.getAlpha() != 0) {
+ break;
+ }
+ child.setVisibility(View.GONE);
+ child.clearAnimation();
+ numChildrenToRemove++;
+ }
+ removeViews(0, numChildrenToRemove);
+ invalidate();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/input/debug/RotaryInputGraphView.java b/services/core/java/com/android/server/input/debug/RotaryInputGraphView.java
new file mode 100644
index 0000000..95635d9
--- /dev/null
+++ b/services/core/java/com/android/server/input/debug/RotaryInputGraphView.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright 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.input.debug;
+
+import static android.util.TypedValue.COMPLEX_UNIT_SP;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.ViewConfiguration;
+
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Shows a graph with the rotary input values as a function of time.
+ * The graph gets reset if no action is received for a certain amount of time.
+ */
+public class RotaryInputGraphView extends View {
+
+ private static final int FRAME_COLOR = 0xbf741b47;
+ private static final int FRAME_WIDTH_SP = 2;
+ private static final int FRAME_BORDER_GAP_SP = 10;
+ private static final int FRAME_TEXT_SIZE_SP = 10;
+ private static final int FRAME_TEXT_OFFSET_SP = 2;
+ private static final int GRAPH_COLOR = 0xffff00ff;
+ private static final int GRAPH_LINE_WIDTH_SP = 1;
+ private static final int GRAPH_POINT_RADIUS_SP = 4;
+ private static final long MAX_SHOWN_TIME_INTERVAL = TimeUnit.SECONDS.toMillis(5);
+ private static final float DEFAULT_FRAME_CENTER_POSITION = 0;
+ private static final int MAX_GRAPH_VALUES_SIZE = 400;
+ /** Maximum time between values so that they are considered part of the same gesture. */
+ private static final long MAX_GESTURE_TIME = TimeUnit.SECONDS.toMillis(1);
+
+ private final DisplayMetrics mDm;
+ /**
+ * Distance in position units (amount scrolled in display pixels) from the center to the
+ * top/bottom frame lines.
+ */
+ private final float mFrameCenterToBorderDistance;
+ private final float mScaledVerticalScrollFactor;
+ private final Locale mDefaultLocale = Locale.getDefault();
+ private final Paint mFramePaint = new Paint();
+ private final Paint mFrameTextPaint = new Paint();
+ private final Paint mGraphLinePaint = new Paint();
+ private final Paint mGraphPointPaint = new Paint();
+
+ private final CyclicBuffer mGraphValues = new CyclicBuffer(MAX_GRAPH_VALUES_SIZE);
+ /** Position at which graph values are placed at the center of the graph. */
+ private float mFrameCenterPosition = DEFAULT_FRAME_CENTER_POSITION;
+
+ public RotaryInputGraphView(Context c) {
+ super(c);
+
+ mDm = mContext.getResources().getDisplayMetrics();
+ // This makes the center-to-border distance equivalent to the display height, meaning
+ // that the total height of the graph is equivalent to 2x the display height.
+ mFrameCenterToBorderDistance = mDm.heightPixels;
+ mScaledVerticalScrollFactor = ViewConfiguration.get(c).getScaledVerticalScrollFactor();
+
+ mFramePaint.setColor(FRAME_COLOR);
+ mFramePaint.setStrokeWidth(applyDimensionSp(FRAME_WIDTH_SP, mDm));
+
+ mFrameTextPaint.setColor(GRAPH_COLOR);
+ mFrameTextPaint.setTextSize(applyDimensionSp(FRAME_TEXT_SIZE_SP, mDm));
+
+ mGraphLinePaint.setColor(GRAPH_COLOR);
+ mGraphLinePaint.setStrokeWidth(applyDimensionSp(GRAPH_LINE_WIDTH_SP, mDm));
+ mGraphLinePaint.setStrokeCap(Paint.Cap.ROUND);
+ mGraphLinePaint.setStrokeJoin(Paint.Join.ROUND);
+
+ mGraphPointPaint.setColor(GRAPH_COLOR);
+ mGraphPointPaint.setStrokeWidth(applyDimensionSp(GRAPH_POINT_RADIUS_SP, mDm));
+ mGraphPointPaint.setStrokeCap(Paint.Cap.ROUND);
+ mGraphPointPaint.setStrokeJoin(Paint.Join.ROUND);
+ }
+
+ /**
+ * Reads new scroll axis value and updates the list accordingly. Old positions are
+ * kept at the front (what you would get with getFirst), while the recent positions are
+ * kept at the back (what you would get with getLast). Also updates the frame center
+ * position to handle out-of-bounds cases.
+ */
+ public void addValue(float scrollAxisValue, long eventTime) {
+ // Remove values that are too old.
+ while (mGraphValues.getSize() > 0
+ && (eventTime - mGraphValues.getFirst().mTime) > MAX_SHOWN_TIME_INTERVAL) {
+ mGraphValues.removeFirst();
+ }
+
+ // If there are no recent values, reset the frame center.
+ if (mGraphValues.getSize() == 0) {
+ mFrameCenterPosition = DEFAULT_FRAME_CENTER_POSITION;
+ }
+
+ // Handle new value. We multiply the scroll axis value by the scaled scroll factor to
+ // get the amount of pixels to be scrolled. We also compute the accumulated position
+ // by adding the current value to the last one (if not empty).
+ final float displacement = scrollAxisValue * mScaledVerticalScrollFactor;
+ final float prevPos = (mGraphValues.getSize() == 0 ? 0 : mGraphValues.getLast().mPos);
+ final float pos = prevPos + displacement;
+
+ mGraphValues.add(pos, eventTime);
+
+ // The difference between the distance of the most recent position from the center
+ // frame (pos - mFrameCenterPosition) and the maximum allowed distance from the center
+ // frame (mFrameCenterToBorderDistance).
+ final float verticalDiff = Math.abs(pos - mFrameCenterPosition)
+ - mFrameCenterToBorderDistance;
+ // If needed, translate frame.
+ if (verticalDiff > 0) {
+ final int sign = pos - mFrameCenterPosition < 0 ? -1 : 1;
+ // Here, we update the center frame position by the exact amount needed for us to
+ // stay within the maximum allowed distance from the center frame.
+ mFrameCenterPosition += sign * verticalDiff;
+ }
+
+ // Redraw canvas.
+ invalidate();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ // Note: vertical coordinates in Canvas go from top to bottom,
+ // that is bottomY > middleY > topY.
+ final int verticalMargin = applyDimensionSp(FRAME_BORDER_GAP_SP, mDm);
+ final int topY = verticalMargin;
+ final int bottomY = getHeight() - verticalMargin;
+ final int middleY = (topY + bottomY) / 2;
+
+ // Note: horizontal coordinates in Canvas go from left to right,
+ // that is rightX > leftX.
+ final int leftX = 0;
+ final int rightX = getWidth();
+
+ // Draw the frame, which includes 3 lines that show the maximum,
+ // minimum and middle positions of the graph.
+ canvas.drawLine(leftX, topY, rightX, topY, mFramePaint);
+ canvas.drawLine(leftX, middleY, rightX, middleY, mFramePaint);
+ canvas.drawLine(leftX, bottomY, rightX, bottomY, mFramePaint);
+
+ // Draw the position that each frame line corresponds to.
+ final int frameTextOffset = applyDimensionSp(FRAME_TEXT_OFFSET_SP, mDm);
+ canvas.drawText(
+ String.format(mDefaultLocale, "%.1f",
+ mFrameCenterPosition + mFrameCenterToBorderDistance),
+ leftX,
+ topY - frameTextOffset, mFrameTextPaint
+ );
+ canvas.drawText(
+ String.format(mDefaultLocale, "%.1f", mFrameCenterPosition),
+ leftX,
+ middleY - frameTextOffset, mFrameTextPaint
+ );
+ canvas.drawText(
+ String.format(mDefaultLocale, "%.1f",
+ mFrameCenterPosition - mFrameCenterToBorderDistance),
+ leftX,
+ bottomY - frameTextOffset, mFrameTextPaint
+ );
+
+ // If there are no graph values to be drawn, stop here.
+ if (mGraphValues.getSize() == 0) {
+ return;
+ }
+
+ // Draw the graph using the times and positions.
+ // We start at the most recent value (which should be drawn at the right) and move
+ // to the older values (which should be drawn to the left of more recent ones). Negative
+ // indices are handled by circuling back to the end of the buffer.
+ final long mostRecentTime = mGraphValues.getLast().mTime;
+ float prevCoordX = 0;
+ float prevCoordY = 0;
+ float prevAge = 0;
+ for (Iterator<GraphValue> iter = mGraphValues.reverseIterator(); iter.hasNext();) {
+ final GraphValue value = iter.next();
+
+ final int age = (int) (mostRecentTime - value.mTime);
+ final float pos = value.mPos;
+
+ // We get the horizontal coordinate in time units from left to right with
+ // (MAX_SHOWN_TIME_INTERVAL - age). Then, we rescale it to match the canvas
+ // units by dividing it by the time-domain length (MAX_SHOWN_TIME_INTERVAL)
+ // and by multiplying it by the canvas length (rightX - leftX). Finally, we
+ // offset the coordinate by adding it to leftX.
+ final float coordX = leftX + ((float) (MAX_SHOWN_TIME_INTERVAL - age)
+ / MAX_SHOWN_TIME_INTERVAL) * (rightX - leftX);
+
+ // We get the vertical coordinate in position units from middle to top with
+ // (pos - mFrameCenterPosition). Then, we rescale it to match the canvas
+ // units by dividing it by half of the position-domain length
+ // (mFrameCenterToBorderDistance) and by multiplying it by half of the canvas
+ // length (middleY - topY). Finally, we offset the coordinate by subtracting
+ // it from middleY (we can't "add" here because the coordinate grows from top
+ // to bottom).
+ final float coordY = middleY - ((pos - mFrameCenterPosition)
+ / mFrameCenterToBorderDistance) * (middleY - topY);
+
+ // Draw a point for this value.
+ canvas.drawPoint(coordX, coordY, mGraphPointPaint);
+
+ // If this value is part of the same gesture as the previous one, draw a line
+ // between them. We ignore the first value (with age = 0).
+ if (age != 0 && (age - prevAge) <= MAX_GESTURE_TIME) {
+ canvas.drawLine(prevCoordX, prevCoordY, coordX, coordY, mGraphLinePaint);
+ }
+
+ prevCoordX = coordX;
+ prevCoordY = coordY;
+ prevAge = age;
+ }
+ }
+
+ public float getFrameCenterPosition() {
+ return mFrameCenterPosition;
+ }
+
+ /**
+ * Converts a dimension in scaled pixel units to integer display pixels.
+ */
+ private static int applyDimensionSp(int dimensionSp, DisplayMetrics dm) {
+ return (int) TypedValue.applyDimension(COMPLEX_UNIT_SP, dimensionSp, dm);
+ }
+
+ /**
+ * Holds data needed to draw each entry in the graph.
+ */
+ private static class GraphValue {
+ /** Position. */
+ float mPos;
+ /** Time when this value was added. */
+ long mTime;
+
+ GraphValue(float pos, long time) {
+ this.mPos = pos;
+ this.mTime = time;
+ }
+ }
+
+ /**
+ * Holds the graph values as a cyclic buffer. It has a fixed capacity, and it replaces the
+ * old values with new ones to avoid creating new objects.
+ */
+ private static class CyclicBuffer {
+ private final GraphValue[] mValues;
+ private final int mCapacity;
+ private int mSize = 0;
+ private int mLastIndex = 0;
+
+ // The iteration index and counter are here to make it easier to reset them.
+ /** Determines the value currently pointed by the iterator. */
+ private int mIteratorIndex;
+ /** Counts how many values have been iterated through. */
+ private int mIteratorCount;
+
+ /** Used traverse the values in reverse order. */
+ private final Iterator<GraphValue> mReverseIterator = new Iterator<GraphValue>() {
+ @Override
+ public boolean hasNext() {
+ return mIteratorCount <= mSize;
+ }
+
+ @Override
+ public GraphValue next() {
+ // Returns the value currently pointed by the iterator and moves the iterator to
+ // the previous one.
+ mIteratorCount++;
+ return mValues[(mIteratorIndex-- + mCapacity) % mCapacity];
+ }
+ };
+
+ CyclicBuffer(int capacity) {
+ mCapacity = capacity;
+ mValues = new GraphValue[capacity];
+ }
+
+ /**
+ * Add new graph value. If there is an existing object, we replace its data with the
+ * new one. With this, we re-use old objects instead of creating new ones.
+ */
+ void add(float pos, long time) {
+ mLastIndex = (mLastIndex + 1) % mCapacity;
+ if (mValues[mLastIndex] == null) {
+ mValues[mLastIndex] = new GraphValue(pos, time);
+ } else {
+ final GraphValue oldValue = mValues[mLastIndex];
+ oldValue.mPos = pos;
+ oldValue.mTime = time;
+ }
+
+ // If needed, account for new value in the buffer size.
+ if (mSize != mCapacity) {
+ mSize++;
+ }
+ }
+
+ int getSize() {
+ return mSize;
+ }
+
+ GraphValue getFirst() {
+ final int distanceBetweenLastAndFirst = (mCapacity - mSize) + 1;
+ final int firstIndex = (mLastIndex + distanceBetweenLastAndFirst) % mCapacity;
+ return mValues[firstIndex];
+ }
+
+ GraphValue getLast() {
+ return mValues[mLastIndex];
+ }
+
+ void removeFirst() {
+ mSize--;
+ }
+
+ /** Returns an iterator pointing at the last value. */
+ Iterator<GraphValue> reverseIterator() {
+ mIteratorIndex = mLastIndex;
+ mIteratorCount = 1;
+ return mReverseIterator;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/input/debug/RotaryInputValueView.java b/services/core/java/com/android/server/input/debug/RotaryInputValueView.java
new file mode 100644
index 0000000..9fadac5
--- /dev/null
+++ b/services/core/java/com/android/server/input/debug/RotaryInputValueView.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 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.input.debug;
+
+import static android.util.TypedValue.COMPLEX_UNIT_SP;
+
+import android.content.Context;
+import android.graphics.ColorFilter;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Typeface;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.ViewConfiguration;
+import android.widget.TextView;
+
+import com.android.internal.R;
+
+import java.util.Locale;
+
+/**
+ * Draws the most recent rotary input value and indicates whether the source is active.
+ */
+public class RotaryInputValueView extends TextView {
+
+ private static final int INACTIVE_TEXT_COLOR = 0xffff00ff;
+ private static final int ACTIVE_TEXT_COLOR = 0xff420f28;
+ private static final int TEXT_SIZE_SP = 8;
+ private static final int SIDE_PADDING_SP = 4;
+ /** Determines how long the active status lasts. */
+ private static final int ACTIVE_STATUS_DURATION = 250 /* milliseconds */;
+ private static final ColorFilter ACTIVE_BACKGROUND_FILTER =
+ new ColorMatrixColorFilter(new float[]{
+ 0, 0, 0, 0, 255, // red
+ 0, 0, 0, 0, 0, // green
+ 0, 0, 0, 0, 255, // blue
+ 0, 0, 0, 0, 200 // alpha
+ });
+
+ private final Runnable mUpdateActivityStatusCallback = () -> updateActivityStatus(false);
+ private final float mScaledVerticalScrollFactor;
+ private final Locale mDefaultLocale = Locale.getDefault();
+
+ public RotaryInputValueView(Context c) {
+ super(c);
+
+ DisplayMetrics dm = mContext.getResources().getDisplayMetrics();
+ mScaledVerticalScrollFactor = ViewConfiguration.get(c).getScaledVerticalScrollFactor();
+
+ setText(getFormattedValue(0));
+ setTextColor(INACTIVE_TEXT_COLOR);
+ setTextSize(applyDimensionSp(TEXT_SIZE_SP, dm));
+ setPaddingRelative(applyDimensionSp(SIDE_PADDING_SP, dm), 0,
+ applyDimensionSp(SIDE_PADDING_SP, dm), 0);
+ setTypeface(null, Typeface.BOLD);
+ setBackgroundResource(R.drawable.focus_event_rotary_input_background);
+ }
+
+ /** Updates the shown text with the formatted value. */
+ public void updateValue(float value) {
+ removeCallbacks(mUpdateActivityStatusCallback);
+
+ setText(getFormattedValue(value * mScaledVerticalScrollFactor));
+
+ updateActivityStatus(true);
+ postDelayed(mUpdateActivityStatusCallback, ACTIVE_STATUS_DURATION);
+ }
+
+ /** Updates whether or not there's active rotary input. */
+ public void updateActivityStatus(boolean active) {
+ if (active) {
+ setTextColor(ACTIVE_TEXT_COLOR);
+ getBackground().setColorFilter(ACTIVE_BACKGROUND_FILTER);
+ } else {
+ setTextColor(INACTIVE_TEXT_COLOR);
+ getBackground().clearColorFilter();
+ }
+ }
+
+ private String getFormattedValue(float value) {
+ return String.format(mDefaultLocale, "%s%.1f", value < 0 ? "-" : "+", Math.abs(value));
+ }
+
+ /**
+ * Converts a dimension in scaled pixel units to integer display pixels.
+ */
+ private static int applyDimensionSp(int dimensionSp, DisplayMetrics dm) {
+ return (int) TypedValue.applyDimension(COMPLEX_UNIT_SP, dimensionSp, dm);
+ }
+}
diff --git a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
index 7726f40..dbbbed3 100644
--- a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
+++ b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
@@ -57,13 +57,13 @@
InputConfig.NOT_FOCUSABLE
| InputConfig.NOT_TOUCHABLE
| InputConfig.SPY
- | InputConfig.INTERCEPTS_STYLUS
- | InputConfig.TRUSTED_OVERLAY;
+ | InputConfig.INTERCEPTS_STYLUS;
// Configure the surface to receive stylus events across the entire display.
mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface's bounds */);
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ mWindowHandle.setTrustedOverlay(t, mInputSurface, true);
t.setInputWindowInfo(mInputSurface, mWindowHandle);
t.setLayer(mInputSurface, InputManagerService.INPUT_OVERLAY_LAYER_HANDWRITING_SURFACE);
t.setPosition(mInputSurface, 0, 0);
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/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index 93c66a1..08cf3f7 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -1431,13 +1431,17 @@
mContextHubWrapper.onBtMainSettingChanged(btEnabled);
}
} else {
- Log.d(TAG, "BT adapter not available. Defaulting to disabled");
- if (forceUpdate || mIsBtMainEnabled) {
- mIsBtMainEnabled = false;
+ Log.d(TAG, "BT adapter not available. Getting permissions from user settings");
+ boolean btEnabled = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.BLUETOOTH_ON, 0) == 1;
+ boolean btScanEnabled = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, 0) == 1;
+ if (forceUpdate || mIsBtMainEnabled != btEnabled) {
+ mIsBtMainEnabled = btEnabled;
mContextHubWrapper.onBtMainSettingChanged(mIsBtMainEnabled);
}
- if (forceUpdate || mIsBtScanningEnabled) {
- mIsBtScanningEnabled = false;
+ if (forceUpdate || mIsBtScanningEnabled != btScanEnabled) {
+ mIsBtScanningEnabled = btScanEnabled;
mContextHubWrapper.onBtScanningSettingChanged(mIsBtScanningEnabled);
}
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index 1c5ecb7..1e8b387 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -45,7 +45,7 @@
import com.android.internal.util.Preconditions;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.LocalServices;
-import com.android.server.PersistentDataBlockManagerInternal;
+import com.android.server.pdb.PersistentDataBlockManagerInternal;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
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/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index c9528d8..9dec1df 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -63,6 +63,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.media.flags.Flags;
import com.android.server.LocalServices;
import com.android.server.pm.UserManagerInternal;
@@ -161,11 +162,13 @@
mPowerManager = mContext.getSystemService(PowerManager.class);
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
- IntentFilter screenOnOffIntentFilter = new IntentFilter();
- screenOnOffIntentFilter.addAction(ACTION_SCREEN_ON);
- screenOnOffIntentFilter.addAction(ACTION_SCREEN_OFF);
+ if (!Flags.disableScreenOffBroadcastReceiver()) {
+ IntentFilter screenOnOffIntentFilter = new IntentFilter();
+ screenOnOffIntentFilter.addAction(ACTION_SCREEN_ON);
+ screenOnOffIntentFilter.addAction(ACTION_SCREEN_OFF);
+ mContext.registerReceiver(mScreenOnOffReceiver, screenOnOffIntentFilter);
+ }
- mContext.registerReceiver(mScreenOnOffReceiver, screenOnOffIntentFilter);
mContext.getPackageManager().addOnPermissionsChangeListener(this::onPermissionsChanged);
MediaFeatureFlagManager.getInstance()
@@ -2779,7 +2782,8 @@
List<ManagerRecord> managerRecords = getManagerRecords();
boolean isManagerScanning = false;
- if (service.mPowerManager.isInteractive()) {
+ if (Flags.disableScreenOffBroadcastReceiver()
+ || service.mPowerManager.isInteractive()) {
isManagerScanning = managerRecords.stream().anyMatch(manager ->
manager.mIsScanning && service.mActivityManager
.getPackageImportance(manager.mOwnerPackageName)
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 95ca08c..a158b18 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -97,20 +97,23 @@
public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionRecordImpl {
/**
- * {@link MediaSession#setMediaButtonBroadcastReceiver(ComponentName)} throws an {@link
- * IllegalArgumentException} if the provided {@link ComponentName} does not resolve to a valid
- * {@link android.content.BroadcastReceiver broadcast receiver} for apps targeting Android U and
- * above. For apps targeting Android T and below, the request will be ignored.
+ * {@link android.media.session.MediaSession#setMediaButtonBroadcastReceiver(
+ * android.content.ComponentName)} throws an {@link
+ * java.lang.IllegalArgumentException} if the provided {@link android.content.ComponentName}
+ * does not resolve to a valid {@link android.content.BroadcastReceiver broadcast receiver}
+ * for apps targeting Android U and above. For apps targeting Android T and below, the request
+ * will be ignored.
*/
@ChangeId
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
static final long THROW_FOR_INVALID_BROADCAST_RECEIVER = 270049379L;
/**
- * {@link MediaSession#setMediaButtonReceiver(PendingIntent)} throws an {@link
- * IllegalArgumentException} if the provided {@link PendingIntent} targets an {@link
- * android.app.Activity activity} for apps targeting Android V and above. For apps targeting
- * Android U and below, the request will be ignored.
+ * {@link android.media.session.MediaSession#setMediaButtonReceiver(android.app.PendingIntent)}
+ * throws an {@link java.lang.IllegalArgumentException} if the provided
+ * {@link android.app.PendingIntent} targets an {@link android.app.Activity activity} for
+ * apps targeting Android V and above. For apps targeting Android U and below, the request will
+ * be ignored.
*/
@ChangeId
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 13d1662..8cbc368 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -76,6 +76,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.Watchdog;
@@ -134,6 +135,7 @@
private final MediaRouter mMediaRouter;
private final MediaRouterCallback mMediaRouterCallback;
+ private final MediaProjectionMetricsLogger mMediaProjectionMetricsLogger;
private MediaRouter.RouteInfo mMediaRouteInfo;
@GuardedBy("mLock")
@@ -160,6 +162,7 @@
mWmInternal = LocalServices.getService(WindowManagerInternal.class);
mMediaRouter = (MediaRouter) mContext.getSystemService(Context.MEDIA_ROUTER_SERVICE);
mMediaRouterCallback = new MediaRouterCallback();
+ mMediaProjectionMetricsLogger = injector.mediaProjectionMetricsLogger();
Watchdog.getInstance().addMonitor(this);
}
@@ -193,6 +196,10 @@
Looper createCallbackLooper() {
return Looper.getMainLooper();
}
+
+ MediaProjectionMetricsLogger mediaProjectionMetricsLogger() {
+ return MediaProjectionMetricsLogger.getInstance();
+ }
}
@Override
@@ -372,6 +379,10 @@
if (mProjectionGrant != null) {
// Cache the session details.
mProjectionGrant.mSession = incomingSession;
+ mMediaProjectionMetricsLogger.notifyProjectionStateChange(
+ mProjectionGrant.uid,
+ FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CAPTURING_IN_PROGRESS,
+ FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN);
dispatchSessionSet(mProjectionGrant.getProjectionInfo(), incomingSession);
}
return true;
@@ -818,6 +829,19 @@
}
@Override // Binder call
+ @EnforcePermission(MANAGE_MEDIA_PROJECTION)
+ public void notifyPermissionRequestStateChange(int hostUid, int state,
+ int sessionCreationSource) {
+ notifyPermissionRequestStateChange_enforcePermission();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mMediaProjectionMetricsLogger.notifyProjectionStateChange(hostUid, state, sessionCreationSource);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
final long token = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java b/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java
new file mode 100644
index 0000000..f18ecad
--- /dev/null
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java
@@ -0,0 +1,50 @@
+/*
+ * 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.media.projection;
+
+
+import com.android.internal.util.FrameworkStatsLog;
+
+/**
+ * Class for emitting logs describing a MediaProjection session.
+ */
+public class MediaProjectionMetricsLogger {
+ private static MediaProjectionMetricsLogger sSingleton = null;
+
+ public static MediaProjectionMetricsLogger getInstance() {
+ if (sSingleton == null) {
+ sSingleton = new MediaProjectionMetricsLogger();
+ }
+ return sSingleton;
+ }
+
+ void notifyProjectionStateChange(int hostUid, int state, int sessionCreationSource) {
+ write(hostUid, state, sessionCreationSource);
+ }
+
+ private void write(int hostUid, int state, int sessionCreationSource) {
+ FrameworkStatsLog.write(
+ /* code */ FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED,
+ /* session_id */ 123,
+ /* state */ state,
+ /* previous_state */ FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN,
+ /* host_uid */ hostUid,
+ /* target_uid */ -1,
+ /* time_since_last_active */ 0,
+ /* creation_source */ sessionCreationSource);
+ }
+}
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/NotificationBitmapJobService.java b/services/core/java/com/android/server/notification/NotificationBitmapJobService.java
index 4335a1d..e1a0707 100644
--- a/services/core/java/com/android/server/notification/NotificationBitmapJobService.java
+++ b/services/core/java/com/android/server/notification/NotificationBitmapJobService.java
@@ -29,7 +29,12 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
-import java.util.Calendar;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.ZonedDateTime;
+import java.time.ZoneId;
/**
* This service runs everyday at 2am local time to remove expired bitmaps.
@@ -69,26 +74,25 @@
* @return Milliseconds until the next time the job should run.
*/
private static long getRunAfterMs() {
- Calendar cal = Calendar.getInstance(); // Uses local time zone
- final long now = cal.getTimeInMillis();
+ ZoneId zoneId = ZoneId.systemDefault();
+ ZonedDateTime now = Instant.now().atZone(zoneId);
- cal.set(Calendar.HOUR_OF_DAY, 2);
- cal.set(Calendar.MINUTE, 0);
- cal.set(Calendar.MILLISECOND, 0);
- final long today2AM = cal.getTimeInMillis();
+ LocalDate today = now.toLocalDate();
+ LocalTime twoAM = LocalTime.of(/* hour= */ 2, /* minute= */ 0);
- cal.add(Calendar.DAY_OF_YEAR, 1);
- final long tomorrow2AM = cal.getTimeInMillis();
+ ZonedDateTime today2AM = ZonedDateTime.of(today, twoAM, zoneId);
+ ZonedDateTime tomorrow2AM = today2AM.plusDays(1);
return getTimeUntilRemoval(now, today2AM, tomorrow2AM);
}
@VisibleForTesting
- static long getTimeUntilRemoval(long now, long today2AM, long tomorrow2AM) {
- if (now < today2AM) {
- return today2AM - now;
+ static long getTimeUntilRemoval(ZonedDateTime now, ZonedDateTime today2AM,
+ ZonedDateTime tomorrow2AM) {
+ if (Duration.between(now, today2AM).isNegative()) {
+ return Duration.between(now, tomorrow2AM).toMillis();
}
- return tomorrow2AM - now;
+ return Duration.between(now, today2AM).toMillis();
}
@Override
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryJobService.java b/services/core/java/com/android/server/notification/NotificationHistoryJobService.java
index 3776ad7..c9317d1 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryJobService.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryJobService.java
@@ -27,6 +27,7 @@
import android.os.CancellationSignal;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
import java.util.concurrent.TimeUnit;
@@ -77,5 +78,11 @@
}
return false;
}
+
+ @Override
+ @VisibleForTesting
+ protected void attachBaseContext(Context base) {
+ super.attachBaseContext(base);
+ }
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index db69d93..837b761 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -396,7 +396,7 @@
static final int MESSAGE_FINISH_TOKEN_TIMEOUT = 7;
static final int MESSAGE_ON_PACKAGE_CHANGED = 8;
- static final long BITMAP_EXPIRATION_TIME_MS = TimeUnit.HOURS.toMillis(24);
+ static final Duration BITMAP_DURATION = Duration.ofHours(24);
// ranking thread messages
private static final int MESSAGE_RECONSIDER_RANKING = 1000;
@@ -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
@@ -6705,7 +6713,7 @@
final long timePostedMs = r.getSbn().getPostTime();
final long timeNowMs = System.currentTimeMillis();
- if (isBitmapExpired(timePostedMs, timeNowMs, BITMAP_EXPIRATION_TIME_MS)) {
+ if (isBitmapExpired(timePostedMs, timeNowMs, BITMAP_DURATION.toMillis())) {
removeBitmapAndRepost(r);
}
}
@@ -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/oemlock/OemLockService.java b/services/core/java/com/android/server/oemlock/OemLockService.java
index 4c6110b..d002688 100644
--- a/services/core/java/com/android/server/oemlock/OemLockService.java
+++ b/services/core/java/com/android/server/oemlock/OemLockService.java
@@ -35,8 +35,8 @@
import android.util.Slog;
import com.android.server.LocalServices;
-import com.android.server.PersistentDataBlockManagerInternal;
import com.android.server.SystemService;
+import com.android.server.pdb.PersistentDataBlockManagerInternal;
import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.UserManagerInternal.UserRestrictionsListener;
import com.android.server.pm.UserRestrictionsUtils;
diff --git a/services/core/java/com/android/server/os/NativeTombstoneManager.java b/services/core/java/com/android/server/os/NativeTombstoneManager.java
index c7c8136..79c1346 100644
--- a/services/core/java/com/android/server/os/NativeTombstoneManager.java
+++ b/services/core/java/com/android/server/os/NativeTombstoneManager.java
@@ -569,6 +569,10 @@
@Override
public void onEvent(int event, @Nullable String path) {
+ if (path == null) {
+ Slog.w(TAG, "path is null at TombstoneWatcher.onEvent()");
+ return;
+ }
mHandler.post(() -> {
// Ignore .tmp files.
if (path.endsWith(".tmp")) {
diff --git a/services/core/java/com/android/server/pdb/OWNERS b/services/core/java/com/android/server/pdb/OWNERS
new file mode 100644
index 0000000..6f322ee
--- /dev/null
+++ b/services/core/java/com/android/server/pdb/OWNERS
@@ -0,0 +1,2 @@
+victorhsieh@google.com
+swillden@google.com
diff --git a/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java b/services/core/java/com/android/server/pdb/PersistentDataBlockManagerInternal.java
similarity index 98%
rename from services/core/java/com/android/server/PersistentDataBlockManagerInternal.java
rename to services/core/java/com/android/server/pdb/PersistentDataBlockManagerInternal.java
index 21fa9f9..66ad716 100644
--- a/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java
+++ b/services/core/java/com/android/server/pdb/PersistentDataBlockManagerInternal.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.pdb;
/**
* Internal interface for storing and retrieving persistent data.
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
similarity index 99%
rename from services/core/java/com/android/server/PersistentDataBlockService.java
rename to services/core/java/com/android/server/pdb/PersistentDataBlockService.java
index 754a7ed..b006ac8 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.pdb;
import static com.android.internal.util.Preconditions.checkArgument;
@@ -37,6 +37,9 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.DumpUtils;
import com.android.server.pm.UserManagerInternal;
+import com.android.server.LocalServices;
+import com.android.server.SystemServerInitThreadPool;
+import com.android.server.SystemService;
import libcore.io.IoUtils;
diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java
index f987629..79cd2a0 100644
--- a/services/core/java/com/android/server/pm/Computer.java
+++ b/services/core/java/com/android/server/pm/Computer.java
@@ -489,6 +489,9 @@
boolean isPackageQuarantinedForUser(@NonNull String packageName, @UserIdInt int userId);
+ /** Check if the package is in a stopped state for a given user. */
+ boolean isPackageStoppedForUser(@NonNull String packageName, @UserIdInt int userId);
+
boolean isSuspendingAnyPackages(@NonNull String suspendingPackage, @UserIdInt int userId);
@NonNull
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 5d2944e..7db7bf5 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -4938,7 +4938,7 @@
int userId) {
final int callingUid = Binder.getCallingUid();
enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
- false /* checkShell */, "isPackageSuspendedForUser for user " + userId);
+ false /* checkShell */, "when asking about packages for user " + userId);
final PackageStateInternal ps = mSettings.getPackage(packageName);
if (ps == null || shouldFilterApplicationIncludingUninstalled(ps, callingUid, userId)) {
throw new IllegalArgumentException("Unknown target package: " + packageName);
@@ -4957,6 +4957,11 @@
}
@Override
+ public boolean isPackageStoppedForUser(@NonNull String packageName, @UserIdInt int userId) {
+ return getUserStageOrDefaultForUser(packageName, userId).isStopped();
+ }
+
+ @Override
public boolean isSuspendingAnyPackages(@NonNull String suspendingPackage,
@UserIdInt int userId) {
for (final PackageStateInternal packageState : getPackageStates().values()) {
diff --git a/services/core/java/com/android/server/pm/IPackageManagerBase.java b/services/core/java/com/android/server/pm/IPackageManagerBase.java
index 76203ac..9a0306b 100644
--- a/services/core/java/com/android/server/pm/IPackageManagerBase.java
+++ b/services/core/java/com/android/server/pm/IPackageManagerBase.java
@@ -961,6 +961,12 @@
}
@Override
+ public final boolean isPackageStoppedForUser(@NonNull String packageName,
+ @UserIdInt int userId) {
+ return snapshot().isPackageStoppedForUser(packageName, userId);
+ }
+
+ @Override
@Deprecated
public final boolean isSafeMode() {
// allow instant applications
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 1c7024b..7d822b5 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -34,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;
@@ -922,7 +923,7 @@
// 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()) {
+ if (isInstallFromAdb() && Flags.useArtServiceV2()) {
var externalProfileErrors = new LinkedHashSet<String>();
for (PackageDexoptResult packageResult : dexoptResult.getPackageDexoptResults()) {
for (DexContainerFileDexoptResult fileResult :
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/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ddc8369..68aa93d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4501,6 +4501,7 @@
boolean stopped, @UserIdInt int userId) {
if (!mUserManager.exists(userId)) return;
final int callingUid = Binder.getCallingUid();
+ boolean wasStopped = false;
if (snapshot.getInstantAppPackageName(callingUid) == null) {
final int permission = mContext.checkCallingOrSelfPermission(
Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
@@ -4522,6 +4523,7 @@
? null : packageState.getUserStateOrDefault(userId);
if (packageState != null && packageUserState.isStopped() != stopped) {
boolean wasNotLaunched = packageUserState.isNotLaunched();
+ wasStopped = packageUserState.isStopped();
commitPackageStateMutation(null, packageName, state -> {
PackageUserStateWrite userState = state.userState(userId);
userState.setStopped(stopped);
@@ -4553,6 +4555,24 @@
ah.setHibernatingGlobally(packageName, false);
}
});
+ // Send UNSTOPPED broadcast if necessary
+ if (wasStopped && Flags.stayStopped()) {
+ final PackageManagerInternal pmi =
+ mInjector.getLocalService(PackageManagerInternal.class);
+ final int [] userIds = resolveUserIds(userId);
+ final SparseArray<int[]> broadcastAllowList =
+ snapshotComputer().getVisibilityAllowLists(packageName, userIds);
+ final Bundle extras = new Bundle();
+ extras.putInt(Intent.EXTRA_UID, pmi.getPackageUid(packageName, 0, userId));
+ extras.putInt(Intent.EXTRA_USER_HANDLE, userId);
+ mHandler.post(() -> {
+ mBroadcastHelper.sendPackageBroadcast(Intent.ACTION_PACKAGE_UNSTOPPED,
+ packageName, extras,
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null,
+ userIds, null, broadcastAllowList, null,
+ null);
+ });
+ }
}
}
@@ -6929,6 +6949,25 @@
public ParceledListSlice<PackageInstaller.SessionInfo> getHistoricalSessions(int userId) {
return mInstallerService.getHistoricalSessions(userId);
}
+
+ @Override
+ public void sendPackageRestartedBroadcast(@NonNull String packageName,
+ int uid, @Intent.Flags int flags) {
+ final int userId = UserHandle.getUserId(uid);
+ final int [] userIds = resolveUserIds(userId);
+ final SparseArray<int[]> broadcastAllowList =
+ snapshotComputer().getVisibilityAllowLists(packageName, userIds);
+ final Bundle extras = new Bundle();
+ extras.putInt(Intent.EXTRA_UID, uid);
+ extras.putInt(Intent.EXTRA_USER_HANDLE, userId);
+ mHandler.post(() -> {
+ mBroadcastHelper.sendPackageBroadcast(Intent.ACTION_PACKAGE_RESTARTED,
+ packageName, extras,
+ flags, null, null,
+ userIds, null, broadcastAllowList, null,
+ null);
+ });
+ }
}
private void setEnabledOverlayPackages(@UserIdInt int userId,
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 82d3e91..0e98158 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -75,13 +75,13 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.multiuser.Flags;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
import android.os.Environment;
import android.os.FileUtils;
-import android.os.Flags;
import android.os.Handler;
import android.os.IBinder;
import android.os.IProgressListener;
@@ -286,6 +286,8 @@
private static final int USER_VERSION = 11;
+ private static final int MAX_USER_STRING_LENGTH = 500;
+
private static final long EPOCH_PLUS_30_YEARS = 30L * 365 * 24 * 60 * 60 * 1000L; // ms
static final int WRITE_USER_MSG = 1;
@@ -1557,7 +1559,7 @@
logQuietModeEnabled(userId, enableQuietMode, callingPackage);
// Broadcast generic intents for all profiles
- if (Flags.allowPrivateProfile()) {
+ if (android.os.Flags.allowPrivateProfile()) {
broadcastProfileAvailabilityChanges(profile, parent.getUserHandle(),
enableQuietMode, false);
}
@@ -3783,6 +3785,8 @@
@GuardedBy({"mPackagesLock"})
private void readUserListLP() {
+ // Whether guest restrictions are present on userlist.xml
+ boolean guestRestrictionsArePresentOnUserListXml = false;
try (ResilientAtomicFile file = getUserListFile()) {
FileInputStream fin = null;
try {
@@ -3832,6 +3836,7 @@
}
}
} else if (name.equals(TAG_GUEST_RESTRICTIONS)) {
+ guestRestrictionsArePresentOnUserListXml = true;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& type != XmlPullParser.END_TAG) {
if (type == XmlPullParser.START_TAG) {
@@ -3850,6 +3855,7 @@
updateUserIds();
upgradeIfNecessaryLP();
+ updateUsersWithFeatureFlags(guestRestrictionsArePresentOnUserListXml);
} catch (Exception e) {
// Remove corrupted file and retry.
file.failRead(fin, e);
@@ -3875,6 +3881,24 @@
}
/**
+ * Update any user formats or Xml data that need to be updated based on the current user state
+ * and the feature flag settings.
+ */
+ @GuardedBy({"mPackagesLock"})
+ private void updateUsersWithFeatureFlags(boolean guestRestrictionsArePresentOnUserListXml) {
+ // User Xml re-writes are required when guest restrictions are saved on userlist.xml but
+ // as per the feature flag it should be on the SYSTEM user's xml or guest restrictions
+ // are saved on SYSTEM user's xml but as per the flags it should not be saved there.
+ if (guestRestrictionsArePresentOnUserListXml
+ == Flags.saveGlobalAndGuestRestrictionsOnSystemUserXmlReadOnly()) {
+ for (int userId: getUserIds()) {
+ writeUserLP(getUserDataNoChecks(userId));
+ }
+ writeUserListLP();
+ }
+ }
+
+ /**
* Version of {@link #upgradeIfNecessaryLP()} that takes in the userVersion for testing
* purposes. For non-tests, use {@link #upgradeIfNecessaryLP()}.
*/
@@ -4374,24 +4398,41 @@
// Write seed data
if (userData.persistSeedData) {
if (userData.seedAccountName != null) {
- serializer.attribute(null, ATTR_SEED_ACCOUNT_NAME, userData.seedAccountName);
+ serializer.attribute(null, ATTR_SEED_ACCOUNT_NAME,
+ truncateString(userData.seedAccountName));
}
if (userData.seedAccountType != null) {
- serializer.attribute(null, ATTR_SEED_ACCOUNT_TYPE, userData.seedAccountType);
+ serializer.attribute(null, ATTR_SEED_ACCOUNT_TYPE,
+ truncateString(userData.seedAccountType));
}
}
if (userInfo.name != null) {
serializer.startTag(null, TAG_NAME);
- serializer.text(userInfo.name);
+ serializer.text(truncateString(userInfo.name));
serializer.endTag(null, TAG_NAME);
}
synchronized (mRestrictionsLock) {
UserRestrictionsUtils.writeRestrictions(serializer,
mBaseUserRestrictions.getRestrictions(userInfo.id), TAG_RESTRICTIONS);
- UserRestrictionsUtils.writeRestrictions(serializer,
- mDevicePolicyUserRestrictions.getRestrictions(UserHandle.USER_ALL),
- TAG_DEVICE_POLICY_GLOBAL_RESTRICTIONS);
+ if (Flags.saveGlobalAndGuestRestrictionsOnSystemUserXmlReadOnly()) {
+ if (userInfo.id == UserHandle.USER_SYSTEM) {
+ UserRestrictionsUtils.writeRestrictions(serializer,
+ mDevicePolicyUserRestrictions.getRestrictions(UserHandle.USER_ALL),
+ TAG_DEVICE_POLICY_GLOBAL_RESTRICTIONS);
+
+ serializer.startTag(null, TAG_GUEST_RESTRICTIONS);
+ synchronized (mGuestRestrictions) {
+ UserRestrictionsUtils.writeRestrictions(serializer, mGuestRestrictions,
+ TAG_RESTRICTIONS);
+ }
+ serializer.endTag(null, TAG_GUEST_RESTRICTIONS);
+ }
+ } else {
+ UserRestrictionsUtils.writeRestrictions(serializer,
+ mDevicePolicyUserRestrictions.getRestrictions(UserHandle.USER_ALL),
+ TAG_DEVICE_POLICY_GLOBAL_RESTRICTIONS);
+ }
UserRestrictionsUtils.writeRestrictions(serializer,
mDevicePolicyUserRestrictions.getRestrictions(userInfo.id),
@@ -4431,6 +4472,13 @@
serializer.endDocument();
}
+ private String truncateString(String original) {
+ if (original == null || original.length() <= MAX_USER_STRING_LENGTH) {
+ return original;
+ }
+ return original.substring(0, MAX_USER_STRING_LENGTH);
+ }
+
/*
* Writes the user list file in this format:
*
@@ -4460,12 +4508,15 @@
serializer.attributeInt(null, ATTR_USER_VERSION, mUserVersion);
serializer.attributeInt(null, ATTR_USER_TYPE_VERSION, mUserTypeVersion);
- serializer.startTag(null, TAG_GUEST_RESTRICTIONS);
- synchronized (mGuestRestrictions) {
- UserRestrictionsUtils
- .writeRestrictions(serializer, mGuestRestrictions, TAG_RESTRICTIONS);
+ if (!Flags.saveGlobalAndGuestRestrictionsOnSystemUserXmlReadOnly()) {
+ serializer.startTag(null, TAG_GUEST_RESTRICTIONS);
+ synchronized (mGuestRestrictions) {
+ UserRestrictionsUtils
+ .writeRestrictions(serializer, mGuestRestrictions,
+ TAG_RESTRICTIONS);
+ }
+ serializer.endTag(null, TAG_GUEST_RESTRICTIONS);
}
- serializer.endTag(null, TAG_GUEST_RESTRICTIONS);
int[] userIdsToWrite;
synchronized (mUsersLock) {
userIdsToWrite = new int[mUsers.size()];
@@ -4609,6 +4660,19 @@
localRestrictions = UserRestrictionsUtils.readRestrictions(parser);
} else if (TAG_DEVICE_POLICY_GLOBAL_RESTRICTIONS.equals(tag)) {
globalRestrictions = UserRestrictionsUtils.readRestrictions(parser);
+ } else if (TAG_GUEST_RESTRICTIONS.equals(tag)) {
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.END_TAG) {
+ if (type == XmlPullParser.START_TAG) {
+ if (parser.getName().equals(TAG_RESTRICTIONS)) {
+ synchronized (mGuestRestrictions) {
+ UserRestrictionsUtils
+ .readRestrictions(parser, mGuestRestrictions);
+ }
+ }
+ break;
+ }
+ }
} else if (TAG_ACCOUNT.equals(tag)) {
type = parser.next();
if (type == XmlPullParser.TEXT) {
@@ -4869,7 +4933,7 @@
@UserIdInt int parentId, boolean preCreate, @Nullable String[] disallowedPackages,
@NonNull TimingsTraceAndSlog t, @Nullable Object token)
throws UserManager.CheckedUserOperationException {
-
+ String truncatedName = truncateString(name);
final UserTypeDetails userTypeDetails = mUserTypes.get(userType);
if (userTypeDetails == null) {
throwCheckedUserOperationException(
@@ -4904,8 +4968,8 @@
// Try to use a pre-created user (if available).
if (!preCreate && parentId < 0 && isUserTypeEligibleForPreCreation(userTypeDetails)) {
- final UserInfo preCreatedUser = convertPreCreatedUserIfPossible(userType, flags, name,
- token);
+ final UserInfo preCreatedUser = convertPreCreatedUserIfPossible(userType, flags,
+ truncatedName, token);
if (preCreatedUser != null) {
return preCreatedUser;
}
@@ -5000,7 +5064,7 @@
flags |= UserInfo.FLAG_EPHEMERAL_ON_CREATE;
}
- userInfo = new UserInfo(userId, name, null, flags, userType);
+ userInfo = new UserInfo(userId, truncatedName, null, flags, userType);
userInfo.serialNumber = mNextSerialNumber++;
userInfo.creationTime = getCreationTime();
userInfo.partial = true;
@@ -6456,8 +6520,8 @@
Slog.e(LOG_TAG, "No such user for settings seed data u=" + userId);
return;
}
- userData.seedAccountName = accountName;
- userData.seedAccountType = accountType;
+ userData.seedAccountName = truncateString(accountName);
+ userData.seedAccountType = truncateString(accountType);
userData.seedAccountOptions = accountOptions;
userData.persistSeedData = persist;
}
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/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index d16a812..d804e01 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -444,7 +444,7 @@
updateApplicationInfo(info, flags, state);
- initForUser(info, pkg, userId);
+ initForUser(info, pkg, userId, state);
// TODO(b/135203078): Remove PackageParser1/toAppInfoWithoutState and clean all this up
PackageStateUnserialized pkgState = pkgSetting.getTransientState();
@@ -690,7 +690,7 @@
info.splitDependencies = pkg.getSplitDependencies().size() == 0
? null : pkg.getSplitDependencies();
- initForUser(info, pkg, userId);
+ initForUser(info, pkg, userId, state);
info.primaryCpuAbi = pkgSetting.getPrimaryCpuAbi();
info.secondaryCpuAbi = pkgSetting.getSecondaryCpuAbi();
@@ -1006,7 +1006,7 @@
}
private static void initForUser(ApplicationInfo output, AndroidPackage input,
- @UserIdInt int userId) {
+ @UserIdInt int userId, PackageUserStateInternal state) {
PackageImpl pkg = ((PackageImpl) input);
String packageName = input.getPackageName();
output.uid = UserHandle.getUid(userId, UserHandle.getAppId(input.getUid()));
@@ -1016,6 +1016,13 @@
return;
}
+ if (android.content.pm.Flags.nullableDataDir()
+ && !state.isInstalled() && !state.dataExists()) {
+ // The data dir has been deleted
+ output.dataDir = null;
+ return;
+ }
+
// For performance reasons, all these paths are built as strings
if (userId == UserHandle.USER_SYSTEM) {
output.credentialProtectedDataDir =
@@ -1050,7 +1057,7 @@
// This duplicates the ApplicationInfo variant because it uses field assignment and the classes
// don't inherit from each other, unfortunately. Consolidating logic would introduce overhead.
private static void initForUser(InstrumentationInfo output, AndroidPackage input,
- @UserIdInt int userId) {
+ @UserIdInt int userId, PackageUserStateInternal state) {
PackageImpl pkg = ((PackageImpl) input);
String packageName = input.getPackageName();
if ("android".equals(packageName)) {
@@ -1058,6 +1065,13 @@
return;
}
+ if (android.content.pm.Flags.nullableDataDir()
+ && !state.isInstalled() && !state.dataExists()) {
+ // The data dir has been deleted
+ output.dataDir = null;
+ return;
+ }
+
// For performance reasons, all these paths are built as strings
if (userId == UserHandle.USER_SYSTEM) {
output.credentialProtectedDataDir =
@@ -1089,12 +1103,23 @@
}
}
- @NonNull
+ /**
+ * Returns the data dir of the app for the target user. Return null if the app isn't installed
+ * on the target user and doesn't have a data dir on the target user.
+ */
+ @Nullable
public static File getDataDir(PackageStateInternal ps, int userId) {
if ("android".equals(ps.getPackageName())) {
return Environment.getDataSystemDirectory();
}
+ if (android.content.pm.Flags.nullableDataDir()
+ && !ps.getUserStateOrDefault(userId).isInstalled()
+ && !ps.getUserStateOrDefault(userId).dataExists()) {
+ // The app has been uninstalled for the user and the data dir has been deleted
+ return null;
+ }
+
if (ps.isDefaultToDeviceProtectedStorage()
&& PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) {
return Environment.getDataUserDePackageDirectory(ps.getVolumeUuid(), userId,
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/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java
index ebc7163..b83421f 100644
--- a/services/core/java/com/android/server/policy/AppOpsPolicy.java
+++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java
@@ -42,6 +42,7 @@
import android.os.Process;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.permission.flags.Flags;
import android.service.voice.VoiceInteractionManagerInternal;
import android.service.voice.VoiceInteractionManagerInternal.HotwordDetectionServiceIdentity;
import android.text.TextUtils;
@@ -75,9 +76,6 @@
private static final boolean SYSPROP_HOTWORD_DETECTION_SERVICE_REQUIRED =
SystemProperties.getBoolean("ro.hotword.detection_service_required", false);
- //TODO(b/289087412): import this from the flag value in set up in device config.
- private static final boolean IS_VOICE_ACTIVATION_OP_ENABLED = false;
-
@NonNull
private final Object mLock = new Object();
@@ -212,7 +210,7 @@
* @return the op that should be noted for the voice activations of the app by detected hotword.
*/
public static int getVoiceActivationOp() {
- if (IS_VOICE_ACTIVATION_OP_ENABLED) {
+ if (Flags.voiceActivationPermissionApis()) {
return OP_RECEIVE_SANDBOX_TRIGGER_AUDIO;
}
return OP_RECORD_AUDIO_HOTWORD;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 097656c..3a6664a 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -333,6 +333,11 @@
static final int SHORT_PRESS_SLEEP_GO_TO_SLEEP = 0;
static final int SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME = 1;
+ // must match: config_shortPressOnSettingsBehavior in config.xml
+ static final int SHORT_PRESS_SETTINGS_NOTHING = 0;
+ static final int SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL = 1;
+ static final int LAST_SHORT_PRESS_SETTINGS_BEHAVIOR = SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL;
+
static final int PENDING_KEY_NULL = -1;
// Must match: config_shortPressOnStemPrimaryBehavior in config.xml
@@ -611,6 +616,9 @@
// What we do when the user double-taps on home
int mDoubleTapOnHomeBehavior;
+ // What we do when the user presses on settings
+ int mShortPressOnSettingsBehavior;
+
// Must match config_primaryShortPressTargetActivity in config.xml
ComponentName mPrimaryShortPressTargetActivity;
@@ -2766,6 +2774,13 @@
if (mPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
mShortPressOnWindowBehavior = SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE;
}
+
+ mShortPressOnSettingsBehavior = res.getInteger(
+ com.android.internal.R.integer.config_shortPressOnSettingsBehavior);
+ if (mShortPressOnSettingsBehavior < SHORT_PRESS_SETTINGS_NOTHING
+ || mShortPressOnSettingsBehavior > LAST_SHORT_PRESS_SETTINGS_BEHAVIOR) {
+ mShortPressOnSettingsBehavior = SHORT_PRESS_SETTINGS_NOTHING;
+ }
}
private void updateSettings() {
@@ -3632,6 +3647,15 @@
Slog.wtf(TAG, "KEYCODE_STYLUS_BUTTON_* should be handled in"
+ " interceptKeyBeforeQueueing");
return true;
+ case KeyEvent.KEYCODE_SETTINGS:
+ if (mShortPressOnSettingsBehavior == SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL) {
+ if (!down) {
+ toggleNotificationPanel();
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.TOGGLE_NOTIFICATION_PANEL);
+ }
+ return true;
+ }
+ break;
}
if (isValidGlobalKey(keyCode)
&& mGlobalKeyManager.handleGlobalKey(mContext, keyCode, event)) {
@@ -6272,6 +6296,9 @@
pw.print("mLongPressOnPowerBehavior=");
pw.println(longPressOnPowerBehaviorToString(mLongPressOnPowerBehavior));
pw.print(prefix);
+ pw.print("mShortPressOnSettingsBehavior=");
+ pw.println(shortPressOnSettingsBehaviorToString(mShortPressOnSettingsBehavior));
+ pw.print(prefix);
pw.print("mLongPressOnPowerAssistantTimeoutMs=");
pw.println(mLongPressOnPowerAssistantTimeoutMs);
pw.print(prefix);
@@ -6470,6 +6497,17 @@
}
}
+ private static String shortPressOnSettingsBehaviorToString(int behavior) {
+ switch (behavior) {
+ case SHORT_PRESS_SETTINGS_NOTHING:
+ return "SHORT_PRESS_SETTINGS_NOTHING";
+ case SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL:
+ return "SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL";
+ default:
+ return Integer.toString(behavior);
+ }
+ }
+
private static String veryLongPressOnPowerBehaviorToString(int behavior) {
switch (behavior) {
case VERY_LONG_PRESS_POWER_NOTHING:
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/testharness/TestHarnessModeService.java b/services/core/java/com/android/server/testharness/TestHarnessModeService.java
index bfe34049e..9a9b836 100644
--- a/services/core/java/com/android/server/testharness/TestHarnessModeService.java
+++ b/services/core/java/com/android/server/testharness/TestHarnessModeService.java
@@ -41,8 +41,8 @@
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.LocalServices;
-import com.android.server.PersistentDataBlockManagerInternal;
import com.android.server.SystemService;
+import com.android.server.pdb.PersistentDataBlockManagerInternal;
import com.android.server.pm.UserManagerInternal;
import java.io.ByteArrayInputStream;
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index a5c0fb3..cddc79d 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -1047,6 +1047,16 @@
// in use frontends when no available frontend has been found.
int priority = getFrontendHighestClientPriority(fr.getOwnerClientId());
if (currentLowestPriority > priority) {
+ // we need to check the max used num if the target frontend type is not
+ // currently in primary use (and simply blocked due to exclusive group)
+ ClientProfile targetOwnerProfile = getClientProfile(fr.getOwnerClientId());
+ int primaryFeId = targetOwnerProfile.getPrimaryFrontend();
+ FrontendResource primaryFe = getFrontendResource(primaryFeId);
+ if (fr.getType() != primaryFe.getType()
+ && isFrontendMaxNumUseReached(fr.getType())) {
+ continue;
+ }
+ // update the target frontend
inUseLowestPriorityFrHandle = fr.getHandle();
currentLowestPriority = priority;
isRequestFromSameProcess = (requestClient.getProcessId()
diff --git a/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java b/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java
index e4f9607..a346216 100644
--- a/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java
+++ b/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java
@@ -19,6 +19,7 @@
import android.annotation.Nullable;
import android.content.res.Resources;
import android.os.VibrationEffect;
+import android.os.vibrator.Flags;
import android.os.VibratorInfo;
import android.os.vibrator.persistence.ParsedVibration;
import android.os.vibrator.persistence.VibrationXmlParser;
@@ -127,6 +128,10 @@
VibrationXmlParser.VibrationXmlParserException,
XmlParserException,
XmlPullParserException {
+ if (!Flags.hapticFeedbackVibrationOemCustomizationEnabled()) {
+ Slog.d(TAG, "Haptic feedback customization feature is not enabled.");
+ return null;
+ }
String customizationFile =
res.getString(
com.android.internal.R.string.config_hapticFeedbackCustomizationFile);
diff --git a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
index 3d89afa..becbbf2 100644
--- a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
+++ b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
@@ -25,6 +25,8 @@
import android.util.Slog;
import android.util.SparseArray;
import android.view.HapticFeedbackConstants;
+import android.view.flags.FeatureFlags;
+import android.view.flags.FeatureFlagsImpl;
import com.android.internal.annotations.VisibleForTesting;
@@ -54,6 +56,7 @@
// If present and valid, a vibration here will be used for an effect.
// Otherwise, the system's default vibration will be used.
@Nullable private final SparseArray<VibrationEffect> mHapticCustomizations;
+ private final FeatureFlags mViewFeatureFlags;
/** @hide */
public HapticFeedbackVibrationProvider(Resources res, Vibrator vibrator) {
@@ -62,14 +65,16 @@
/** @hide */
public HapticFeedbackVibrationProvider(Resources res, VibratorInfo vibratorInfo) {
- this(res, vibratorInfo, loadHapticCustomizations(res, vibratorInfo));
+ this(res, vibratorInfo, loadHapticCustomizations(res, vibratorInfo),
+ new FeatureFlagsImpl());
}
/** @hide */
@VisibleForTesting HapticFeedbackVibrationProvider(
Resources res,
VibratorInfo vibratorInfo,
- @Nullable SparseArray<VibrationEffect> hapticCustomizations) {
+ @Nullable SparseArray<VibrationEffect> hapticCustomizations,
+ FeatureFlags viewFeatureFlags) {
mVibratorInfo = vibratorInfo;
mHapticTextHandleEnabled = res.getBoolean(
com.android.internal.R.bool.config_enableHapticTextHandle);
@@ -78,6 +83,7 @@
hapticCustomizations = null;
}
mHapticCustomizations = hapticCustomizations;
+ mViewFeatureFlags = viewFeatureFlags;
mSafeModeEnabledVibrationEffect =
effectHasCustomization(HapticFeedbackConstants.SAFE_MODE_ENABLED)
@@ -201,12 +207,16 @@
default:
attrs = TOUCH_VIBRATION_ATTRIBUTES;
}
+
+ int flags = 0;
if (bypassVibrationIntensitySetting) {
- attrs = new VibrationAttributes.Builder(attrs)
- .setFlags(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)
- .build();
+ flags |= VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF;
}
- return attrs;
+ if (shouldBypassInterruptionPolicy(effectId, mViewFeatureFlags)) {
+ flags |= VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
+ }
+
+ return flags == 0 ? attrs : new VibrationAttributes.Builder(attrs).setFlags(flags).build();
}
/** Dumps relevant state. */
@@ -295,4 +305,19 @@
return null;
}
}
+
+ private static boolean shouldBypassInterruptionPolicy(
+ int effectId, FeatureFlags viewFeatureFlags) {
+ switch (effectId) {
+ case HapticFeedbackConstants.SCROLL_TICK:
+ case HapticFeedbackConstants.SCROLL_ITEM_FOCUS:
+ case HapticFeedbackConstants.SCROLL_LIMIT:
+ // The SCROLL_* constants should bypass interruption filter, so that scroll haptics
+ // can play regardless of focus modes like DND. Guard this behavior by the feature
+ // flag controlling the general scroll feedback APIs.
+ return viewFeatureFlags.scrollFeedbackApi();
+ default:
+ return false;
+ }
+ }
}
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/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index ee3d697..45bd152 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -448,6 +448,7 @@
String reason, IBinder token) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason = " + reason);
try {
+ attrs = fixupVibrationAttributes(attrs, effect);
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.VIBRATE, "vibrate");
return vibrateInternal(uid, displayId, opPkg, effect, attrs, reason, token);
@@ -457,7 +458,7 @@
}
HalVibration vibrateWithoutPermissionCheck(int uid, int displayId, String opPkg,
- @NonNull CombinedVibration effect, @Nullable VibrationAttributes attrs,
+ @NonNull CombinedVibration effect, @NonNull VibrationAttributes attrs,
String reason, IBinder token) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate no perm check, reason = " + reason);
try {
@@ -468,7 +469,7 @@
}
private HalVibration vibrateInternal(int uid, int displayId, String opPkg,
- @NonNull CombinedVibration effect, @Nullable VibrationAttributes attrs,
+ @NonNull CombinedVibration effect, @NonNull VibrationAttributes attrs,
String reason, IBinder token) {
if (token == null) {
Slog.e(TAG, "token must not be null");
@@ -478,7 +479,6 @@
if (!isEffectValid(effect)) {
return null;
}
- attrs = fixupVibrationAttributes(attrs, effect);
// Create Vibration.Stats as close to the received request as possible, for tracking.
HalVibration vib = new HalVibration(token, effect,
new Vibration.CallerInfo(attrs, uid, displayId, opPkg, reason));
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 01ea33f..0718f2f 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -2843,7 +2843,7 @@
WallpaperData systemWallpaper = mWallpaperMap.get(mCurrentUserId);
WallpaperData lockWallpaper = mLockWallpaperMap.get(mCurrentUserId);
boolean systemValid = systemWallpaper != null;
- boolean lockValid = lockWallpaper != null && !isLockscreenLiveWallpaperEnabled();
+ boolean lockValid = lockWallpaper != null && isLockscreenLiveWallpaperEnabled();
return systemValid && lockValid ? new WallpaperData[]{systemWallpaper, lockWallpaper}
: systemValid ? new WallpaperData[]{systemWallpaper}
: lockValid ? new WallpaperData[]{lockWallpaper}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index ed10346..ebd21d9 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -5367,18 +5367,11 @@
// rather than just direct membership.
inFinishingTransition = mTransitionController.inFinishingTransition(this);
if (!inFinishingTransition && (visible || !mDisplayContent.isSleeping())) {
- Slog.e(TAG, "setVisibility=" + visible
- + " while transition is not collecting or finishing "
- + this + " caller=" + Debug.getCallers(8));
- // Force showing the parents because they may be hidden by previous transition.
if (visible) {
- final Transaction t = getSyncTransaction();
- for (WindowContainer<?> p = getParent(); p != null && p != mDisplayContent;
- p = p.getParent()) {
- if (p.mSurfaceControl != null) {
- t.show(p.mSurfaceControl);
- }
- }
+ mTransitionController.onVisibleWithoutCollectingTransition(this,
+ Debug.getCallers(1, 1));
+ } else {
+ Slog.w(TAG, "Set invisible without transition " + this);
}
}
}
@@ -8271,7 +8264,11 @@
private void clearSizeCompatModeAttributes() {
mInSizeCompatModeForBounds = false;
+ final float lastSizeCompatScale = mSizeCompatScale;
mSizeCompatScale = 1f;
+ if (mSizeCompatScale != lastSizeCompatScale) {
+ forAllWindows(WindowState::updateGlobalScale, false /* traverseTopToBottom */);
+ }
mSizeCompatBounds = null;
mCompatDisplayInsets = null;
mLetterboxUiController.clearInheritedCompatDisplayInsets();
@@ -8279,11 +8276,7 @@
@VisibleForTesting
void clearSizeCompatMode() {
- final float lastSizeCompatScale = mSizeCompatScale;
clearSizeCompatModeAttributes();
- if (mSizeCompatScale != lastSizeCompatScale) {
- forAllWindows(WindowState::updateGlobalScale, false /* traverseTopToBottom */);
- }
// Clear config override in #updateCompatDisplayInsets().
final int activityType = getActivityType();
final Configuration overrideConfig = getRequestedOverrideConfiguration();
@@ -9269,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/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index c39b266..4a5311b 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -577,7 +577,8 @@
.getRootTask(WINDOWING_MODE_UNDEFINED, activityType);
if (rootTask == null) return false;
final ActivityRecord r = rootTask.topRunningActivity();
- if (r == null || r.isVisibleRequested() || !r.attachedToProcess()
+ if (r == null || (r.isVisibleRequested() && rootTask.isTopRootTaskInDisplayArea())
+ || !r.attachedToProcess()
|| !r.mActivityComponent.equals(intent.getComponent())
|| !mService.isCallerRecents(r.getUid())
// Recents keeps invisible while device is locked.
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/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 61bae71..0b67321 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -6448,19 +6448,8 @@
if (!restarting && hasVisibleActivities) {
deferWindowLayout();
try {
- final Task topTask = mRootWindowContainer.getTopDisplayFocusedRootTask();
- if (topTask != null
- && topTask.topRunningActivity(true /* focusableOnly */) == null) {
- topTask.adjustFocusToNextFocusableTask("handleAppDied");
- }
- if (!mRootWindowContainer.resumeFocusedTasksTopActivities()) {
- // If there was nothing to resume, and we are not already restarting
- // this process, but there is a visible activity that is hosted by the
- // process...then make sure all visible activities are running, taking
- // care of restarting this process.
- mRootWindowContainer.ensureActivitiesVisible(null, 0,
- !PRESERVE_WINDOWS);
- }
+ mRootWindowContainer.ensureVisibilityOnVisibleActivityDiedOrCrashed(
+ "handleAppDied");
} finally {
continueWindowLayout();
}
@@ -6901,7 +6890,18 @@
@Override
public int finishTopCrashedActivities(WindowProcessController crashedApp, String reason) {
synchronized (mGlobalLock) {
- return mRootWindowContainer.finishTopCrashedActivities(crashedApp, reason);
+ deferWindowLayout();
+ try {
+ final Task finishedTask = mRootWindowContainer.finishTopCrashedActivities(
+ crashedApp, reason);
+ if (finishedTask != null) {
+ mRootWindowContainer.ensureVisibilityOnVisibleActivityDiedOrCrashed(reason);
+ return finishedTask.mTaskId;
+ }
+ return INVALID_TASK_ID;
+ } finally {
+ continueWindowLayout();
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 9fd4720..12e1e2c 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -47,6 +47,7 @@
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
@@ -1592,6 +1593,9 @@
}
private void removePinnedRootTaskInSurfaceTransaction(Task rootTask) {
+ rootTask.mTransitionController.requestTransitionIfNeeded(TRANSIT_TO_BACK, 0 /* flags */,
+ rootTask, rootTask.mDisplayContent, null /* remoteTransition */,
+ null /* displayChange */);
/**
* Workaround: Force-stop all the activities in the root pinned task before we reparent them
* to the fullscreen root task. This is to guarantee that when we are removing a root task,
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index c2885da..4237668 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -163,7 +163,8 @@
if (window == null) {
EmbeddedWindowController.EmbeddedWindow embeddedWindow =
- wmService.mEmbeddedWindowController.getByFocusToken(focusedWindowToken);
+ wmService.mEmbeddedWindowController.getByInputTransferToken(
+ focusedWindowToken);
if (embeddedWindow != null) {
ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
"Current focused window is embeddedWindow. Dispatch KEYCODE_BACK.");
diff --git a/services/core/java/com/android/server/wm/CompatModePackages.java b/services/core/java/com/android/server/wm/CompatModePackages.java
index 2439159..73bcc8d 100644
--- a/services/core/java/com/android/server/wm/CompatModePackages.java
+++ b/services/core/java/com/android/server/wm/CompatModePackages.java
@@ -66,29 +66,29 @@
public final class CompatModePackages {
/**
- * {@link CompatModePackages#DOWNSCALED_INVERSE} is the gatekeeper of all per-app buffer inverse
- * downscale changes. Enabling this change will allow the following scaling factors:
- * {@link CompatModePackages#DOWNSCALE_90}
- * {@link CompatModePackages#DOWNSCALE_85}
- * {@link CompatModePackages#DOWNSCALE_80}
- * {@link CompatModePackages#DOWNSCALE_75}
- * {@link CompatModePackages#DOWNSCALE_70}
- * {@link CompatModePackages#DOWNSCALE_65}
- * {@link CompatModePackages#DOWNSCALE_60}
- * {@link CompatModePackages#DOWNSCALE_55}
- * {@link CompatModePackages#DOWNSCALE_50}
- * {@link CompatModePackages#DOWNSCALE_45}
- * {@link CompatModePackages#DOWNSCALE_40}
- * {@link CompatModePackages#DOWNSCALE_35}
- * {@link CompatModePackages#DOWNSCALE_30}
+ * <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> is the gatekeeper of all per-app buffer
+ * inverse downscale changes. Enabling this change will allow the following scaling factors:
+ * <a href="#DOWNSCALE_90">DOWNSCALE_90</a>
+ * <a href="#DOWNSCALE_85">DOWNSCALE_85</a>
+ * <a href="#DOWNSCALE_80">DOWNSCALE_80</a>
+ * <a href="#DOWNSCALE_75">DOWNSCALE_75</a>
+ * <a href="#DOWNSCALE_70">DOWNSCALE_70</a>
+ * <a href="#DOWNSCALE_65">DOWNSCALE_65</a>
+ * <a href="#DOWNSCALE_60">DOWNSCALE_60</a>
+ * <a href="#DOWNSCALE_55">DOWNSCALE_55</a>
+ * <a href="#DOWNSCALE_50">DOWNSCALE_50</a>
+ * <a href="#DOWNSCALE_45">DOWNSCALE_45</a>
+ * <a href="#DOWNSCALE_40">DOWNSCALE_40</a>
+ * <a href="#DOWNSCALE_35">DOWNSCALE_35</a>
+ * <a href="#DOWNSCALE_30">DOWNSCALE_30</a>
*
- * If {@link CompatModePackages#DOWNSCALED_INVERSE} is enabled for an app package, then the app
- * will be forcibly resized to the lowest enabled scaling factor e.g. 1/0.8 if both 1/0.8 and
- * 1/0.7 (* 100%) were enabled.
+ * If <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> is enabled for an app package, then
+ * the app will be forcibly resized to the lowest enabled scaling factor e.g. 1/0.8 if both
+ * 1/0.8 and 1/0.7 (* 100%) were enabled.
*
- * When both {@link CompatModePackages#DOWNSCALED_INVERSE}
- * and {@link CompatModePackages#DOWNSCALED} are enabled, then
- * {@link CompatModePackages#DOWNSCALED_INVERSE} takes precedence.
+ * When both <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a>
+ * and <a href="#DOWNSCALED">DOWNSCALED</a> are enabled, then
+ * <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> takes precedence.
*/
@ChangeId
@Disabled
@@ -96,29 +96,29 @@
public static final long DOWNSCALED_INVERSE = 273564678L; // This is a Bug ID.
/**
- * {@link CompatModePackages#DOWNSCALED} is the gatekeeper of all per-app buffer downscaling
+ * <a href="#DOWNSCALED">DOWNSCALED</a> is the gatekeeper of all per-app buffer downscaling
* changes. Enabling this change will allow the following scaling factors:
- * {@link CompatModePackages#DOWNSCALE_90}
- * {@link CompatModePackages#DOWNSCALE_85}
- * {@link CompatModePackages#DOWNSCALE_80}
- * {@link CompatModePackages#DOWNSCALE_75}
- * {@link CompatModePackages#DOWNSCALE_70}
- * {@link CompatModePackages#DOWNSCALE_65}
- * {@link CompatModePackages#DOWNSCALE_60}
- * {@link CompatModePackages#DOWNSCALE_55}
- * {@link CompatModePackages#DOWNSCALE_50}
- * {@link CompatModePackages#DOWNSCALE_45}
- * {@link CompatModePackages#DOWNSCALE_40}
- * {@link CompatModePackages#DOWNSCALE_35}
- * {@link CompatModePackages#DOWNSCALE_30}
+ * <a href="#DOWNSCALE_90">DOWNSCALE_90</a>
+ * <a href="#DOWNSCALE_85">DOWNSCALE_85</a>
+ * <a href="#DOWNSCALE_80">DOWNSCALE_80</a>
+ * <a href="#DOWNSCALE_75">DOWNSCALE_75</a>
+ * <a href="#DOWNSCALE_70">DOWNSCALE_70</a>
+ * <a href="#DOWNSCALE_65">DOWNSCALE_65</a>
+ * <a href="#DOWNSCALE_60">DOWNSCALE_60</a>
+ * <a href="#DOWNSCALE_55">DOWNSCALE_55</a>
+ * <a href="#DOWNSCALE_50">DOWNSCALE_50</a>
+ * <a href="#DOWNSCALE_45">DOWNSCALE_45</a>
+ * <a href="#DOWNSCALE_40">DOWNSCALE_40</a>
+ * <a href="#DOWNSCALE_35">DOWNSCALE_35</a>
+ * <a href="#DOWNSCALE_30">DOWNSCALE_30</a>
*
- * If {@link CompatModePackages#DOWNSCALED} is enabled for an app package, then the app will be
+ * If <a href="#DOWNSCALED">DOWNSCALED</a> is enabled for an app package, then the app will be
* forcibly resized to the highest enabled scaling factor e.g. 80% if both 80% and 70% were
* enabled.
*
- * When both {@link CompatModePackages#DOWNSCALED_INVERSE}
- * and {@link CompatModePackages#DOWNSCALED} are enabled, then
- * {@link CompatModePackages#DOWNSCALED_INVERSE} takes precedence.
+ * When both <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a>
+ * and <a href="#DOWNSCALED">DOWNSCALED</a> are enabled, then
+ * <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> takes precedence.
*/
@ChangeId
@Disabled
@@ -126,12 +126,13 @@
public static final long DOWNSCALED = 168419799L;
/**
- * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id
- * {@link CompatModePackages#DOWNSCALE_90} for a package will force the app to assume it's
+ * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id
+ * <a href="#DOWNSCALE_90">DOWNSCALE_90</a> for a package will force the app to assume it's
* running on a display with 90% the vertical and horizontal resolution of the real display.
*
- * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's
- * running on a display with 111.11% the vertical and horizontal resolution of the real display
+ * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to
+ * assume it's running on a display with 111.11% the vertical and horizontal resolution of
+ * the real display
*/
@ChangeId
@Disabled
@@ -139,12 +140,13 @@
public static final long DOWNSCALE_90 = 182811243L;
/**
- * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id
- * {@link CompatModePackages#DOWNSCALE_85} for a package will force the app to assume it's
+ * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id
+ * <a href="#DOWNSCALE_85">DOWNSCALE_85</a> for a package will force the app to assume it's
* running on a display with 85% the vertical and horizontal resolution of the real display.
*
- * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's
- * running on a display with 117.65% the vertical and horizontal resolution of the real display
+ * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to
+ * assume it's running on a display with 117.65% the vertical and horizontal resolution of the
+ * real display
*/
@ChangeId
@Disabled
@@ -152,12 +154,13 @@
public static final long DOWNSCALE_85 = 189969734L;
/**
- * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id
- * {@link CompatModePackages#DOWNSCALE_80} for a package will force the app to assume it's
+ * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id
+ * <a href="#DOWNSCALE_80">DOWNSCALE_80</a> for a package will force the app to assume it's
* running on a display with 80% the vertical and horizontal resolution of the real display.
*
- * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's
- * running on a display with 125% the vertical and horizontal resolution of the real display
+ * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to
+ * assume it's running on a display with 125% the vertical and horizontal resolution of the real
+ * display
*/
@ChangeId
@Disabled
@@ -165,12 +168,13 @@
public static final long DOWNSCALE_80 = 176926753L;
/**
- * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id
- * {@link CompatModePackages#DOWNSCALE_75} for a package will force the app to assume it's
+ * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id
+ * <a href="#DOWNSCALE_75">DOWNSCALE_75</a> for a package will force the app to assume it's
* running on a display with 75% the vertical and horizontal resolution of the real display.
*
- * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's
- * running on a display with 133.33% the vertical and horizontal resolution of the real display
+ * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to
+ * assume it's running on a display with 133.33% the vertical and horizontal resolution of the
+ * real display
*/
@ChangeId
@Disabled
@@ -178,12 +182,13 @@
public static final long DOWNSCALE_75 = 189969779L;
/**
- * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id
- * {@link CompatModePackages#DOWNSCALE_70} for a package will force the app to assume it's
+ * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id
+ * <a href="#DOWNSCALE_70">DOWNSCALE_70</a> for a package will force the app to assume it's
* running on a display with 70% the vertical and horizontal resolution of the real display.
*
- * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's
- * running on a display with 142.86% the vertical and horizontal resolution of the real display
+ * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to
+ * assume it's running on a display with 142.86% the vertical and horizontal resolution of the
+ * real display
*/
@ChangeId
@Disabled
@@ -191,12 +196,13 @@
public static final long DOWNSCALE_70 = 176926829L;
/**
- * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id
- * {@link CompatModePackages#DOWNSCALE_65} for a package will force the app to assume it's
+ * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id
+ * <a href="#DOWNSCALE_65">DOWNSCALE_65</a> for a package will force the app to assume it's
* running on a display with 65% the vertical and horizontal resolution of the real display.
*
- * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's
- * running on a display with 153.85% the vertical and horizontal resolution of the real display
+ * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to
+ * assume it's running on a display with 153.85% the vertical and horizontal resolution of the
+ * real display
*/
@ChangeId
@Disabled
@@ -204,12 +210,13 @@
public static final long DOWNSCALE_65 = 189969744L;
/**
- * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id
- * {@link CompatModePackages#DOWNSCALE_60} for a package will force the app to assume it's
+ * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id
+ * <a href="#DOWNSCALE_60">DOWNSCALE_60</a> for a package will force the app to assume it's
* running on a display with 60% the vertical and horizontal resolution of the real display.
*
- * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's
- * running on a display with 166.67% the vertical and horizontal resolution of the real display
+ * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to
+ * assume it's running on a display with 166.67% the vertical and horizontal resolution of the
+ * real display
*/
@ChangeId
@Disabled
@@ -217,12 +224,13 @@
public static final long DOWNSCALE_60 = 176926771L;
/**
- * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id
- * {@link CompatModePackages#DOWNSCALE_55} for a package will force the app to assume it's
+ * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id
+ * <a href="#DOWNSCALE_55">DOWNSCALE_55</a> for a package will force the app to assume it's
* running on a display with 55% the vertical and horizontal resolution of the real display.
*
- * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's
- * running on a display with 181.82% the vertical and horizontal resolution of the real display
+ * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to
+ * assume it's running on a display with 181.82% the vertical and horizontal resolution of the
+ * real display
*/
@ChangeId
@Disabled
@@ -230,12 +238,13 @@
public static final long DOWNSCALE_55 = 189970036L;
/**
- * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id
- * {@link CompatModePackages#DOWNSCALE_50} for a package will force the app to assume it's
+ * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id
+ * <a href="#DOWNSCALE_50">DOWNSCALE_50</a> for a package will force the app to assume it's
* running on a display with 50% vertical and horizontal resolution of the real display.
*
- * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's
- * running on a display with 200% the vertical and horizontal resolution of the real display
+ * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to
+ * assume it's running on a display with 200% the vertical and horizontal resolution of the real
+ * display
*/
@ChangeId
@Disabled
@@ -243,12 +252,13 @@
public static final long DOWNSCALE_50 = 176926741L;
/**
- * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id
- * {@link CompatModePackages#DOWNSCALE_45} for a package will force the app to assume it's
+ * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id
+ * <a href="#DOWNSCALE_45">DOWNSCALE_45</a> for a package will force the app to assume it's
* running on a display with 45% the vertical and horizontal resolution of the real display.
*
- * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's
- * running on a display with 222.22% the vertical and horizontal resolution of the real display
+ * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to
+ * assume it's running on a display with 222.22% the vertical and horizontal resolution of the
+ * real display
*/
@ChangeId
@Disabled
@@ -256,12 +266,13 @@
public static final long DOWNSCALE_45 = 189969782L;
/**
- * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id
- * {@link CompatModePackages#DOWNSCALE_40} for a package will force the app to assume it's
+ * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id
+ * <a href="#DOWNSCALE_40">DOWNSCALE_40</a> for a package will force the app to assume it's
* running on a display with 40% the vertical and horizontal resolution of the real display.
*
- * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's
- * running on a display with 250% the vertical and horizontal resolution of the real display
+ * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to
+ * assume it's running on a display with 250% the vertical and horizontal resolution of the real
+ * display
*/
@ChangeId
@Disabled
@@ -269,12 +280,13 @@
public static final long DOWNSCALE_40 = 189970038L;
/**
- * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id
- * {@link CompatModePackages#DOWNSCALE_35} for a package will force the app to assume it's
+ * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id
+ * <a href="#DOWNSCALE_35">DOWNSCALE_35</a> for a package will force the app to assume it's
* running on a display with 35% the vertical and horizontal resolution of the real display.
*
- * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's
- * running on a display with 285.71% the vertical and horizontal resolution of the real display
+ * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to
+ * assume it's running on a display with 285.71% the vertical and horizontal resolution of the
+ * real display
*/
@ChangeId
@Disabled
@@ -282,12 +294,13 @@
public static final long DOWNSCALE_35 = 189969749L;
/**
- * With {@link CompatModePackages#DOWNSCALED} enabled, subsequently enabling change-id
- * {@link CompatModePackages#DOWNSCALE_30} for a package will force the app to assume it's
+ * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id
+ * <a href="#DOWNSCALE_30">DOWNSCALE_30</a> for a package will force the app to assume it's
* running on a display with 30% the vertical and horizontal resolution of the real display.
*
- * With {@link CompatModePackages#DOWNSCALED_INVERSE} enabled will force the app to assume it's
- * running on a display with 333.33% the vertical and horizontal resolution of the real display
+ * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to
+ * assume it's running on a display with 333.33% the vertical and horizontal resolution of the
+ * real display
*/
@ChangeId
@Disabled
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/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index f6fe9b1..b7b5c2af 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -218,6 +218,7 @@
import android.view.DisplayInfo;
import android.view.DisplayShape;
import android.view.Gravity;
+import android.view.IDecorViewGestureListener;
import android.view.IDisplayWindowInsetsController;
import android.view.ISystemGestureExclusionListener;
import android.view.IWindow;
@@ -471,6 +472,8 @@
private final RemoteCallbackList<ISystemGestureExclusionListener>
mSystemGestureExclusionListeners = new RemoteCallbackList<>();
+ private final RemoteCallbackList<IDecorViewGestureListener> mDecorViewGestureListener =
+ new RemoteCallbackList<>();
private final Region mSystemGestureExclusion = new Region();
private boolean mSystemGestureExclusionWasRestricted = false;
private final Region mSystemGestureExclusionUnrestricted = new Region();
@@ -5968,6 +5971,27 @@
mSystemGestureExclusionListeners.unregister(listener);
}
+ void registerDecorViewGestureListener(IDecorViewGestureListener listener) {
+ mDecorViewGestureListener.register(listener);
+ }
+
+ void unregisterDecorViewGestureListener(IDecorViewGestureListener listener) {
+ mDecorViewGestureListener.unregister(listener);
+ }
+
+ void updateDecorViewGestureIntercepted(IBinder token, boolean intercepted) {
+ for (int i = mDecorViewGestureListener.beginBroadcast() - 1; i >= 0; --i) {
+ try {
+ mDecorViewGestureListener
+ .getBroadcastItem(i)
+ .onInterceptionChanged(token, intercepted);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to notify DecorViewGestureListener", e);
+ }
+ }
+ mDecorViewGestureListener.finishBroadcast();
+ }
+
void updateKeepClearAreas() {
final Set<Rect> restrictedKeepClearAreas = new ArraySet<>();
final Set<Rect> unrestrictedKeepClearAreas = new ArraySet<>();
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 0f1a105..7af4aad 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -48,7 +48,6 @@
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
-import android.os.InputConfig;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -186,6 +185,10 @@
// Crop the input surface to the display size.
mTmpClipRect.set(0, 0, mDisplaySize.x, mDisplaySize.y);
+ // Make trusted overlay to not block any touches while D&D ongoing and allowing
+ // touches to pass through to windows underneath. This allows user to interact with the
+ // UI to navigate while dragging.
+ h.setTrustedOverlay(mTransaction, mInputSurface, true);
mTransaction.show(mInputSurface)
.setInputWindowInfo(mInputSurface, h)
.setLayer(mInputSurface, Integer.MAX_VALUE)
@@ -377,11 +380,6 @@
mDragWindowHandle.ownerUid = MY_UID;
mDragWindowHandle.scaleFactor = 1.0f;
- // InputConfig.TRUSTED_OVERLAY: To not block any touches while D&D ongoing and allowing
- // touches to pass through to windows underneath. This allows user to interact with the
- // UI to navigate while dragging.
- mDragWindowHandle.inputConfig = InputConfig.TRUSTED_OVERLAY;
-
// The drag window cannot receive new touches.
mDragWindowHandle.touchableRegion.setEmpty();
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index c9bae12..275396f 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -45,8 +45,8 @@
private static final String TAG = TAG_WITH_CLASS_NAME ? "EmbeddedWindowController" : TAG_WM;
/* maps input token to an embedded window */
private ArrayMap<IBinder /*input token */, EmbeddedWindow> mWindows = new ArrayMap<>();
- private ArrayMap<IBinder /*focus grant token */, EmbeddedWindow> mWindowsByFocusToken =
- new ArrayMap<>();
+ private ArrayMap<IBinder /*input transfer token */, EmbeddedWindow>
+ mWindowsByInputTransferToken = new ArrayMap<>();
private ArrayMap<IBinder /*window token*/, EmbeddedWindow> mWindowsByWindowToken =
new ArrayMap<>();
private final Object mGlobalLock;
@@ -67,14 +67,14 @@
void add(IBinder inputToken, EmbeddedWindow window) {
try {
mWindows.put(inputToken, window);
- final IBinder focusToken = window.getFocusGrantToken();
- mWindowsByFocusToken.put(focusToken, window);
+ final IBinder inputTransferToken = window.getInputTransferToken();
+ mWindowsByInputTransferToken.put(inputTransferToken, window);
mWindowsByWindowToken.put(window.getWindowToken(), window);
updateProcessController(window);
window.mClient.asBinder().linkToDeath(()-> {
synchronized (mGlobalLock) {
mWindows.remove(inputToken);
- mWindowsByFocusToken.remove(focusToken);
+ mWindowsByInputTransferToken.remove(inputTransferToken);
}
}, 0);
} catch (RemoteException e) {
@@ -105,7 +105,7 @@
EmbeddedWindow ew = mWindows.valueAt(i);
if (ew.mClient.asBinder() == client.asBinder()) {
mWindows.removeAt(i).onRemoved();
- mWindowsByFocusToken.remove(ew.getFocusGrantToken());
+ mWindowsByInputTransferToken.remove(ew.getInputTransferToken());
mWindowsByWindowToken.remove(ew.getWindowToken());
return;
}
@@ -117,7 +117,7 @@
EmbeddedWindow ew = mWindows.valueAt(i);
if (ew.mHostWindowState == host) {
mWindows.removeAt(i).onRemoved();
- mWindowsByFocusToken.remove(ew.getFocusGrantToken());
+ mWindowsByInputTransferToken.remove(ew.getInputTransferToken());
mWindowsByWindowToken.remove(ew.getWindowToken());
}
}
@@ -127,8 +127,8 @@
return mWindows.get(inputToken);
}
- EmbeddedWindow getByFocusToken(IBinder focusGrantToken) {
- return mWindowsByFocusToken.get(focusGrantToken);
+ EmbeddedWindow getByInputTransferToken(IBinder inputTransferToken) {
+ return mWindowsByInputTransferToken.get(inputTransferToken);
}
EmbeddedWindow getByWindowToken(IBinder windowToken) {
@@ -153,7 +153,7 @@
* to request focus transfer to the embedded. This is not the input token since we don't
* want to give clients access to each others input token.
*/
- private final IBinder mFocusGrantToken;
+ private final IBinder mInputTransferToken;
private boolean mIsFocusable;
@@ -171,7 +171,7 @@
*/
EmbeddedWindow(Session session, WindowManagerService service, IWindow clientToken,
WindowState hostWindowState, int ownerUid, int ownerPid, int windowType,
- int displayId, IBinder focusGrantToken, String inputHandleName,
+ int displayId, IBinder inputTransferToken, String inputHandleName,
boolean isFocusable) {
mSession = session;
mWmService = service;
@@ -183,7 +183,7 @@
mOwnerPid = ownerPid;
mWindowType = windowType;
mDisplayId = displayId;
- mFocusGrantToken = focusGrantToken;
+ mInputTransferToken = inputTransferToken;
final String hostWindowName =
(mHostWindowState != null) ? "-" + mHostWindowState.getWindowTag().toString()
: "";
@@ -260,8 +260,8 @@
return mOwnerUid;
}
- IBinder getFocusGrantToken() {
- return mFocusGrantToken;
+ IBinder getInputTransferToken() {
+ return mInputTransferToken;
}
IBinder getInputChannelToken() {
@@ -290,7 +290,7 @@
// Use null session since this is being granted by system server and doesn't
// require the host session to be passed in
mWmService.grantEmbeddedWindowFocus(null, mHostWindowState.mClient,
- mFocusGrantToken, grantFocus);
+ mInputTransferToken, grantFocus);
if (grantFocus) {
// If granting focus to the embedded when tapped, we need to ensure the host
// gains focus as well or the transfer won't take effect since it requires
@@ -298,7 +298,7 @@
mHostWindowState.handleTapOutsideFocusInsideSelf();
}
} else {
- mWmService.grantEmbeddedWindowFocus(mSession, mFocusGrantToken, grantFocus);
+ mWmService.grantEmbeddedWindowFocus(mSession, mInputTransferToken, grantFocus);
}
}
}
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index 39622c1..c21930d 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -74,7 +74,7 @@
mWindowHandle.ownerPid = WindowManagerService.MY_PID;
mWindowHandle.ownerUid = WindowManagerService.MY_UID;
mWindowHandle.scaleFactor = 1.0f;
- mWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE | InputConfig.TRUSTED_OVERLAY;
+ mWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE;
mInputSurface = mService.makeSurfaceBuilder(
mService.mRoot.getDisplayContent(displayId).getSession())
@@ -129,12 +129,14 @@
void show(SurfaceControl.Transaction t, WindowContainer w) {
t.show(mInputSurface);
+ mWindowHandle.setTrustedOverlay(t, mInputSurface, true);
t.setInputWindowInfo(mInputSurface, mWindowHandle);
t.setRelativeLayer(mInputSurface, w.getSurfaceControl(), 1);
}
void show(SurfaceControl.Transaction t, int layer) {
t.show(mInputSurface);
+ mWindowHandle.setTrustedOverlay(t, mInputSurface, true);
t.setInputWindowInfo(mInputSurface, mWindowHandle);
t.setLayer(mInputSurface, layer);
}
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 825d38b..af307ec3 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -675,6 +675,11 @@
w.getKeyInterceptionInfo());
if (w.mWinAnimator.hasSurface()) {
+ // Update trusted overlay changes here because they are tied to input info. Input
+ // changes can be updated even if surfaces aren't.
+ inputWindowHandle.setTrustedOverlay(mInputTransaction,
+ w.mWinAnimator.mSurfaceController.mSurfaceControl,
+ w.isWindowTrustedOverlay());
populateInputWindowHandle(inputWindowHandle, w);
setInputWindowInfoIfNeeded(mInputTransaction,
w.mWinAnimator.mSurfaceController.mSurfaceControl, inputWindowHandle);
@@ -732,7 +737,7 @@
new InputWindowHandle(null /* inputApplicationHandle */, displayId));
inputWindowHandle.setName(name);
inputWindowHandle.setLayoutParamsType(TYPE_SECURE_SYSTEM_OVERLAY);
- inputWindowHandle.setTrustedOverlay(true);
+ inputWindowHandle.setTrustedOverlay(t, sc, true);
populateOverlayInputInfo(inputWindowHandle);
setInputWindowInfoIfNeeded(t, sc, inputWindowHandle);
}
diff --git a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
index 64b7a60..90d81bd 100644
--- a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
+++ b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
@@ -195,6 +195,11 @@
mChanged = true;
}
+ void setTrustedOverlay(SurfaceControl.Transaction t, SurfaceControl sc,
+ boolean trustedOverlay) {
+ mHandle.setTrustedOverlay(t, sc, trustedOverlay);
+ }
+
void setOwnerPid(int pid) {
if (mHandle.ownerPid == pid) {
return;
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index c3977d6..82d4b90 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -62,6 +62,7 @@
import android.window.TaskSnapshot;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.IResultReceiver;
import com.android.internal.protolog.common.ProtoLog;
import com.android.server.LocalServices;
import com.android.server.inputmethod.InputMethodManagerInternal;
@@ -244,7 +245,8 @@
}
@Override
- public void finish(boolean moveHomeToTop, boolean sendUserLeaveHint) {
+ public void finish(boolean moveHomeToTop, boolean sendUserLeaveHint,
+ IResultReceiver finishCb) {
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
"finish(%b): mCanceled=%b", moveHomeToTop, mCanceled);
final long token = Binder.clearCallingIdentity();
@@ -257,6 +259,13 @@
} finally {
Binder.restoreCallingIdentity(token);
}
+ if (finishCb != null) {
+ try {
+ finishCb.send(0, new Bundle());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to report animation finished", e);
+ }
+ }
}
@Override
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/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index a88aa2e..cf6a1fe 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2319,19 +2319,36 @@
*
* @param app The app that crashed.
* @param reason Reason to perform this action.
- * @return The task id that was finished in this root task, or INVALID_TASK_ID if none was
- * finished.
+ * @return The finished task which was on top or visible, otherwise {@code null} if the crashed
+ * app doesn't have activity in visible task.
*/
- int finishTopCrashedActivities(WindowProcessController app, String reason) {
+ @Nullable
+ Task finishTopCrashedActivities(WindowProcessController app, String reason) {
Task focusedRootTask = getTopDisplayFocusedRootTask();
final Task[] finishedTask = new Task[1];
forAllRootTasks(rootTask -> {
+ final boolean recordTopOrVisible = finishedTask[0] == null
+ && (focusedRootTask == rootTask || rootTask.isVisibleRequested());
final Task t = rootTask.finishTopCrashedActivityLocked(app, reason);
- if (rootTask == focusedRootTask || finishedTask[0] == null) {
+ if (recordTopOrVisible) {
finishedTask[0] = t;
}
});
- return finishedTask[0] != null ? finishedTask[0].mTaskId : INVALID_TASK_ID;
+ return finishedTask[0];
+ }
+
+ void ensureVisibilityOnVisibleActivityDiedOrCrashed(String reason) {
+ final Task topTask = getTopDisplayFocusedRootTask();
+ if (topTask != null && topTask.topRunningActivity(true /* focusableOnly */) == null) {
+ // Move the next focusable task to front.
+ topTask.adjustFocusToNextFocusableTask(reason);
+ }
+ if (!resumeFocusedTasksTopActivities()) {
+ // It may be nothing to resume because there are pausing activities or all the top
+ // activities are resumed. Then it still needs to make sure all visible activities are
+ // running in case the tasks were reordered or there are non-top visible activities.
+ ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, !PRESERVE_WINDOWS);
+ }
}
boolean resumeFocusedTasksTopActivities() {
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index bbe44c5..3775ccd 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -560,6 +560,16 @@
}
@Override
+ public void reportDecorViewGestureInterceptionChanged(IWindow window, boolean intercepted) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mService.reportDecorViewGestureChanged(this, window, intercepted);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
public void reportKeepClearAreasChanged(IWindow window, List<Rect> restricted,
List<Rect> unrestricted) {
if (!mSetsUnrestrictedKeepClearAreas && !unrestricted.isEmpty()) {
@@ -884,8 +894,8 @@
@Override
public void grantInputChannel(int displayId, SurfaceControl surface,
IWindow window, IBinder hostInputToken, int flags, int privateFlags, int type,
- int inputFeatures, IBinder windowToken, IBinder focusGrantToken, String inputHandleName,
- InputChannel outInputChannel) {
+ int inputFeatures, IBinder windowToken, IBinder inputTransferToken,
+ String inputHandleName, InputChannel outInputChannel) {
if (hostInputToken == null && !mCanAddInternalSystemWindow) {
// Callers without INTERNAL_SYSTEM_WINDOW permission cannot grant input channel to
// embedded windows without providing a host window input token
@@ -896,7 +906,7 @@
try {
mService.grantInputChannel(this, mUid, mPid, displayId, surface, window, hostInputToken,
flags, mCanAddInternalSystemWindow ? privateFlags : 0,
- type, inputFeatures, windowToken, focusGrantToken, inputHandleName,
+ type, inputFeatures, windowToken, inputTransferToken, inputHandleName,
outInputChannel);
} finally {
Binder.restoreCallingIdentity(identity);
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index c6547a0..f3fb7c4 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -372,8 +372,7 @@
parent.forAllTasks(t -> {
// Skip transient-launch task
if (t == transientRootTask) return false;
- if (t.isVisibleRequested() && !t.isAlwaysOnTop()
- && !t.getWindowConfiguration().tasksAreFloating()) {
+ if (t.isVisibleRequested() && !t.isAlwaysOnTop()) {
if (t.isRootTask()) {
mTransientHideTasks.add(t);
}
@@ -704,6 +703,7 @@
if (dc == null || mTargetDisplays.contains(dc)) return;
mTargetDisplays.add(dc);
addOnTopTasks(dc, mOnTopTasksStart);
+ mController.startPerfHintForDisplay(dc.mDisplayId);
}
/**
@@ -1392,7 +1392,6 @@
dc.handleCompleteDeferredRemoval();
}
validateKeyguardOcclusion();
- validateVisibility();
mState = STATE_FINISHED;
// Rotation change may be deferred while there is a display change transition, so check
@@ -2767,29 +2766,6 @@
}
}
- private void validateVisibility() {
- for (int i = mTargets.size() - 1; i >= 0; --i) {
- if (reduceMode(mTargets.get(i).mReadyMode) != TRANSIT_CLOSE) {
- return;
- }
- }
- // All modes are CLOSE. The surfaces may be hidden by the animation unexpectedly.
- // If the window container should be visible, then recover it.
- mController.mStateValidators.add(() -> {
- for (int i = mTargets.size() - 1; i >= 0; --i) {
- final ChangeInfo change = mTargets.get(i);
- if (!change.mContainer.isVisibleRequested()
- || change.mContainer.mSurfaceControl == null) {
- continue;
- }
- Slog.e(TAG, "Force show for visible " + change.mContainer
- + " which may be hidden by transition unexpectedly");
- change.mContainer.getSyncTransaction().show(change.mContainer.mSurfaceControl);
- change.mContainer.scheduleAnimation();
- }
- });
- }
-
/**
* Returns {@code true} if the transition and the corresponding transaction should be applied
* on display thread. Currently, this only checks for display rotation change because the order
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 78afaa8..de7871e 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
@@ -960,6 +991,36 @@
mValidateDisplayVis.clear();
}
+ void onVisibleWithoutCollectingTransition(WindowContainer<?> wc, String caller) {
+ final boolean isPlaying = !mPlayingTransitions.isEmpty();
+ Slog.e(TAG, "Set visible without transition " + wc + " playing=" + isPlaying
+ + " caller=" + caller);
+ if (!isPlaying) {
+ enforceSurfaceVisible(wc);
+ return;
+ }
+ // Update surface visibility after the playing transitions are finished, so the last
+ // visibility won't be replaced by the finish transaction of transition.
+ mStateValidators.add(() -> {
+ if (wc.isVisibleRequested()) {
+ enforceSurfaceVisible(wc);
+ }
+ });
+ }
+
+ private void enforceSurfaceVisible(WindowContainer<?> wc) {
+ if (wc.mSurfaceControl == null) return;
+ wc.getSyncTransaction().show(wc.mSurfaceControl);
+ // Force showing the parents because they may be hidden by previous transition.
+ for (WindowContainer<?> p = wc.getParent(); p != null && p != wc.mDisplayContent;
+ p = p.getParent()) {
+ if (p.mSurfaceControl != null) {
+ p.getSyncTransaction().show(p.mSurfaceControl);
+ }
+ }
+ wc.scheduleAnimation();
+ }
+
/**
* Called when the transition has a complete set of participants for its operation. In other
* words, it is when the transition is "ready" but is still waiting for participants to draw.
@@ -1006,12 +1067,20 @@
// legacy sync
mSyncEngine.startSyncSet(queued.mLegacySync);
}
- // Post this so that the now-playing transition logic isn't interrupted.
- mAtm.mH.post(() -> {
- synchronized (mAtm.mGlobalLock) {
- queued.mOnStartCollect.onCollectStarted(true /* deferred */);
- }
- });
+ if (queued.mTransition != null
+ && queued.mTransition.mType == WindowManager.TRANSIT_SLEEP) {
+ // SLEEP transitions are special in that they don't collect anything (in fact if they
+ // do collect things it can cause problems). So, we need to run it's onCollectStarted
+ // immediately.
+ queued.mOnStartCollect.onCollectStarted(true /* deferred */);
+ } else {
+ // Post this so that the now-playing transition logic isn't interrupted.
+ mAtm.mH.post(() -> {
+ synchronized (mAtm.mGlobalLock) {
+ queued.mOnStartCollect.onCollectStarted(true /* deferred */);
+ }
+ });
+ }
}
void moveToPlaying(Transition transition) {
@@ -1164,18 +1233,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..5b9acb2
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WindowManagerFlags.java
@@ -0,0 +1,51 @@
+/*
+ * 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();
+
+ final boolean mWindowStateResizeItemFlag = Flags.windowStateResizeItemFlag();
+
+ /* 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 60dc84c..f339d24 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -249,6 +249,7 @@
import android.view.Gravity;
import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.ICrossWindowBlurEnabledListener;
+import android.view.IDecorViewGestureListener;
import android.view.IDisplayChangeWindowController;
import android.view.IDisplayFoldListener;
import android.view.IDisplayWindowInsetsController;
@@ -303,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;
@@ -546,6 +548,8 @@
@VisibleForTesting
WindowManagerPolicy mPolicy;
+ final WindowManagerFlags mFlags;
+
final IActivityManager mActivityManager;
final ActivityManagerInternal mAmInternal;
final UserManagerInternal mUmInternal;
@@ -1035,6 +1039,8 @@
sThreadPriorityBooster.reset();
}
+ SystemPerformanceHinter mSystemPerformanceHinter;
+
void openSurfaceTransaction() {
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "openSurfaceTransaction");
@@ -1152,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(
@@ -1328,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() {
@@ -2493,14 +2507,6 @@
outInsetsState.set(win.getCompatInsetsState(), true /* copySources */);
}
- // TODO (b/298562855): Remove this after identifying the reason why the frame is empty.
- if (win.mAttrs.providedInsets != null && win.getFrame().isEmpty()) {
- Slog.w(TAG, "Empty frame of " + win
- + " configChanged=" + configChanged
- + " frame=" + win.getFrame().toShortString()
- + " attrs=" + attrs);
- }
-
ProtoLog.v(WM_DEBUG_FOCUS, "Relayout of %s: focusMayChange=%b",
win, focusMayChange);
@@ -3108,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);
}
}
@@ -4637,8 +4648,9 @@
synchronized (mGlobalLock) {
final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
if (displayContent == null) {
- throw new IllegalArgumentException("Trying to register visibility event "
- + "for invalid display: " + displayId);
+ throw new IllegalArgumentException(
+ "Trying to register system gesture exclusion event for invalid display: "
+ + displayId);
}
displayContent.registerSystemGestureExclusionListener(listener);
}
@@ -4650,13 +4662,64 @@
synchronized (mGlobalLock) {
final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
if (displayContent == null) {
- throw new IllegalArgumentException("Trying to register visibility event "
- + "for invalid display: " + displayId);
+ throw new IllegalArgumentException(
+ "Trying to unregister system gesture exclusion event for invalid display: "
+ + displayId);
}
displayContent.unregisterSystemGestureExclusionListener(listener);
}
}
+ @Override
+ public void registerDecorViewGestureListener(
+ IDecorViewGestureListener listener, int displayId) {
+ if (!checkCallingPermission(android.Manifest.permission.MONITOR_INPUT,
+ "registerDecorViewGestureListener()")) {
+ throw new SecurityException("Requires MONITOR_INPUT permission");
+ }
+ synchronized (mGlobalLock) {
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ if (displayContent == null) {
+ throw new IllegalArgumentException(
+ "Trying to register DecorView gesture event listener"
+ + "for invalid display: "
+ + displayId);
+ }
+ displayContent.registerDecorViewGestureListener(listener);
+ }
+ }
+
+ @Override
+ public void unregisterDecorViewGestureListener(
+ IDecorViewGestureListener listener, int displayId) {
+ if (!checkCallingPermission(android.Manifest.permission.MONITOR_INPUT,
+ "unregisterSystemGestureExclusionListener()")) {
+ throw new SecurityException("Requires MONITOR_INPUT permission");
+ }
+ synchronized (mGlobalLock) {
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ if (displayContent == null) {
+ throw new IllegalArgumentException(
+ "Trying to unregister DecorView gesture event listener"
+ + "for invalid display: "
+ + displayId);
+ }
+ displayContent.unregisterDecorViewGestureListener(listener);
+ }
+ }
+
+ void reportDecorViewGestureChanged(Session session, IWindow window, boolean intercepted) {
+ synchronized (mGlobalLock) {
+ final WindowState win =
+ windowForClientLocked(session, window, false /* throwOnError */);
+ if (win == null) {
+ return;
+ }
+ win.getDisplayContent()
+ .updateDecorViewGestureIntercepted(win.mToken.token, intercepted);
+ }
+ }
+
void reportSystemGestureExclusionChanged(Session session, IWindow window,
List<Rect> exclusionRects) {
synchronized (mGlobalLock) {
@@ -8803,7 +8866,7 @@
void grantInputChannel(Session session, int callingUid, int callingPid, int displayId,
SurfaceControl surface, IWindow window, IBinder hostInputToken,
int flags, int privateFlags, int inputFeatures, int type, IBinder windowToken,
- IBinder focusGrantToken, String inputHandleName, InputChannel outInputChannel) {
+ IBinder inputTransferToken, String inputHandleName, InputChannel outInputChannel) {
final int sanitizedType = sanitizeWindowType(session, displayId, windowToken, type);
final InputApplicationHandle applicationHandle;
final String name;
@@ -8812,7 +8875,7 @@
EmbeddedWindowController.EmbeddedWindow win =
new EmbeddedWindowController.EmbeddedWindow(session, this, window,
mInputToWindowMap.get(hostInputToken), callingUid, callingPid,
- sanitizedType, displayId, focusGrantToken, inputHandleName,
+ sanitizedType, displayId, inputTransferToken, inputHandleName,
(flags & FLAG_NOT_FOCUSABLE) == 0);
win.openInputChannel(outInputChannel);
mEmbeddedWindowController.add(outInputChannel.getToken(), win);
@@ -8884,11 +8947,6 @@
h.inputConfig |= InputConfig.NOT_FOCUSABLE;
}
- // Check private trusted overlay flag to set trustedOverlay field of input window handle.
- if ((privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0) {
- h.inputConfig |= InputConfig.TRUSTED_OVERLAY;
- }
-
h.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
h.ownerUid = callingUid;
h.ownerPid = callingPid;
@@ -8908,6 +8966,8 @@
}
final SurfaceControl.Transaction t = mTransactionFactory.get();
+ // Check private trusted overlay flag to set trustedOverlay field of input window handle.
+ h.setTrustedOverlay(t, surface, (privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0);
t.setInputWindowInfo(surface, h);
t.apply();
t.close();
@@ -9139,10 +9199,10 @@
return mPossibleDisplayInfoMapper.getPossibleDisplayInfos(displayId);
}
- void grantEmbeddedWindowFocus(Session session, IBinder focusToken, boolean grantFocus) {
+ void grantEmbeddedWindowFocus(Session session, IBinder inputTransferToken, boolean grantFocus) {
synchronized (mGlobalLock) {
final EmbeddedWindowController.EmbeddedWindow embeddedWindow =
- mEmbeddedWindowController.getByFocusToken(focusToken);
+ mEmbeddedWindowController.getByInputTransferToken(inputTransferToken);
if (embeddedWindow == null) {
Slog.e(TAG, "Embedded window not found");
return;
@@ -9187,8 +9247,8 @@
}
}
- void grantEmbeddedWindowFocus(Session session, IWindow callingWindow, IBinder targetFocusToken,
- boolean grantFocus) {
+ void grantEmbeddedWindowFocus(Session session, IWindow callingWindow,
+ IBinder inputTransferToken, boolean grantFocus) {
synchronized (mGlobalLock) {
final WindowState hostWindow =
windowForClientLocked(session, callingWindow, false /* throwOnError*/);
@@ -9201,7 +9261,7 @@
return;
}
final EmbeddedWindowController.EmbeddedWindow embeddedWindow =
- mEmbeddedWindowController.getByFocusToken(targetFocusToken);
+ mEmbeddedWindowController.getByInputTransferToken(inputTransferToken);
if (embeddedWindow == null) {
Slog.e(TAG, "Embedded window not found");
return;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index ebef606..726d4d7 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -178,6 +178,7 @@
import static com.android.server.wm.WindowStateProto.VIEW_VISIBILITY;
import static com.android.server.wm.WindowStateProto.WINDOW_CONTAINER;
import static com.android.server.wm.WindowStateProto.WINDOW_FRAMES;
+import static com.android.window.flags.Flags.surfaceTrustedOverlay;
import android.annotation.CallSuper;
import android.annotation.NonNull;
@@ -185,6 +186,7 @@
import android.app.ActivityTaskManager;
import android.app.AppOpsManager;
import android.app.admin.DevicePolicyCache;
+import android.app.servertransaction.WindowStateResizeItem;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Matrix;
@@ -1110,7 +1112,9 @@
mInputWindowHandle.setName(getName());
mInputWindowHandle.setPackageName(mAttrs.packageName);
mInputWindowHandle.setLayoutParamsType(mAttrs.type);
- mInputWindowHandle.setTrustedOverlay(shouldWindowHandleBeTrusted(s));
+ if (!surfaceTrustedOverlay()) {
+ mInputWindowHandle.setTrustedOverlay(isWindowTrustedOverlay());
+ }
if (DEBUG) {
Slog.v(TAG, "Window " + this + " client=" + c.asBinder()
+ " token=" + token + " (" + mAttrs.token + ")" + " params=" + a);
@@ -1185,12 +1189,12 @@
}
}
- boolean shouldWindowHandleBeTrusted(Session s) {
+ public boolean isWindowTrustedOverlay() {
return InputMonitor.isTrustedOverlay(mAttrs.type)
|| ((mAttrs.privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0
- && s.mCanAddInternalSystemWindow)
+ && mSession.mCanAddInternalSystemWindow)
|| ((mAttrs.privateFlags & PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY) != 0
- && s.mCanCreateSystemApplicationOverlay);
+ && mSession.mCanCreateSystemApplicationOverlay);
}
int getTouchOcclusionMode() {
@@ -3729,30 +3733,44 @@
markRedrawForSyncReported();
- try {
- mClient.resized(mClientWindowFrames, reportDraw, mLastReportedConfiguration,
- getCompatInsetsState(), forceRelayout, alwaysConsumeSystemBars, displayId,
- syncWithBuffers ? mSyncSeqId : -1, isDragResizing);
- if (drawPending && prevRotation >= 0 && prevRotation != mLastReportedConfiguration
- .getMergedConfiguration().windowConfiguration.getRotation()) {
- mOrientationChangeRedrawRequestTime = SystemClock.elapsedRealtime();
- ProtoLog.v(WM_DEBUG_ORIENTATION,
- "Requested redraw for orientation change: %s", this);
+ if (mWmService.mFlags.mWindowStateResizeItemFlag) {
+ getProcess().scheduleClientTransactionItem(
+ WindowStateResizeItem.obtain(mClient, mClientWindowFrames, reportDraw,
+ mLastReportedConfiguration, getCompatInsetsState(), forceRelayout,
+ alwaysConsumeSystemBars, displayId,
+ syncWithBuffers ? mSyncSeqId : -1, isDragResizing));
+ onResizePostDispatched(drawPending, prevRotation, displayId);
+ } else {
+ // TODO(b/301870955): cleanup after launch
+ try {
+ mClient.resized(mClientWindowFrames, reportDraw, mLastReportedConfiguration,
+ getCompatInsetsState(), forceRelayout, alwaysConsumeSystemBars, displayId,
+ syncWithBuffers ? mSyncSeqId : -1, isDragResizing);
+ onResizePostDispatched(drawPending, prevRotation, displayId);
+ } catch (RemoteException e) {
+ // Cancel orientation change of this window to avoid blocking unfreeze display.
+ setOrientationChanging(false);
+ mLastFreezeDuration = (int) (SystemClock.elapsedRealtime()
+ - mWmService.mDisplayFreezeTime);
+ Slog.w(TAG, "Failed to report 'resized' to " + this + " due to " + e);
}
-
- if (mWmService.mAccessibilityController.hasCallbacks()) {
- mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(displayId);
- }
- } catch (RemoteException e) {
- // Cancel orientation change of this window to avoid blocking unfreeze display.
- setOrientationChanging(false);
- mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
- - mWmService.mDisplayFreezeTime);
- Slog.w(TAG, "Failed to report 'resized' to " + this + " due to " + e);
}
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
+ private void onResizePostDispatched(boolean drawPending, int prevRotation, int displayId) {
+ if (drawPending && prevRotation >= 0 && prevRotation != mLastReportedConfiguration
+ .getMergedConfiguration().windowConfiguration.getRotation()) {
+ mOrientationChangeRedrawRequestTime = SystemClock.elapsedRealtime();
+ ProtoLog.v(WM_DEBUG_ORIENTATION,
+ "Requested redraw for orientation change: %s", this);
+ }
+
+ if (mWmService.mAccessibilityController.hasCallbacks()) {
+ mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(displayId);
+ }
+ }
+
boolean inRelaunchingActivity() {
return mActivityRecord != null && mActivityRecord.isRelaunching();
}
@@ -5187,6 +5205,9 @@
updateFrameRateSelectionPriorityIfNeeded();
updateScaleIfNeeded();
mWinAnimator.prepareSurfaceLocked(getSyncTransaction());
+ if (surfaceTrustedOverlay()) {
+ getSyncTransaction().setTrustedOverlay(mSurfaceControl, isWindowTrustedOverlay());
+ }
}
super.prepareSurfaces();
}
@@ -5939,7 +5960,13 @@
}
boolean isTrustedOverlay() {
- return mInputWindowHandle.isTrustedOverlay();
+ if (surfaceTrustedOverlay()) {
+ WindowState parentWindow = getParentWindow();
+ return isWindowTrustedOverlay() || (parentWindow != null
+ && parentWindow.isWindowTrustedOverlay());
+ } else {
+ return mInputWindowHandle.isTrustedOverlay();
+ }
}
public boolean receiveFocusFromTapOutside() {
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index ec5378f..709d5e3 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -70,7 +70,7 @@
"com_android_server_UsbHostManager.cpp",
"com_android_server_vibrator_VibratorController.cpp",
"com_android_server_vibrator_VibratorManagerService.cpp",
- "com_android_server_PersistentDataBlockService.cpp",
+ "com_android_server_pdb_PersistentDataBlockService.cpp",
"com_android_server_am_LowMemDetector.cpp",
"com_android_server_pm_PackageManagerShellCommandDataLoader.cpp",
"com_android_server_sensor_SensorService.cpp",
@@ -78,6 +78,7 @@
"onload.cpp",
":lib_cachedAppOptimizer_native",
":lib_gameManagerService_native",
+ ":lib_oomConnection_native",
],
include_dirs: [
@@ -122,6 +123,7 @@
"libhardware_legacy",
"libhidlbase",
"libmeminfo",
+ "libmemevents",
"libmemtrackproxy",
"libmtp",
"libnativehelper",
@@ -239,3 +241,8 @@
"com_android_server_app_GameManagerService.cpp",
],
}
+
+filegroup {
+ name: "lib_oomConnection_native",
+ srcs: ["com_android_server_am_OomConnection.cpp",],
+}
diff --git a/services/core/jni/com_android_server_am_OomConnection.cpp b/services/core/jni/com_android_server_am_OomConnection.cpp
new file mode 100644
index 0000000..e892d23
--- /dev/null
+++ b/services/core/jni/com_android_server_am_OomConnection.cpp
@@ -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.
+ */
+
+#define LOG_TAG "OomConnection"
+
+#include <core_jni_helpers.h>
+#include <jni.h>
+#include <memevents/memevents.h>
+
+namespace android {
+
+// Used to cache the results of the JNI name lookup
+static struct {
+ jclass clazz;
+ jmethodID ctor;
+} sOomKillRecordInfo;
+
+static memevents::MemEventListener memevent_listener;
+
+/**
+ * Initialize listening and waiting for new out-of-memory (OOM) events to occur.
+ * Once a OOM event is detected, we then fetch the list of OOM kills, and return
+ * a corresponding java array with the information gathered.
+ *
+ * In the case that we encounter an error, we make sure to close the epfd, and
+ * the OOM file descriptor, by calling `deregisterAllEvents()`.
+ *
+ * @return list of `android.os.OomKillRecord`
+ * @throws java.lang.RuntimeException
+ */
+static jobjectArray android_server_am_OomConnection_waitOom(JNIEnv* env, jobject) {
+ const memevents::MemEvent oom_event = memevents::MemEvent::OOM_KILL;
+ if (!memevent_listener.registerEvent(oom_event)) {
+ memevent_listener.deregisterAllEvents();
+ jniThrowRuntimeException(env, "listener failed to register to OOM events");
+ return nullptr;
+ }
+
+ memevents::MemEvent event_received;
+ do {
+ event_received = memevent_listener.listen();
+ if (event_received == memevents::MemEvent::ERROR) {
+ memevent_listener.deregisterAllEvents();
+ jniThrowRuntimeException(env, "listener received error event");
+ return nullptr;
+ }
+ } while (event_received != oom_event);
+
+ std::vector<memevents::OomKill> oom_events;
+ if (!memevent_listener.getOomEvents(oom_events)) {
+ memevent_listener.deregisterAllEvents();
+ jniThrowRuntimeException(env, "Failed to get OOM events");
+ return nullptr;
+ }
+
+ jobjectArray java_oom_array =
+ env->NewObjectArray(oom_events.size(), sOomKillRecordInfo.clazz, nullptr);
+ if (java_oom_array == NULL) {
+ memevent_listener.deregisterAllEvents();
+ jniThrowRuntimeException(env, "Failed to create OomKillRecord array");
+ return nullptr;
+ }
+
+ for (int i = 0; i < oom_events.size(); i++) {
+ const memevents::OomKill oom_event = oom_events[i];
+ jstring process_name = env->NewStringUTF(oom_event.process_name);
+ if (process_name == NULL) {
+ memevent_listener.deregisterAllEvents();
+ jniThrowRuntimeException(env, "Failed creating java string for process name");
+ }
+ jobject java_oom_kill = env->NewObject(sOomKillRecordInfo.clazz, sOomKillRecordInfo.ctor,
+ oom_event.timestamp_ms, oom_event.pid, oom_event.uid,
+ process_name, oom_event.oom_score_adj);
+ if (java_oom_kill == NULL) {
+ memevent_listener.deregisterAllEvents();
+ jniThrowRuntimeException(env, "Failed to create OomKillRecord object");
+ return java_oom_array;
+ }
+ env->SetObjectArrayElement(java_oom_array, i, java_oom_kill);
+ }
+ return java_oom_array;
+}
+
+static const JNINativeMethod sOomConnectionMethods[] = {
+ /* name, signature, funcPtr */
+ {"waitOom", "()[Landroid/os/OomKillRecord;",
+ (void*)android_server_am_OomConnection_waitOom},
+};
+
+int register_android_server_am_OomConnection(JNIEnv* env) {
+ sOomKillRecordInfo.clazz = FindClassOrDie(env, "android/os/OomKillRecord");
+ sOomKillRecordInfo.clazz = MakeGlobalRefOrDie(env, sOomKillRecordInfo.clazz);
+
+ sOomKillRecordInfo.ctor =
+ GetMethodIDOrDie(env, sOomKillRecordInfo.clazz, "<init>", "(JIILjava/lang/String;S)V");
+
+ return RegisterMethodsOrDie(env, "com/android/server/am/OomConnection", sOomConnectionMethods,
+ NELEM(sOomConnectionMethods));
+}
+} // namespace android
\ No newline at end of file
diff --git a/services/core/jni/com_android_server_PersistentDataBlockService.cpp b/services/core/jni/com_android_server_pdb_PersistentDataBlockService.cpp
similarity index 85%
rename from services/core/jni/com_android_server_PersistentDataBlockService.cpp
rename to services/core/jni/com_android_server_pdb_PersistentDataBlockService.cpp
index 97e69fb..fc5a113 100644
--- a/services/core/jni/com_android_server_PersistentDataBlockService.cpp
+++ b/services/core/jni/com_android_server_pdb_PersistentDataBlockService.cpp
@@ -76,7 +76,7 @@
return ret;
}
- static jlong com_android_server_PersistentDataBlockService_getBlockDeviceSize(JNIEnv *env, jclass, jstring jpath)
+ static jlong com_android_server_pdb_PersistentDataBlockService_getBlockDeviceSize(JNIEnv *env, jclass, jstring jpath)
{
ScopedUtfChars path(env, jpath);
int fd = open(path.c_str(), O_RDONLY);
@@ -91,7 +91,7 @@
return size;
}
- static int com_android_server_PersistentDataBlockService_wipe(JNIEnv *env, jclass, jstring jpath) {
+ static int com_android_server_pdb_PersistentDataBlockService_wipe(JNIEnv *env, jclass, jstring jpath) {
ScopedUtfChars path(env, jpath);
int fd = open(path.c_str(), O_WRONLY);
@@ -107,13 +107,13 @@
static const JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
- {"nativeGetBlockDeviceSize", "(Ljava/lang/String;)J", (void*)com_android_server_PersistentDataBlockService_getBlockDeviceSize},
- {"nativeWipe", "(Ljava/lang/String;)I", (void*)com_android_server_PersistentDataBlockService_wipe},
+ {"nativeGetBlockDeviceSize", "(Ljava/lang/String;)J", (void*)com_android_server_pdb_PersistentDataBlockService_getBlockDeviceSize},
+ {"nativeWipe", "(Ljava/lang/String;)I", (void*)com_android_server_pdb_PersistentDataBlockService_wipe},
};
- int register_android_server_PersistentDataBlockService(JNIEnv* env)
+ int register_android_server_pdb_PersistentDataBlockService(JNIEnv* env)
{
- return jniRegisterNativeMethods(env, "com/android/server/PersistentDataBlockService",
+ return jniRegisterNativeMethods(env, "com/android/server/pdb/PersistentDataBlockService",
sMethods, NELEM(sMethods));
}
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index f6f6737..11734da 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -47,11 +47,12 @@
int register_android_server_devicepolicy_CryptoTestHelper(JNIEnv*);
int register_android_server_tv_TvUinputBridge(JNIEnv* env);
int register_android_server_tv_TvInputHal(JNIEnv* env);
-int register_android_server_PersistentDataBlockService(JNIEnv* env);
+int register_android_server_pdb_PersistentDataBlockService(JNIEnv* env);
int register_android_server_Watchdog(JNIEnv* env);
int register_android_server_HardwarePropertiesManagerService(JNIEnv* env);
int register_android_server_SyntheticPasswordManager(JNIEnv* env);
int register_android_hardware_display_DisplayViewport(JNIEnv* env);
+int register_android_server_am_OomConnection(JNIEnv* env);
int register_android_server_am_CachedAppOptimizer(JNIEnv* env);
int register_android_server_am_LowMemDetector(JNIEnv* env);
int register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(JNIEnv* env);
@@ -107,11 +108,12 @@
register_android_server_BatteryStatsService(env);
register_android_server_tv_TvUinputBridge(env);
register_android_server_tv_TvInputHal(env);
- register_android_server_PersistentDataBlockService(env);
+ register_android_server_pdb_PersistentDataBlockService(env);
register_android_server_HardwarePropertiesManagerService(env);
register_android_server_storage_AppFuse(env);
register_android_server_SyntheticPasswordManager(env);
register_android_hardware_display_DisplayViewport(env);
+ register_android_server_am_OomConnection(env);
register_android_server_am_CachedAppOptimizer(env);
register_android_server_am_LowMemDetector(env);
register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(env);
diff --git a/services/core/jni/tvinput/JTvInputHal.cpp b/services/core/jni/tvinput/JTvInputHal.cpp
index 68e2c9a..c736617 100644
--- a/services/core/jni/tvinput/JTvInputHal.cpp
+++ b/services/core/jni/tvinput/JTvInputHal.cpp
@@ -147,7 +147,6 @@
}
int JTvInputHal::setTvMessageEnabled(int deviceId, int streamId, int type, bool enabled) {
- Mutex::Autolock autoLock(&mLock);
if (!mTvInput->setTvMessageEnabled(deviceId, streamId,
static_cast<AidlTvMessageEventType>(type), enabled)
.isOk()) {
@@ -188,7 +187,7 @@
void JTvInputHal::onDeviceAvailable(const TvInputDeviceInfoWrapper& info) {
{
- Mutex::Autolock autoLock(&mLock);
+ Mutex::Autolock autoLock(&mStreamLock);
mConnections.add(info.deviceId, KeyedVector<int, Connection>());
}
JNIEnv* env = AndroidRuntime::getJNIEnv();
@@ -275,7 +274,7 @@
void JTvInputHal::onDeviceUnavailable(int deviceId) {
{
- Mutex::Autolock autoLock(&mLock);
+ Mutex::Autolock autoLock(&mStreamLock);
KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
for (size_t i = 0; i < connections.size(); ++i) {
removeStream(deviceId, connections.keyAt(i));
@@ -289,7 +288,7 @@
void JTvInputHal::onStreamConfigurationsChanged(int deviceId, int cableConnectionStatus) {
{
- Mutex::Autolock autoLock(&mLock);
+ Mutex::Autolock autoLock(&mStreamLock);
KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
for (size_t i = 0; i < connections.size(); ++i) {
removeStream(deviceId, connections.keyAt(i));
@@ -330,7 +329,7 @@
void JTvInputHal::onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded) {
sp<BufferProducerThread> thread;
{
- Mutex::Autolock autoLock(&mLock);
+ Mutex::Autolock autoLock(&mStreamLock);
KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
Connection& connection = connections.editValueFor(streamId);
if (connection.mThread == NULL) {
diff --git a/services/core/jni/tvinput/JTvInputHal.h b/services/core/jni/tvinput/JTvInputHal.h
index b7b4b16..1d8d162 100644
--- a/services/core/jni/tvinput/JTvInputHal.h
+++ b/services/core/jni/tvinput/JTvInputHal.h
@@ -220,7 +220,6 @@
void onTvMessage(int deviceId, int streamId, AidlTvMessageEventType type,
AidlTvMessage& message, signed char data[], int dataLength);
- Mutex mLock;
Mutex mStreamLock;
jweak mThiz;
sp<Looper> mLooper;
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index d0a9c45..debd891 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -46,6 +46,8 @@
<xs:annotation name="nonnull"/>
<xs:annotation name="final"/>
</xs:element>
+ <xs:element type="powerThrottlingConfig" name="powerThrottlingConfig" minOccurs="0"
+ maxOccurs="1"/>
<xs:element type="luxThrottling" name="luxThrottling" minOccurs="0"
maxOccurs="1"/>
<xs:element type="highBrightnessMode" name="highBrightnessMode" minOccurs="0"
@@ -350,6 +352,43 @@
</xs:sequence>
</xs:complexType>
+ <xs:complexType name="powerThrottlingMap">
+ <xs:sequence>
+ <xs:element name="powerThrottlingPoint" type="powerThrottlingPoint" maxOccurs="unbounded" minOccurs="1">
+ <xs:annotation name="nonnull"/>
+ <xs:annotation name="final"/>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="id" type="xs:string"/>
+ </xs:complexType>
+
+ <xs:complexType name="powerThrottlingPoint">
+ <xs:sequence>
+ <xs:element type="thermalStatus" name="thermalStatus">
+ <xs:annotation name="nonnull"/>
+ <xs:annotation name="final"/>
+ </xs:element>
+ <xs:element type="nonNegativeDecimal" name="powerQuotaMilliWatts">
+ <xs:annotation name="nonnull"/>
+ <xs:annotation name="final"/>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:complexType name="powerThrottlingConfig">
+ <xs:element type="nonNegativeDecimal" name="brightnessLowestCapAllowed">
+ <xs:annotation name="nonnull"/>
+ <xs:annotation name="final"/>
+ </xs:element>
+ <xs:element name="pollingWindowMillis" type="xs:nonNegativeInteger">
+ <xs:annotation name="nonnull"/>
+ <xs:annotation name="final"/>
+ </xs:element>
+ <xs:element type="powerThrottlingMap" name="powerThrottlingMap" maxOccurs="unbounded">
+ <xs:annotation name="final"/>
+ </xs:element>
+ </xs:complexType>
+
<xs:complexType name="nitsMap">
<xs:sequence>
<xs:element name="point" type="point" maxOccurs="unbounded" minOccurs="2">
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 949b1f2..2d27f0c 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -106,6 +106,7 @@
method public final com.android.server.display.config.SensorDetails getLightSensor();
method public com.android.server.display.config.LuxThrottling getLuxThrottling();
method @Nullable public final String getName();
+ method public com.android.server.display.config.PowerThrottlingConfig getPowerThrottlingConfig();
method public final com.android.server.display.config.SensorDetails getProxSensor();
method public com.android.server.display.config.DisplayQuirks getQuirks();
method public com.android.server.display.config.RefreshRateConfigs getRefreshRate();
@@ -138,6 +139,7 @@
method public final void setLightSensor(com.android.server.display.config.SensorDetails);
method public void setLuxThrottling(com.android.server.display.config.LuxThrottling);
method public final void setName(@Nullable String);
+ method public void setPowerThrottlingConfig(com.android.server.display.config.PowerThrottlingConfig);
method public final void setProxSensor(com.android.server.display.config.SensorDetails);
method public void setQuirks(com.android.server.display.config.DisplayQuirks);
method public void setRefreshRate(com.android.server.display.config.RefreshRateConfigs);
@@ -246,6 +248,30 @@
method public final void setValue(@NonNull java.math.BigDecimal);
}
+ public class PowerThrottlingConfig {
+ ctor public PowerThrottlingConfig();
+ method @NonNull public final java.math.BigDecimal getBrightnessLowestCapAllowed();
+ method @NonNull public final java.math.BigInteger getPollingWindowMillis();
+ method public final java.util.List<com.android.server.display.config.PowerThrottlingMap> getPowerThrottlingMap();
+ method public final void setBrightnessLowestCapAllowed(@NonNull java.math.BigDecimal);
+ method public final void setPollingWindowMillis(@NonNull java.math.BigInteger);
+ }
+
+ public class PowerThrottlingMap {
+ ctor public PowerThrottlingMap();
+ method public String getId();
+ method @NonNull public final java.util.List<com.android.server.display.config.PowerThrottlingPoint> getPowerThrottlingPoint();
+ method public void setId(String);
+ }
+
+ public class PowerThrottlingPoint {
+ ctor public PowerThrottlingPoint();
+ method @NonNull public final java.math.BigDecimal getPowerQuotaMilliWatts();
+ method @NonNull public final com.android.server.display.config.ThermalStatus getThermalStatus();
+ method public final void setPowerQuotaMilliWatts(@NonNull java.math.BigDecimal);
+ method public final void setThermalStatus(@NonNull com.android.server.display.config.ThermalStatus);
+ }
+
public enum PredefinedBrightnessLimitNames {
method public String getRawName();
enum_constant public static final com.android.server.display.config.PredefinedBrightnessLimitNames _default;
diff --git a/services/core/xsd/display-layout-config/display-layout-config.xsd b/services/core/xsd/display-layout-config/display-layout-config.xsd
index 57b5d00..4e95465 100644
--- a/services/core/xsd/display-layout-config/display-layout-config.xsd
+++ b/services/core/xsd/display-layout-config/display-layout-config.xsd
@@ -52,6 +52,7 @@
<xs:element name="address" type="xs:nonNegativeInteger"/>
<xs:element name="position" type="xs:string" minOccurs="0" maxOccurs="1" />
<xs:element name="brightnessThrottlingMapId" type="xs:string" minOccurs="0" maxOccurs="1" />
+ <xs:element name="powerThrottlingMapId" type="xs:string" minOccurs="0" maxOccurs="1" />
<xs:element name="refreshRateThermalThrottlingMapId" type="xs:string" minOccurs="0" />
<xs:element name="leadDisplayAddress" type="xs:nonNegativeInteger" minOccurs="0" maxOccurs="1" />
</xs:sequence>
diff --git a/services/core/xsd/display-layout-config/schema/current.txt b/services/core/xsd/display-layout-config/schema/current.txt
index 2d4f7a4..195cae5 100644
--- a/services/core/xsd/display-layout-config/schema/current.txt
+++ b/services/core/xsd/display-layout-config/schema/current.txt
@@ -8,6 +8,7 @@
method public String getDisplayGroup();
method public java.math.BigInteger getLeadDisplayAddress();
method public String getPosition();
+ method public String getPowerThrottlingMapId();
method public String getRefreshRateThermalThrottlingMapId();
method public String getRefreshRateZoneId();
method public boolean isDefaultDisplay();
@@ -19,6 +20,7 @@
method public void setEnabled(boolean);
method public void setLeadDisplayAddress(java.math.BigInteger);
method public void setPosition(String);
+ method public void setPowerThrottlingMapId(String);
method public void setRefreshRateThermalThrottlingMapId(String);
method public void setRefreshRateZoneId(String);
}
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
index b90f08e..3c190bf 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
@@ -32,6 +32,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.ResultReceiver;
+import android.os.UserHandle;
import android.service.credentials.CredentialProviderInfoFactory;
import android.util.Slog;
@@ -171,7 +172,9 @@
.setAction(UUID.randomUUID().toString());
//TODO: Create unique pending intent using request code and cancel any pre-existing pending
// intents
- return PendingIntent.getActivity(
- mContext, /*requestCode=*/0, intent, PendingIntent.FLAG_IMMUTABLE);
+ return PendingIntent.getActivityAsUser(
+ mContext, /*requestCode=*/0, intent,
+ PendingIntent.FLAG_IMMUTABLE, /*options=*/null,
+ UserHandle.of(mUserId));
}
}
diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
index 3eb6718..7bd1cc4 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
@@ -31,6 +31,7 @@
import android.credentials.ui.GetCredentialProviderData;
import android.credentials.ui.ProviderPendingIntentResponse;
import android.os.ICancellationSignal;
+import android.service.autofill.Flags;
import android.service.credentials.Action;
import android.service.credentials.BeginGetCredentialOption;
import android.service.credentials.BeginGetCredentialRequest;
@@ -42,6 +43,7 @@
import android.service.credentials.RemoteEntry;
import android.util.Pair;
import android.util.Slog;
+import android.view.autofill.AutofillId;
import java.util.ArrayList;
import java.util.HashMap;
@@ -379,13 +381,23 @@
// but does not resolve to a valid option. For now, not skipping it because
// it may be possible that the provider adds their own extras and expects to receive
// those and complete the flow.
- if (mBeginGetOptionToCredentialOptionMap.get(id) == null) {
+ Intent intent = new Intent();
+ CredentialOption credentialOption = mBeginGetOptionToCredentialOptionMap.get(id);
+ if (credentialOption == null) {
Slog.w(TAG, "Id from Credential Entry does not resolve to a valid option");
- return new Intent();
+ return intent;
}
- return new Intent().putExtra(CredentialProviderService.EXTRA_GET_CREDENTIAL_REQUEST,
+ AutofillId autofillId = credentialOption
+ .getCandidateQueryData()
+ .getParcelable(CredentialProviderService.EXTRA_AUTOFILL_ID, AutofillId.class);
+ if (autofillId != null && Flags.autofillCredmanIntegration()) {
+ intent.putExtra(CredentialProviderService.EXTRA_AUTOFILL_ID, autofillId);
+ }
+ return intent.putExtra(
+ CredentialProviderService.EXTRA_GET_CREDENTIAL_REQUEST,
new GetCredentialRequest(
- mCallingAppInfo, List.of(mBeginGetOptionToCredentialOptionMap.get(id))));
+ mCallingAppInfo,
+ List.of(credentialOption)));
}
private Intent setUpFillInIntentWithQueryRequest() {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 43e47d7..5a620a3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -486,7 +486,6 @@
import com.android.server.LocalManagerRegistry;
import com.android.server.LocalServices;
import com.android.server.LockGuard;
-import com.android.server.PersistentDataBlockManagerInternal;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
@@ -494,6 +493,7 @@
import com.android.server.devicepolicy.flags.FlagUtils;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.net.NetworkPolicyManagerInternal;
+import com.android.server.pdb.PersistentDataBlockManagerInternal;
import com.android.server.pm.DefaultCrossProfileIntentFilter;
import com.android.server.pm.DefaultCrossProfileIntentFiltersUtils;
import com.android.server.pm.PackageManagerLocal;
@@ -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/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
index 16876ac..eb893fc 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
@@ -251,7 +251,14 @@
private int runSetDeviceOwner(PrintWriter pw) {
parseArgs();
- mService.setActiveAdmin(mComponent, /* refreshing= */ true, mUserId);
+ boolean isAdminAdded = false;
+ try {
+ mService.setActiveAdmin(mComponent, /* refreshing= */ false, mUserId);
+ isAdminAdded = true;
+ } catch (IllegalArgumentException e) {
+ pw.printf("%s was already an admin for user %d. No need to set it again.\n",
+ mComponent.flattenToShortString(), mUserId);
+ }
try {
if (!mService.setDeviceOwner(mComponent, mUserId,
@@ -260,8 +267,10 @@
"Can't set package " + mComponent + " as device owner.");
}
} catch (Exception e) {
- // Need to remove the admin that we just added.
- mService.removeActiveAdmin(mComponent, UserHandle.USER_SYSTEM);
+ if (isAdminAdded) {
+ // Need to remove the admin that we just added.
+ mService.removeActiveAdmin(mComponent, mUserId);
+ }
throw e;
}
diff --git a/services/foldables/devicestateprovider/tests/Android.bp b/services/foldables/devicestateprovider/tests/Android.bp
index a8db05e..84a6df3 100644
--- a/services/foldables/devicestateprovider/tests/Android.bp
+++ b/services/foldables/devicestateprovider/tests/Android.bp
@@ -20,11 +20,11 @@
"foldable-device-state-provider",
"androidx.test.rules",
"junit",
- "truth-prebuilt",
+ "truth",
"mockito-target-extended-minus-junit4",
"androidx.test.uiautomator_uiautomator",
"androidx.test.ext.junit",
"testables",
],
- test_suites: ["device-tests"]
+ test_suites: ["device-tests"],
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 57fa12d..c26aee8 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;
@@ -160,6 +161,7 @@
import com.android.server.os.DeviceIdentifiersPolicyService;
import com.android.server.os.NativeTombstoneManagerService;
import com.android.server.os.SchedulingPolicyService;
+import com.android.server.pdb.PersistentDataBlockService;
import com.android.server.people.PeopleService;
import com.android.server.permission.access.AccessCheckingService;
import com.android.server.pm.ApexManager;
@@ -264,6 +266,8 @@
"com.android.server.backup.BackupManagerService$Lifecycle";
private static final String APPWIDGET_SERVICE_CLASS =
"com.android.server.appwidget.AppWidgetService";
+ private static final String ARC_SYSTEM_HEALTH_SERVICE =
+ "com.android.server.arc.health.ArcSystemHealthService";
private static final String VOICE_RECOGNITION_MANAGER_SERVICE_CLASS =
"com.android.server.voiceinteraction.VoiceInteractionManagerService";
private static final String APP_HIBERNATION_SERVICE_CLASS =
@@ -914,6 +918,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".
@@ -1287,6 +1299,12 @@
}
}
+ if (Build.IS_ARC) {
+ t.traceBegin("StartArcSystemHealthService");
+ mSystemServiceManager.startService(ARC_SYSTEM_HEALTH_SERVICE);
+ t.traceEnd();
+ }
+
t.traceBegin("StartUserManagerService");
mSystemServiceManager.startService(UserManagerService.LifeCycle.class);
t.traceEnd();
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index 486ddb4..a8902fc 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -1391,7 +1391,6 @@
private static final String[] EMPTY_STRING_ARRAY = new String[0];
private void addLegacyPackageDeviceServer(ServiceInfo serviceInfo, int userId) {
- Log.d(TAG, "addLegacyPackageDeviceServer()" + userId);
XmlResourceParser parser = null;
try {
@@ -1529,7 +1528,6 @@
@RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
private void addUmpPackageDeviceServer(ServiceInfo serviceInfo, int userId) {
- Log.d(TAG, "addUmpPackageDeviceServer()" + userId);
XmlResourceParser parser = null;
try {
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/robotests/backup/Android.bp b/services/robotests/backup/Android.bp
index e04dd68..8b9efb3 100644
--- a/services/robotests/backup/Android.bp
+++ b/services/robotests/backup/Android.bp
@@ -59,7 +59,7 @@
"mockito-robolectric-prebuilt",
"platform-test-annotations",
"testng",
- "truth-prebuilt",
+ "truth",
],
instrumentation_for: "BackupFrameworksServicesLib",
diff --git a/services/tests/InputMethodSystemServerTests/Android.bp b/services/tests/InputMethodSystemServerTests/Android.bp
index 36446f6..ffe6dc5 100644
--- a/services/tests/InputMethodSystemServerTests/Android.bp
+++ b/services/tests/InputMethodSystemServerTests/Android.bp
@@ -44,7 +44,7 @@
"service-permission.stubs.system_server",
"servicestests-core-utils",
"servicestests-utils-mockito-extended",
- "truth-prebuilt",
+ "truth",
],
libs: [
@@ -92,7 +92,7 @@
"service-permission.stubs.system_server",
"servicestests-core-utils",
"servicestests-utils-mockito-extended",
- "truth-prebuilt",
+ "truth",
"SimpleImeTestingLib",
"SimpleImeImsLib",
],
diff --git a/services/tests/PackageManager/packageinstaller/Android.bp b/services/tests/PackageManager/packageinstaller/Android.bp
index 35d754b..e8fce8e 100644
--- a/services/tests/PackageManager/packageinstaller/Android.bp
+++ b/services/tests/PackageManager/packageinstaller/Android.bp
@@ -32,7 +32,7 @@
"androidx.test.runner",
"junit",
"kotlin-test",
- "truth-prebuilt",
+ "truth",
],
platform_apis: true,
test_suites: ["device-tests"],
diff --git a/services/tests/PackageManagerComponentOverrideTests/Android.bp b/services/tests/PackageManagerComponentOverrideTests/Android.bp
index bc36970..00850a5 100644
--- a/services/tests/PackageManagerComponentOverrideTests/Android.bp
+++ b/services/tests/PackageManagerComponentOverrideTests/Android.bp
@@ -38,7 +38,7 @@
"service-permission.stubs.system_server",
"servicestests-utils-mockito-extended",
"testng", // TODO: remove once Android migrates to JUnit 4.12, which provides assertThrows
- "truth-prebuilt",
+ "truth",
],
jni_libs: [
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/Android.bp b/services/tests/PackageManagerServiceTests/appenumeration/Android.bp
index 9c4e6fd..ad7af44 100644
--- a/services/tests/PackageManagerServiceTests/appenumeration/Android.bp
+++ b/services/tests/PackageManagerServiceTests/appenumeration/Android.bp
@@ -29,7 +29,7 @@
static_libs: [
"compatibility-device-util-axt",
"androidx.test.runner",
- "truth-prebuilt",
+ "truth",
"Harrier",
],
platform_apis: true,
diff --git a/services/tests/PackageManagerServiceTests/host/Android.bp b/services/tests/PackageManagerServiceTests/host/Android.bp
index ce28682..6eacef7 100644
--- a/services/tests/PackageManagerServiceTests/host/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/Android.bp
@@ -30,7 +30,7 @@
libs: [
"tradefed",
"junit",
- "truth-prebuilt",
+ "truth",
],
static_libs: [
"ApexInstallHelper",
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp
index 462c580..cea9c59 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp
@@ -38,6 +38,6 @@
"junit-params",
"androidx.test.ext.junit",
"androidx.test.rules",
- "truth-prebuilt",
+ "truth",
],
}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/Android.bp
index 5718474..ed5f2b5 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/Android.bp
@@ -28,6 +28,6 @@
"androidx.test.runner",
"junit",
"kotlin-test",
- "truth-prebuilt",
+ "truth",
],
}
diff --git a/services/tests/PackageManagerServiceTests/server/Android.bp b/services/tests/PackageManagerServiceTests/server/Android.bp
index a1d846e..3aca1ca 100644
--- a/services/tests/PackageManagerServiceTests/server/Android.bp
+++ b/services/tests/PackageManagerServiceTests/server/Android.bp
@@ -41,7 +41,7 @@
"mockito-target-minus-junit4",
"platform-test-annotations",
"ShortcutManagerTestUtils",
- "truth-prebuilt",
+ "truth",
"testables",
"platformprotosnano",
"framework-protos",
@@ -51,7 +51,7 @@
"servicestests-utils",
"service-permission.impl",
"testng",
- "truth-prebuilt",
+ "truth",
"junit",
"junit-params",
"platform-compat-test-rules",
diff --git a/services/tests/PackageManagerServiceTests/unit/Android.bp b/services/tests/PackageManagerServiceTests/unit/Android.bp
index 9b3b8c35..85059838 100644
--- a/services/tests/PackageManagerServiceTests/unit/Android.bp
+++ b/services/tests/PackageManagerServiceTests/unit/Android.bp
@@ -37,7 +37,7 @@
"services.core",
"servicestests-utils",
"servicestests-core-utils",
- "truth-prebuilt",
+ "truth",
],
jni_libs: [
"libdexmakerjvmtiagent",
diff --git a/services/tests/RemoteProvisioningServiceTests/Android.bp b/services/tests/RemoteProvisioningServiceTests/Android.bp
index fc2c085..19c9136 100644
--- a/services/tests/RemoteProvisioningServiceTests/Android.bp
+++ b/services/tests/RemoteProvisioningServiceTests/Android.bp
@@ -30,8 +30,8 @@
"mockito-target",
"service-rkp.impl",
"services.core",
- "truth-prebuilt",
- "truth-java8-extension-jar",
+ "truth",
+ "truth-java8-extension",
],
test_suites: [
"device-tests",
diff --git a/services/tests/apexsystemservices/Android.bp b/services/tests/apexsystemservices/Android.bp
index e724e804..9dacfea 100644
--- a/services/tests/apexsystemservices/Android.bp
+++ b/services/tests/apexsystemservices/Android.bp
@@ -34,7 +34,7 @@
"compatibility-host-util",
"cts-install-lib-host",
"frameworks-base-hostutils",
- "truth-prebuilt",
+ "truth",
"modules-utils-build-testing",
],
test_suites: [
diff --git a/services/tests/displayservicetests/Android.bp b/services/tests/displayservicetests/Android.bp
index 0275c7d..6e4069f 100644
--- a/services/tests/displayservicetests/Android.bp
+++ b/services/tests/displayservicetests/Android.bp
@@ -40,6 +40,7 @@
"services.core",
"servicestests-utils",
"testables",
+ "TestParameterInjector",
],
defaults: [
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
index 6ef150c..c37d21a 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -179,6 +179,89 @@
}
@Test
+ public void testPowerThrottlingConfigFromDisplayConfig() throws IOException {
+ setupDisplayDeviceConfigFromDisplayConfigFile();
+
+ DisplayDeviceConfig.PowerThrottlingConfigData powerThrottlingConfigData =
+ mDisplayDeviceConfig.getPowerThrottlingConfigData();
+ assertNotNull(powerThrottlingConfigData);
+ assertEquals(0.1f, powerThrottlingConfigData.brightnessLowestCapAllowed, SMALL_DELTA);
+ assertEquals(10, powerThrottlingConfigData.pollingWindowMillis);
+ }
+
+ @Test
+ public void testPowerThrottlingDataFromDisplayConfig() throws IOException {
+ setupDisplayDeviceConfigFromDisplayConfigFile();
+
+ List<DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel>
+ defaultThrottlingLevels = new ArrayList<>();
+ defaultThrottlingLevels.add(
+ new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.light), 800f
+ ));
+ defaultThrottlingLevels.add(
+ new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.moderate), 600f
+ ));
+ defaultThrottlingLevels.add(
+ new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.severe), 400f
+ ));
+ defaultThrottlingLevels.add(
+ new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.critical), 200f
+ ));
+ defaultThrottlingLevels.add(
+ new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.emergency), 100f
+ ));
+ defaultThrottlingLevels.add(
+ new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.shutdown), 50f
+ ));
+
+ DisplayDeviceConfig.PowerThrottlingData defaultThrottlingData =
+ new DisplayDeviceConfig.PowerThrottlingData(defaultThrottlingLevels);
+
+ List<DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel>
+ concurrentThrottlingLevels = new ArrayList<>();
+ concurrentThrottlingLevels.add(
+ new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.light), 800f
+ ));
+ concurrentThrottlingLevels.add(
+ new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.moderate), 600f
+ ));
+ concurrentThrottlingLevels.add(
+ new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.severe), 400f
+ ));
+ concurrentThrottlingLevels.add(
+ new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.critical), 200f
+ ));
+ concurrentThrottlingLevels.add(
+ new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.emergency), 100f
+ ));
+ concurrentThrottlingLevels.add(
+ new DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel(
+ DisplayDeviceConfig.convertThermalStatus(ThermalStatus.shutdown), 50f
+ ));
+ DisplayDeviceConfig.PowerThrottlingData concurrentThrottlingData =
+ new DisplayDeviceConfig.PowerThrottlingData(concurrentThrottlingLevels);
+
+ HashMap<String, DisplayDeviceConfig.PowerThrottlingData> throttlingDataMap =
+ new HashMap<>(2);
+ throttlingDataMap.put("default", defaultThrottlingData);
+ throttlingDataMap.put("concurrent", concurrentThrottlingData);
+
+ assertEquals(throttlingDataMap,
+ mDisplayDeviceConfig.getPowerThrottlingDataMapByThrottlingId());
+ }
+
+ @Test
public void testConfigValuesFromConfigResource() {
setupDisplayDeviceConfigFromConfigResourceFile();
verifyConfigValuesFromConfigResource();
@@ -845,6 +928,64 @@
+ "</screenBrightnessRampSlowIncreaseIdle>\n";
}
+ private String getPowerThrottlingConfig() {
+ return "<powerThrottlingConfig >\n"
+ + "<brightnessLowestCapAllowed>0.1</brightnessLowestCapAllowed>\n"
+ + "<pollingWindowMillis>10</pollingWindowMillis>\n"
+ + "<powerThrottlingMap>\n"
+ + "<powerThrottlingPoint>\n"
+ + "<thermalStatus>light</thermalStatus>\n"
+ + "<powerQuotaMilliWatts>800</powerQuotaMilliWatts>\n"
+ + "</powerThrottlingPoint>\n"
+ + "<powerThrottlingPoint>\n"
+ + "<thermalStatus>moderate</thermalStatus>\n"
+ + "<powerQuotaMilliWatts>600</powerQuotaMilliWatts>\n"
+ + "</powerThrottlingPoint>\n"
+ + "<powerThrottlingPoint>\n"
+ + "<thermalStatus>severe</thermalStatus>\n"
+ + "<powerQuotaMilliWatts>400</powerQuotaMilliWatts>\n"
+ + "</powerThrottlingPoint>\n"
+ + "<powerThrottlingPoint>\n"
+ + "<thermalStatus>critical</thermalStatus>\n"
+ + "<powerQuotaMilliWatts>200</powerQuotaMilliWatts>\n"
+ + "</powerThrottlingPoint>\n"
+ + "<powerThrottlingPoint>\n"
+ + "<thermalStatus>emergency</thermalStatus>\n"
+ + "<powerQuotaMilliWatts>100</powerQuotaMilliWatts>\n"
+ + "</powerThrottlingPoint>\n"
+ + "<powerThrottlingPoint>\n"
+ + "<thermalStatus>shutdown</thermalStatus>\n"
+ + "<powerQuotaMilliWatts>50</powerQuotaMilliWatts>\n"
+ + "</powerThrottlingPoint>\n"
+ + "</powerThrottlingMap>\n"
+ + "<powerThrottlingMap id=\"concurrent\">\n"
+ + "<powerThrottlingPoint>\n"
+ + "<thermalStatus>light</thermalStatus>\n"
+ + "<powerQuotaMilliWatts>800</powerQuotaMilliWatts>\n"
+ + "</powerThrottlingPoint>\n"
+ + "<powerThrottlingPoint>\n"
+ + "<thermalStatus>moderate</thermalStatus>\n"
+ + "<powerQuotaMilliWatts>600</powerQuotaMilliWatts>\n"
+ + "</powerThrottlingPoint>\n"
+ + "<powerThrottlingPoint>\n"
+ + "<thermalStatus>severe</thermalStatus>\n"
+ + "<powerQuotaMilliWatts>400</powerQuotaMilliWatts>\n"
+ + "</powerThrottlingPoint>\n"
+ + "<powerThrottlingPoint>\n"
+ + "<thermalStatus>critical</thermalStatus>\n"
+ + "<powerQuotaMilliWatts>200</powerQuotaMilliWatts>\n"
+ + "</powerThrottlingPoint>\n"
+ + "<powerThrottlingPoint>\n"
+ + "<thermalStatus>emergency</thermalStatus>\n"
+ + "<powerQuotaMilliWatts>100</powerQuotaMilliWatts>\n"
+ + "</powerThrottlingPoint>\n"
+ + "<powerThrottlingPoint>\n"
+ + "<thermalStatus>shutdown</thermalStatus>\n"
+ + "<powerQuotaMilliWatts>50</powerQuotaMilliWatts>\n"
+ + "</powerThrottlingPoint>\n"
+ + "</powerThrottlingMap>\n"
+ + "</powerThrottlingConfig>\n";
+ }
private String getScreenBrightnessRampCapsIdle() {
return "<screenBrightnessRampIncreaseMaxIdleMillis>"
+ "4000"
@@ -915,6 +1056,7 @@
+ "</displayBrightnessPoint>\n"
+ "</displayBrightnessMapping>\n"
+ "</autoBrightness>\n"
+ + getPowerThrottlingConfig()
+ "<highBrightnessMode enabled=\"true\">\n"
+ "<transitionPoint>" + BRIGHTNESS[1] + "</transitionPoint>\n"
+ "<minimumLux>10000</minimumLux>\n"
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index 2396905..d021f1d 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -114,17 +114,18 @@
import com.android.server.display.DisplayManagerService.DeviceStateListener;
import com.android.server.display.DisplayManagerService.SyncRoot;
import com.android.server.display.feature.DisplayManagerFlags;
+import com.android.server.display.notifications.DisplayNotificationManager;
import com.android.server.input.InputManagerInternal;
import com.android.server.lights.LightsManager;
import com.android.server.pm.UserManagerInternal;
import com.android.server.sensors.SensorManagerInternal;
import com.android.server.wm.WindowManagerInternal;
+import com.google.common.truth.Expect;
+
import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
-import com.google.common.truth.Expect;
-
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -201,9 +202,12 @@
@Override
LocalDisplayAdapter getLocalDisplayAdapter(SyncRoot syncRoot, Context context,
Handler handler, DisplayAdapter.Listener displayAdapterListener,
- DisplayManagerFlags flags) {
+ DisplayManagerFlags flags,
+ DisplayNotificationManager displayNotificationManager) {
return new LocalDisplayAdapter(syncRoot, context, handler,
- displayAdapterListener, flags, new LocalDisplayAdapter.Injector() {
+ displayAdapterListener, flags,
+ mMockedDisplayNotificationManager,
+ new LocalDisplayAdapter.Injector() {
@Override
public LocalDisplayAdapter.SurfaceControlProxy getSurfaceControlProxy() {
return mSurfaceControlProxy;
@@ -248,13 +252,15 @@
@Override
LocalDisplayAdapter getLocalDisplayAdapter(SyncRoot syncRoot, Context context,
Handler handler, DisplayAdapter.Listener displayAdapterListener,
- DisplayManagerFlags flags) {
+ DisplayManagerFlags flags,
+ DisplayNotificationManager displayNotificationManager) {
return new LocalDisplayAdapter(
syncRoot,
context,
handler,
displayAdapterListener,
flags,
+ mMockedDisplayNotificationManager,
new LocalDisplayAdapter.Injector() {
@Override
public LocalDisplayAdapter.SurfaceControlProxy getSurfaceControlProxy() {
@@ -288,6 +294,7 @@
private final DisplayManagerService.Injector mBasicInjector = new BasicInjector();
+ @Mock DisplayNotificationManager mMockedDisplayNotificationManager;
@Mock IMediaProjectionManager mMockProjectionService;
@Mock IVirtualDeviceManager mIVirtualDeviceManager;
@Mock InputManagerInternal mMockInputManagerInternal;
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
index 147e8f2..9ac0062 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -60,6 +60,7 @@
import com.android.server.display.LocalDisplayAdapter.BacklightAdapter;
import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.mode.DisplayModeDirector;
+import com.android.server.display.notifications.DisplayNotificationManager;
import com.android.server.lights.LightsManager;
import com.android.server.lights.LogicalLight;
@@ -113,6 +114,8 @@
@Mock
private LogicalLight mMockedBacklight;
@Mock
+ private DisplayNotificationManager mMockedDisplayNotificationManager;
+ @Mock
private DisplayManagerFlags mFlags;
private Handler mHandler;
@@ -148,7 +151,7 @@
mInjector = new Injector();
when(mSurfaceControlProxy.getBootDisplayModeSupport()).thenReturn(true);
mAdapter = new LocalDisplayAdapter(mMockedSyncRoot, mMockedContext, mHandler,
- mListener, mFlags, mInjector);
+ mListener, mFlags, mMockedDisplayNotificationManager, mInjector);
spyOn(mAdapter);
doReturn(mMockedContext).when(mAdapter).getOverlayContext();
diff --git a/services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java b/services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java
new file mode 100644
index 0000000..5c50acb
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.display;
+
+import static com.android.internal.display.RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.when;
+
+import android.hardware.display.DisplayManager;
+import android.testing.TestableContext;
+import android.view.Display;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.display.RefreshRateSettingsUtils;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RefreshRateSettingsUtilsTest {
+
+ @Rule
+ public final TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getContext());
+
+ @Mock
+ private DisplayManager mDisplayManagerMock;
+ @Mock
+ private Display mDisplayMock;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext.addMockSystemService(DisplayManager.class, mDisplayManagerMock);
+
+ Display.Mode[] modes = new Display.Mode[]{
+ new Display.Mode(/* modeId= */ 0, /* width= */ 800, /* height= */ 600,
+ /* refreshRate= */ 60),
+ new Display.Mode(/* modeId= */ 0, /* width= */ 800, /* height= */ 600,
+ /* refreshRate= */ 120),
+ new Display.Mode(/* modeId= */ 0, /* width= */ 800, /* height= */ 600,
+ /* refreshRate= */ 90)
+ };
+
+ when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(mDisplayMock);
+ when(mDisplayMock.getSupportedModes()).thenReturn(modes);
+ }
+
+ @Test
+ public void testFindHighestRefreshRateForDefaultDisplay() {
+ when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(null);
+ assertEquals(DEFAULT_REFRESH_RATE,
+ RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext),
+ /* delta= */ 0);
+
+ when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(mDisplayMock);
+ assertEquals(120,
+ RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext),
+ /* delta= */ 0);
+ }
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
index c63fac9..ee187ba 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
@@ -22,6 +22,9 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+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;
@@ -97,6 +100,37 @@
}
@Test
+ public void testRegisterHdrListener() {
+ verify(mMockHdrInfoListener).register(mMockBinder);
+ }
+
+ @Test
+ public void testRegisterOtherHdrListenerWhenCalledWithOtherToken() {
+ IBinder otherBinder = mock(IBinder.class);
+ mHdrClamper.resetHdrConfig(TEST_HDR_DATA, WIDTH, HEIGHT, MIN_HDR_PERCENT, otherBinder);
+
+ verify(mMockHdrInfoListener).unregister(mMockBinder);
+ verify(mMockHdrInfoListener).register(otherBinder);
+ }
+
+ @Test
+ public void testRegisterHdrListenerOnceWhenCalledWithSameToken() {
+ mHdrClamper.resetHdrConfig(TEST_HDR_DATA, WIDTH, HEIGHT, MIN_HDR_PERCENT, mMockBinder);
+
+ verify(mMockHdrInfoListener, never()).unregister(mMockBinder);
+ verify(mMockHdrInfoListener, times(1)).register(mMockBinder);
+ }
+
+ @Test
+ public void testRegisterNotCalledIfHbmConfigIsMissing() {
+ IBinder otherBinder = mock(IBinder.class);
+ mHdrClamper.resetHdrConfig(TEST_HDR_DATA, WIDTH, HEIGHT, -1, otherBinder);
+
+ verify(mMockHdrInfoListener).unregister(mMockBinder);
+ verify(mMockHdrInfoListener, never()).register(otherBinder);
+ }
+
+ @Test
public void testClamper_AmbientLuxChangesAboveLimit() {
mHdrClamper.onAmbientLuxChange(500);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/color/ColorDisplayServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/color/ColorDisplayServiceTest.java
index c7c09b5..ec27f9d 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/color/ColorDisplayServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/color/ColorDisplayServiceTest.java
@@ -44,12 +44,12 @@
import android.test.mock.MockContentResolver;
import android.view.Display;
-import androidx.test.InstrumentationRegistry;
+import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.server.LocalServices;
+import com.android.internal.util.test.LocalServiceKeeperRule;
import com.android.server.SystemService;
import com.android.server.twilight.TwilightListener;
import com.android.server.twilight.TwilightManager;
@@ -57,6 +57,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
@@ -90,9 +91,13 @@
ColorDisplayManager.COLOR_MODE_BOOSTED,
};
+ @Rule
+ public LocalServiceKeeperRule mLocalServiceKeeperRule = new LocalServiceKeeperRule();
+
@Before
public void setUp() {
- mContext = Mockito.spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
+ mContext = Mockito.spy(new ContextWrapper(
+ InstrumentationRegistry.getInstrumentation().getTargetContext()));
doReturn(mContext).when(mContext).getApplicationContext();
final Resources res = Mockito.spy(mContext.getResources());
@@ -112,43 +117,36 @@
doReturn(am).when(mContext).getSystemService(Context.ALARM_SERVICE);
mTwilightManager = new MockTwilightManager();
- LocalServices.addService(TwilightManager.class, mTwilightManager);
+ mLocalServiceKeeperRule.overrideLocalService(TwilightManager.class, mTwilightManager);
mDisplayTransformManager = Mockito.mock(DisplayTransformManager.class);
doReturn(true).when(mDisplayTransformManager).needsLinearColorMatrix();
- LocalServices.addService(DisplayTransformManager.class, mDisplayTransformManager);
+ mLocalServiceKeeperRule.overrideLocalService(
+ DisplayTransformManager.class, mDisplayTransformManager);
mDisplayManagerInternal = Mockito.mock(DisplayManagerInternal.class);
- LocalServices.removeServiceForTest(DisplayManagerInternal.class);
- LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternal);
+ mLocalServiceKeeperRule.overrideLocalService(
+ DisplayManagerInternal.class, mDisplayManagerInternal);
mCds = new ColorDisplayService(mContext);
mBinderService = mCds.new BinderService();
- LocalServices.addService(ColorDisplayService.ColorDisplayServiceInternal.class,
+ mLocalServiceKeeperRule.overrideLocalService(
+ ColorDisplayService.ColorDisplayServiceInternal.class,
mCds.new ColorDisplayServiceInternal());
}
@After
public void tearDown() {
- /*
- * Wait for internal {@link Handler} to finish processing pending messages, so that test
- * code can safelyremove {@link DisplayTransformManager} mock from {@link LocalServices}.
- */
- mCds.mHandler.runWithScissors(() -> { /* nop */ }, /* timeout */ 1000);
+ // synchronously cancel all animations
+ mCds.mHandler.runWithScissors(() -> mCds.cancelAllAnimators(), /* timeout */ 1000);
mCds = null;
- LocalServices.removeServiceForTest(TwilightManager.class);
mTwilightManager = null;
- LocalServices.removeServiceForTest(DisplayTransformManager.class);
-
mUserId = UserHandle.USER_NULL;
mContext = null;
FakeSettingsProvider.clearSettingsProvider();
-
- LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class);
- LocalServices.removeServiceForTest(DisplayManagerInternal.class);
}
@Test
@@ -1249,10 +1247,10 @@
private void startService() {
Secure.putIntForUser(mContext.getContentResolver(), Secure.USER_SETUP_COMPLETE, 1, mUserId);
- InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
- mCds.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
- mCds.onUserChanged(mUserId);
- });
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(
+ () -> mCds.onBootPhase(SystemService.PHASE_BOOT_COMPLETED));
+ // onUserChanged cancels running animations, and should be called in handler thread
+ mCds.mHandler.runWithScissors(() -> mCds.onUserChanged(mUserId), 1000);
}
/**
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/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index b8c18e07..c4f72b3 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -27,6 +27,7 @@
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.server.display.mode.Vote.INVALID_SIZE;
import static com.google.common.truth.Truth.assertThat;
@@ -85,10 +86,12 @@
import com.android.internal.R;
import com.android.internal.display.BrightnessSynchronizer;
+import com.android.internal.display.RefreshRateSettingsUtils;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.Preconditions;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.util.test.FakeSettingsProviderRule;
+import com.android.modules.utils.testing.ExtendedMockitoRule;
import com.android.server.display.DisplayDeviceConfig;
import com.android.server.display.TestUtils;
import com.android.server.display.feature.DisplayManagerFlags;
@@ -106,7 +109,7 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
+import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;
import java.util.ArrayList;
@@ -252,9 +255,15 @@
@Mock
private DisplayManagerFlags mDisplayManagerFlags;
+ @Rule
+ public final ExtendedMockitoRule mExtendedMockitoRule =
+ new ExtendedMockitoRule.Builder(this)
+ .setStrictness(Strictness.LENIENT)
+ .spyStatic(RefreshRateSettingsUtils.class)
+ .build();
+
@Before
public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
final MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContext);
when(mContext.getContentResolver()).thenReturn(resolver);
@@ -1470,6 +1479,94 @@
}
@Test
+ public void testPeakRefreshRate_FlagEnabled() {
+ when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
+ .thenReturn(true);
+ float highestRefreshRate = 130;
+ doReturn(highestRefreshRate).when(() ->
+ RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext));
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
+ director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
+
+ Sensor lightSensor = createLightSensor();
+ SensorManager sensorManager = createMockSensorManager(lightSensor);
+ director.start(sensorManager);
+
+ setPeakRefreshRate(Float.POSITIVE_INFINITY);
+
+ Vote vote = director.getVote(Display.DEFAULT_DISPLAY,
+ Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
+ assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
+ highestRefreshRate);
+ }
+
+ @Test
+ public void testPeakRefreshRate_FlagDisabled() {
+ when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
+ .thenReturn(false);
+ float peakRefreshRate = 130;
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
+ director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
+
+ Sensor lightSensor = createLightSensor();
+ SensorManager sensorManager = createMockSensorManager(lightSensor);
+ director.start(sensorManager);
+
+ setPeakRefreshRate(peakRefreshRate);
+
+ Vote vote = director.getVote(Display.DEFAULT_DISPLAY,
+ Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
+ assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
+ peakRefreshRate);
+ }
+
+ @Test
+ public void testMinRefreshRate_FlagEnabled() {
+ when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
+ .thenReturn(true);
+ float highestRefreshRate = 130;
+ doReturn(highestRefreshRate).when(() ->
+ RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext));
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
+ director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
+
+ Sensor lightSensor = createLightSensor();
+ SensorManager sensorManager = createMockSensorManager(lightSensor);
+ director.start(sensorManager);
+
+ setMinRefreshRate(Float.POSITIVE_INFINITY);
+
+ Vote vote = director.getVote(Display.DEFAULT_DISPLAY,
+ Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
+ assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ highestRefreshRate,
+ /* frameRateHigh= */ Float.POSITIVE_INFINITY);
+ }
+
+ @Test
+ public void testMinRefreshRate_FlagDisabled() {
+ when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
+ .thenReturn(false);
+ float minRefreshRate = 130;
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
+ director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
+
+ Sensor lightSensor = createLightSensor();
+ SensorManager sensorManager = createMockSensorManager(lightSensor);
+ director.start(sensorManager);
+
+ setMinRefreshRate(minRefreshRate);
+
+ Vote vote = director.getVote(Display.DEFAULT_DISPLAY,
+ Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
+ assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ minRefreshRate,
+ /* frameRateHigh= */ Float.POSITIVE_INFINITY);
+ }
+
+ @Test
public void testSensorRegistration() {
// First, configure brightness zones or DMD won't register for sensor data.
final FakeDeviceConfig config = mInjector.getDeviceConfig();
@@ -3167,6 +3264,13 @@
waitForIdleSync();
}
+ private void setMinRefreshRate(float fps) {
+ Settings.System.putFloat(mContext.getContentResolver(), Settings.System.MIN_REFRESH_RATE,
+ fps);
+ mInjector.notifyMinRefreshRateChanged();
+ waitForIdleSync();
+ }
+
private static SensorManager createMockSensorManager(Sensor... sensors) {
SensorManager sensorManager = mock(SensorManager.class);
when(sensorManager.getSensorList(anyInt())).then((invocation) -> {
@@ -3216,6 +3320,7 @@
private final SensorManagerInternal mSensorManagerInternal;
private ContentObserver mPeakRefreshRateObserver;
+ private ContentObserver mMinRefreshRateObserver;
FakesInjector() {
this(null, null, null);
@@ -3247,6 +3352,12 @@
}
@Override
+ public void registerMinRefreshRateObserver(@NonNull ContentResolver cr,
+ @NonNull ContentObserver observer) {
+ mMinRefreshRateObserver = observer;
+ }
+
+ @Override
public void registerDisplayListener(DisplayListener listener, Handler handler) {}
@Override
@@ -3318,5 +3429,12 @@
PEAK_REFRESH_RATE_URI);
}
}
+
+ void notifyMinRefreshRateChanged() {
+ if (mMinRefreshRateObserver != null) {
+ mMinRefreshRateObserver.dispatchChange(false /*selfChange*/,
+ MIN_REFRESH_RATE_URI);
+ }
+ }
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/notifications/DisplayNotificationManagerTest.java b/services/tests/displayservicetests/src/com/android/server/display/notifications/DisplayNotificationManagerTest.java
new file mode 100644
index 0000000..d7c35ed
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/notifications/DisplayNotificationManagerTest.java
@@ -0,0 +1,125 @@
+/*
+ * 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.display.notifications;
+
+import static android.app.Notification.FLAG_ONGOING_EVENT;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assume.assumeFalse;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.display.feature.DisplayManagerFlags;
+import com.android.server.display.notifications.DisplayNotificationManager.Injector;
+
+import com.google.testing.junit.testparameterinjector.TestParameter;
+import com.google.testing.junit.testparameterinjector.TestParameterInjector;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link DisplayNotificationManager}
+ * Run: atest DisplayNotificationManagerTest
+ */
+@SmallTest
+@RunWith(TestParameterInjector.class)
+public class DisplayNotificationManagerTest {
+ @Mock
+ private Injector mMockedInjector;
+ @Mock
+ private NotificationManager mMockedNotificationManager;
+ @Mock
+ private DisplayManagerFlags mMockedFlags;
+ @Captor
+ private ArgumentCaptor<String> mNotifyTagCaptor;
+ @Captor
+ private ArgumentCaptor<Integer> mNotifyNoteIdCaptor;
+ @Captor
+ private ArgumentCaptor<Notification> mNotifyAsUserNotificationCaptor;
+
+ /** Setup tests. */
+ @Before
+ public void setup() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testNotificationOnHotplugConnectionError() {
+ var dnm = createDisplayNotificationManager(/*isNotificationManagerAvailable=*/ true,
+ /*isErrorHandlingEnabled=*/ true);
+ dnm.onHotplugConnectionError();
+ assertExpectedNotification();
+ }
+
+ @Test
+ public void testNoErrorNotification(
+ @TestParameter final boolean isNotificationManagerAvailable,
+ @TestParameter final boolean isErrorHandlingEnabled) {
+ /* This case is tested by #testNotificationOnHotplugConnectionError */
+ assumeFalse(isNotificationManagerAvailable && isErrorHandlingEnabled);
+ var dnm = createDisplayNotificationManager(isNotificationManagerAvailable,
+ isErrorHandlingEnabled);
+ dnm.onHotplugConnectionError();
+ verify(mMockedNotificationManager, never()).notify(anyString(), anyInt(), any());
+ }
+
+ private DisplayNotificationManager createDisplayNotificationManager(
+ final boolean isNotificationManagerAvailable,
+ final boolean isErrorHandlingEnabled) {
+ when(mMockedFlags.isConnectedDisplayErrorHandlingEnabled()).thenReturn(
+ isErrorHandlingEnabled);
+ when(mMockedInjector.getNotificationManager()).thenReturn(
+ (isNotificationManagerAvailable) ? mMockedNotificationManager : null);
+ final var displayNotificationManager = new DisplayNotificationManager(mMockedFlags,
+ ApplicationProvider.getApplicationContext(), mMockedInjector);
+ displayNotificationManager.onBootCompleted();
+ return displayNotificationManager;
+ }
+
+ private void assertExpectedNotification() {
+ verify(mMockedNotificationManager).notify(
+ mNotifyTagCaptor.capture(),
+ mNotifyNoteIdCaptor.capture(),
+ mNotifyAsUserNotificationCaptor.capture());
+ assertThat(mNotifyTagCaptor.getValue()).isEqualTo("DisplayNotificationManager");
+ assertThat((int) mNotifyNoteIdCaptor.getValue()).isEqualTo(1);
+ final var notification = mNotifyAsUserNotificationCaptor.getValue();
+ assertThat(notification.getChannelId()).isEqualTo("ALERTS");
+ assertThat(notification.category).isEqualTo(Notification.CATEGORY_ERROR);
+ assertThat(notification.visibility).isEqualTo(Notification.VISIBILITY_PUBLIC);
+ assertThat(notification.flags & FLAG_ONGOING_EVENT).isEqualTo(0);
+ assertThat(notification.when).isEqualTo(0);
+ assertThat(notification.getTimeoutAfter()).isEqualTo(30000L);
+ }
+}
diff --git a/services/tests/inprocesstests/Android.bp b/services/tests/inprocesstests/Android.bp
index 7c237ac..086e84b 100644
--- a/services/tests/inprocesstests/Android.bp
+++ b/services/tests/inprocesstests/Android.bp
@@ -14,7 +14,7 @@
"androidx.test.core",
"androidx.test.rules",
"services.core",
- "truth-prebuilt",
+ "truth",
"platform-test-annotations",
],
test_suites: ["general-tests"],
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index 101498a..063af57 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -46,6 +46,7 @@
"androidx.test.espresso.core",
"androidx.test.espresso.contrib",
"androidx.test.ext.truth",
+ "flag-junit",
"frameworks-base-testutils",
"hamcrest-library",
"kotlin-test",
@@ -67,7 +68,7 @@
"servicestests-core-utils",
"servicestests-utils-mockito-extended",
"testables",
- "truth-prebuilt",
+ "truth",
// TODO: remove once Android migrates to JUnit 4.12, which provides assertThrows
"testng",
"compatibility-device-util-axt",
diff --git a/services/tests/mockingservicestests/jni/Android.bp b/services/tests/mockingservicestests/jni/Android.bp
index f1dc1fa..1eb9888 100644
--- a/services/tests/mockingservicestests/jni/Android.bp
+++ b/services/tests/mockingservicestests/jni/Android.bp
@@ -22,6 +22,7 @@
srcs: [
":lib_cachedAppOptimizer_native",
":lib_gameManagerService_native",
+ ":lib_oomConnection_native",
"onload.cpp",
],
@@ -42,6 +43,7 @@
"libgui",
"libhidlbase",
"liblog",
+ "libmemevents",
"libmeminfo",
"libnativehelper",
"libprocessgroup",
diff --git a/services/tests/mockingservicestests/jni/onload.cpp b/services/tests/mockingservicestests/jni/onload.cpp
index 23ccb22..fb91051 100644
--- a/services/tests/mockingservicestests/jni/onload.cpp
+++ b/services/tests/mockingservicestests/jni/onload.cpp
@@ -26,6 +26,7 @@
namespace android {
int register_android_server_am_CachedAppOptimizer(JNIEnv* env);
int register_android_server_app_GameManagerService(JNIEnv* env);
+int register_android_server_am_OomConnection(JNIEnv* env);
};
using namespace android;
@@ -42,6 +43,7 @@
ALOG_ASSERT(env, "Could not retrieve the env!");
register_android_server_am_CachedAppOptimizer(env);
register_android_server_app_GameManagerService(env);
+ register_android_server_am_OomConnection(env);
return JNI_VERSION_1_4;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index 5b1508b..be29163 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -1290,7 +1290,8 @@
}
@Test
- public void testLightStepIdleStateIdlingTimeIncreases() {
+ public void testLightStepIdleStateIdlingTimeIncreasesExponentially() {
+ mConstants.LIGHT_IDLE_INCREASE_LINEARLY = false;
final long maintenanceTimeMs = 60_000L;
mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = maintenanceTimeMs;
mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = maintenanceTimeMs;
@@ -1335,13 +1336,88 @@
eq(mInjector.nowElapsed + idlingTimeMs),
anyLong(), anyString(), any(), any(Handler.class));
- for (int i = 0; i < 2; ++i) {
+ for (int i = 0; i < 10; ++i) {
// IDLE->MAINTENANCE alarm
mInjector.nowElapsed = mDeviceIdleController.getNextLightAlarmTimeForTesting();
alarmListener.onAlarm();
verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
long maintenanceExpiryTime = mInjector.nowElapsed + maintenanceTimeMs;
idlingTimeMs *= mConstants.LIGHT_IDLE_FACTOR;
+ idlingTimeMs = Math.min(idlingTimeMs, mConstants.LIGHT_MAX_IDLE_TIMEOUT);
+ // Set MAINTENANCE->IDLE
+ alarmManagerInOrder.verify(mAlarmManager).setWindow(
+ eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
+ eq(maintenanceExpiryTime),
+ anyLong(), anyString(), any(), any(Handler.class));
+
+ // MAINTENANCE->IDLE alarm
+ mInjector.nowElapsed = mDeviceIdleController.getNextLightAlarmTimeForTesting();
+ alarmListener.onAlarm();
+ verifyLightStateConditions(LIGHT_STATE_IDLE);
+ // Set IDLE->MAINTENANCE again
+ alarmManagerInOrder.verify(mAlarmManager).setWindow(
+ eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
+ eq(mInjector.nowElapsed + idlingTimeMs),
+ anyLong(), anyString(), any(), any(Handler.class));
+ }
+ }
+
+ @Test
+ public void testLightStepIdleStateIdlingTimeIncreasesLinearly() {
+ mConstants.LIGHT_IDLE_INCREASE_LINEARLY = true;
+ final long maintenanceTimeMs = 60_000L;
+ mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = maintenanceTimeMs;
+ mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = maintenanceTimeMs;
+ mConstants.LIGHT_IDLE_TIMEOUT = 5 * 60_000L;
+ mConstants.LIGHT_MAX_IDLE_TIMEOUT = 30 * 60_000L;
+ mConstants.LIGHT_IDLE_FACTOR = 2f;
+ mConstants.LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS = 2 * 60_000L;
+
+ setNetworkConnected(true);
+ mDeviceIdleController.setJobsActive(false);
+ mDeviceIdleController.setAlarmsActive(false);
+ mDeviceIdleController.setActiveIdleOpsForTest(0);
+
+ InOrder alarmManagerInOrder = inOrder(mAlarmManager);
+
+ final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListenerCaptor = ArgumentCaptor
+ .forClass(AlarmManager.OnAlarmListener.class);
+ doNothing().when(mAlarmManager).setWindow(anyInt(), anyLong(), anyLong(),
+ eq("DeviceIdleController.light"), alarmListenerCaptor.capture(), any());
+
+ // Set state to INACTIVE.
+ mDeviceIdleController.becomeActiveLocked("testing", 0);
+ setChargingOn(false);
+ setScreenOn(false);
+ verifyLightStateConditions(LIGHT_STATE_INACTIVE);
+ long idlingTimeMs = mConstants.LIGHT_IDLE_TIMEOUT;
+ final long idleAfterInactiveExpiryTime =
+ mInjector.nowElapsed + mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT;
+ alarmManagerInOrder.verify(mAlarmManager).setWindow(
+ eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
+ eq(idleAfterInactiveExpiryTime),
+ anyLong(), anyString(), any(), any(Handler.class));
+
+ final AlarmManager.OnAlarmListener alarmListener =
+ alarmListenerCaptor.getAllValues().get(0);
+
+ // INACTIVE -> IDLE alarm
+ mInjector.nowElapsed = mDeviceIdleController.getNextLightAlarmTimeForTesting();
+ alarmListener.onAlarm();
+ verifyLightStateConditions(LIGHT_STATE_IDLE);
+ alarmManagerInOrder.verify(mAlarmManager).setWindow(
+ eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
+ eq(mInjector.nowElapsed + idlingTimeMs),
+ anyLong(), anyString(), any(), any(Handler.class));
+
+ for (int i = 0; i < 10; ++i) {
+ // IDLE->MAINTENANCE alarm
+ mInjector.nowElapsed = mDeviceIdleController.getNextLightAlarmTimeForTesting();
+ alarmListener.onAlarm();
+ verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
+ long maintenanceExpiryTime = mInjector.nowElapsed + maintenanceTimeMs;
+ idlingTimeMs += mConstants.LIGHT_IDLE_LINEAR_INCREASE_FACTOR_MS;
+ idlingTimeMs = Math.min(idlingTimeMs, mConstants.LIGHT_MAX_IDLE_TIMEOUT);
// Set MAINTENANCE->IDLE
alarmManagerInOrder.verify(mAlarmManager).setWindow(
eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 5b51963..21e3b34 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -62,8 +62,6 @@
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.APP_STANDBY_BUCKET_CHANGED;
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.CHARGING_STATUS_CHANGED;
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.CHECK_EXACT_ALARM_PERMISSION_ON_UPDATE;
-import static com.android.server.alarm.AlarmManagerService.AlarmHandler.EXACT_ALARM_DENY_LIST_PACKAGES_ADDED;
-import static com.android.server.alarm.AlarmManagerService.AlarmHandler.EXACT_ALARM_DENY_LIST_PACKAGES_REMOVED;
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REFRESH_EXACT_ALARM_CANDIDATES;
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REMOVE_EXACT_ALARMS;
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REMOVE_EXACT_LISTENER_ALARMS_ON_CACHED;
@@ -75,7 +73,6 @@
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_QUOTA;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_WINDOW;
-import static com.android.server.alarm.AlarmManagerService.Constants.KEY_EXACT_ALARM_DENY_LIST;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_LISTENER_TIMEOUT;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MAX_DEVICE_IDLE_FUZZ;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MAX_INTERVAL;
@@ -85,7 +82,6 @@
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_WINDOW;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_PRIORITY_ALARM_DELAY;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_TEMPORARY_QUOTA_BUMP;
-import static com.android.server.alarm.AlarmManagerService.Constants.MAX_EXACT_ALARM_DENY_LIST_SIZE;
import static com.android.server.alarm.AlarmManagerService.FREQUENT_INDEX;
import static com.android.server.alarm.AlarmManagerService.INDEFINITE_DELAY;
import static com.android.server.alarm.AlarmManagerService.IS_WAKEUP_MASK;
@@ -799,47 +795,6 @@
}
@Test
- public void updatingExactAlarmDenyList() {
- ArraySet<String> denyListed = new ArraySet<>(new String[]{
- "com.example.package1",
- "com.example.package2",
- "com.example.package3",
- });
- setDeviceConfigString(KEY_EXACT_ALARM_DENY_LIST,
- "com.example.package1,com.example.package2,com.example.package3");
- assertEquals(denyListed, mService.mConstants.EXACT_ALARM_DENY_LIST);
-
-
- denyListed = new ArraySet<>(new String[]{
- "com.example.package1",
- "com.example.package4",
- });
- setDeviceConfigString(KEY_EXACT_ALARM_DENY_LIST,
- "com.example.package1,com.example.package4");
- assertEquals(denyListed, mService.mConstants.EXACT_ALARM_DENY_LIST);
-
- setDeviceConfigString(KEY_EXACT_ALARM_DENY_LIST, "");
- assertEquals(0, mService.mConstants.EXACT_ALARM_DENY_LIST.size());
- }
-
- @Test
- public void exactAlarmDenyListMaxSize() {
- final ArraySet<String> expectedSet = new ArraySet<>();
- final StringBuilder sb = new StringBuilder("package1");
- expectedSet.add("package1");
- for (int i = 2; i <= 2 * MAX_EXACT_ALARM_DENY_LIST_SIZE; i++) {
- sb.append(",package");
- sb.append(i);
- if (i <= MAX_EXACT_ALARM_DENY_LIST_SIZE) {
- expectedSet.add("package" + i);
- }
- }
- assertEquals(MAX_EXACT_ALARM_DENY_LIST_SIZE, expectedSet.size());
- setDeviceConfigString(KEY_EXACT_ALARM_DENY_LIST, sb.toString());
- assertEquals(expectedSet, mService.mConstants.EXACT_ALARM_DENY_LIST);
- }
-
- @Test
public void positiveWhileIdleQuotas() {
setDeviceConfigInt(KEY_ALLOW_WHILE_IDLE_QUOTA, -3);
assertEquals(1, mService.mConstants.ALLOW_WHILE_IDLE_QUOTA);
@@ -2212,50 +2167,30 @@
}
@Test
- public void hasScheduleExactAlarmBinderCallNotDenyListedPreT() throws RemoteException {
+ public void hasScheduleExactAlarmBinderCallPreT() throws RemoteException {
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
- mockScheduleExactAlarmStatePreT(true, false, MODE_DEFAULT);
+ mockScheduleExactAlarmStatePreT(true, MODE_DEFAULT);
assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
- mockScheduleExactAlarmStatePreT(true, false, MODE_ALLOWED);
+ mockScheduleExactAlarmStatePreT(true, MODE_ALLOWED);
assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
- mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED);
+ mockScheduleExactAlarmStatePreT(true, MODE_ERRORED);
assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
- mockScheduleExactAlarmStatePreT(true, false, MODE_IGNORED);
+ mockScheduleExactAlarmStatePreT(true, MODE_IGNORED);
assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
}
@Test
- public void hasScheduleExactAlarmBinderCallDenyListedPreT() throws RemoteException {
- mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
-
- mockScheduleExactAlarmStatePreT(true, true, MODE_ERRORED);
- assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
-
- mockScheduleExactAlarmStatePreT(true, true, MODE_DEFAULT);
- assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
-
- mockScheduleExactAlarmStatePreT(true, true, MODE_IGNORED);
- assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
-
- mockScheduleExactAlarmStatePreT(true, true, MODE_ALLOWED);
- assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
- }
-
- @Test
public void hasScheduleExactAlarmBinderCallNotDeclaredPreT() throws RemoteException {
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
- mockScheduleExactAlarmStatePreT(false, false, MODE_DEFAULT);
+ mockScheduleExactAlarmStatePreT(false, MODE_DEFAULT);
assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
- mockScheduleExactAlarmStatePreT(false, false, MODE_ALLOWED);
- assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
-
- mockScheduleExactAlarmStatePreT(false, true, MODE_ALLOWED);
+ mockScheduleExactAlarmStatePreT(false, MODE_ALLOWED);
assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
}
@@ -2281,41 +2216,33 @@
mockChangeEnabled(AlarmManager.ENABLE_USE_EXACT_ALARM, true);
// No permission, no exemption.
- mockScheduleExactAlarmStatePreT(true, true, MODE_DEFAULT);
- assertFalse(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE));
-
- // No permission, no exemption.
- mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED);
+ mockScheduleExactAlarmStatePreT(true, MODE_ERRORED);
assertFalse(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE));
// Policy permission only, no exemption.
- mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED);
+ mockScheduleExactAlarmStatePreT(true, MODE_ERRORED);
mockUseExactAlarmState(true);
assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE));
mockUseExactAlarmState(false);
// User permission only, no exemption.
- mockScheduleExactAlarmStatePreT(true, false, MODE_DEFAULT);
- assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE));
-
- // User permission only, no exemption.
- mockScheduleExactAlarmStatePreT(true, true, MODE_ALLOWED);
+ mockScheduleExactAlarmStatePreT(true, MODE_DEFAULT);
assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE));
// No permission, exemption.
- mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED);
+ mockScheduleExactAlarmStatePreT(true, MODE_ERRORED);
when(mDeviceIdleInternal.isAppOnWhitelist(TEST_CALLING_UID)).thenReturn(true);
assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE));
// No permission, exemption.
- mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED);
+ mockScheduleExactAlarmStatePreT(true, MODE_ERRORED);
when(mDeviceIdleInternal.isAppOnWhitelist(TEST_CALLING_UID)).thenReturn(false);
doReturn(true).when(() -> UserHandle.isCore(TEST_CALLING_UID));
assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE));
// Both permissions and exemption.
- mockScheduleExactAlarmStatePreT(true, false, MODE_ALLOWED);
+ mockScheduleExactAlarmStatePreT(true, MODE_ALLOWED);
mockUseExactAlarmState(true);
assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE));
}
@@ -2514,17 +2441,12 @@
assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
}
- private void mockScheduleExactAlarmStatePreT(boolean declared, boolean denyList, int mode) {
+ private void mockScheduleExactAlarmStatePreT(boolean declared, int mode) {
String[] requesters = declared ? new String[]{TEST_CALLING_PACKAGE} : EmptyArray.STRING;
when(mPermissionManagerInternal.getAppOpPermissionPackages(SCHEDULE_EXACT_ALARM))
.thenReturn(requesters);
mService.refreshExactAlarmCandidates();
- if (denyList) {
- setDeviceConfigString(KEY_EXACT_ALARM_DENY_LIST, TEST_CALLING_PACKAGE);
- } else {
- setDeviceConfigString(KEY_EXACT_ALARM_DENY_LIST, "");
- }
when(mAppOpsManager.checkOpNoThrow(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID,
TEST_CALLING_PACKAGE)).thenReturn(mode);
}
@@ -2556,7 +2478,7 @@
public void alarmClockBinderCallWithoutPermission() throws RemoteException {
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
- mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED);
+ mockScheduleExactAlarmStatePreT(true, MODE_ERRORED);
when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true);
final PendingIntent alarmPi = getNewMockPendingIntent();
@@ -2630,7 +2552,7 @@
public void exactBinderCallWithAllowlist() throws RemoteException {
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
// If permission is denied, only then allowlist will be checked.
- mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED);
+ mockScheduleExactAlarmStatePreT(true, MODE_ERRORED);
when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true);
final PendingIntent alarmPi = getNewMockPendingIntent();
@@ -2650,7 +2572,7 @@
public void exactAllowWhileIdleBinderCallWithSEAPermission() throws RemoteException {
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
- mockScheduleExactAlarmStatePreT(true, false, MODE_ALLOWED);
+ mockScheduleExactAlarmStatePreT(true, MODE_ALLOWED);
final PendingIntent alarmPi = getNewMockPendingIntent();
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null);
@@ -2676,7 +2598,7 @@
mockChangeEnabled(AlarmManager.ENABLE_USE_EXACT_ALARM, true);
mockUseExactAlarmState(true);
- mockScheduleExactAlarmStatePreT(false, false, MODE_ERRORED);
+ mockScheduleExactAlarmStatePreT(false, MODE_ERRORED);
final PendingIntent alarmPi = getNewMockPendingIntent();
mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0,
FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null);
@@ -2700,7 +2622,7 @@
public void exactAllowWhileIdleBinderCallWithAllowlist() throws RemoteException {
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
// If permission is denied, only then allowlist will be checked.
- mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED);
+ mockScheduleExactAlarmStatePreT(true, MODE_ERRORED);
when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true);
final PendingIntent alarmPi = getNewMockPendingIntent();
@@ -2726,7 +2648,7 @@
public void exactBinderCallsWithoutPermissionWithoutAllowlist() throws RemoteException {
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
- mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED);
+ mockScheduleExactAlarmStatePreT(true, MODE_ERRORED);
when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(false);
final PendingIntent alarmPi = getNewMockPendingIntent();
@@ -2836,7 +2758,7 @@
public void binderCallWithUserAllowlist() throws RemoteException {
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
- mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED);
+ mockScheduleExactAlarmStatePreT(true, MODE_ERRORED);
when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true);
when(mAppStateTracker.isUidPowerSaveUserExempt(TEST_CALLING_UID)).thenReturn(true);
@@ -3052,135 +2974,11 @@
}
@Test
- public void denyListChanged() {
- mService.mConstants.EXACT_ALARM_DENY_LIST = new ArraySet<>(new String[]{"p1", "p2", "p3"});
- when(mActivityManagerInternal.getStartedUserIds()).thenReturn(EmptyArray.INT);
-
- setDeviceConfigString(KEY_EXACT_ALARM_DENY_LIST, "p2,p4,p5");
-
- final ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
- verify(mService.mHandler, times(2)).sendMessageAtTime(messageCaptor.capture(),
- anyLong());
-
- final List<Message> messages = messageCaptor.getAllValues();
- for (final Message msg : messages) {
- assertTrue("Unwanted message sent to handler: " + msg.what,
- msg.what == EXACT_ALARM_DENY_LIST_PACKAGES_ADDED
- || msg.what == EXACT_ALARM_DENY_LIST_PACKAGES_REMOVED);
- mService.mHandler.handleMessage(msg);
- }
-
- ArraySet<String> added = new ArraySet<>(new String[]{"p4", "p5"});
- verify(mService).handleChangesToExactAlarmDenyList(eq(added), eq(true));
-
- ArraySet<String> removed = new ArraySet<>(new String[]{"p1", "p3"});
- verify(mService).handleChangesToExactAlarmDenyList(eq(removed), eq(false));
- }
-
- @Test
- public void permissionGrantedDueToDenyList() {
- mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
-
- final String[] packages = {"example.package.1", "example.package.2"};
-
- final int appId1 = 232;
- final int appId2 = 431;
-
- final int userId1 = 42;
- final int userId2 = 53;
-
- registerAppIds(packages, new Integer[]{appId1, appId2});
-
- when(mActivityManagerInternal.getStartedUserIds()).thenReturn(new int[]{userId1, userId2});
-
- when(mPermissionManagerInternal.getAppOpPermissionPackages(
- SCHEDULE_EXACT_ALARM)).thenReturn(packages);
- mService.refreshExactAlarmCandidates();
-
- final long allowListDuration = 53442;
- when(mActivityManagerInternal.getBootTimeTempAllowListDuration()).thenReturn(
- allowListDuration);
-
- mService.mLastOpScheduleExactAlarm.put(UserHandle.getUid(userId1, appId1), MODE_ALLOWED);
- mService.mLastOpScheduleExactAlarm.put(UserHandle.getUid(userId2, appId1), MODE_DEFAULT);
- mService.mLastOpScheduleExactAlarm.put(UserHandle.getUid(userId1, appId2), MODE_IGNORED);
- mService.mLastOpScheduleExactAlarm.put(UserHandle.getUid(userId2, appId2), MODE_ERRORED);
-
- mService.handleChangesToExactAlarmDenyList(new ArraySet<>(packages), false);
-
- // No permission revoked.
- verify(mService, never()).removeExactAlarmsOnPermissionRevoked(anyInt(), anyString(),
- anyBoolean());
-
- // Permission got granted only for (appId1, userId2).
- final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
- final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
- final ArgumentCaptor<UserHandle> userCaptor = ArgumentCaptor.forClass(UserHandle.class);
-
- verify(mMockContext).sendBroadcastAsUser(intentCaptor.capture(), userCaptor.capture(),
- isNull(), bundleCaptor.capture());
-
- assertEquals(userId2, userCaptor.getValue().getIdentifier());
-
- // Validate the intent.
- assertEquals(AlarmManager.ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED,
- intentCaptor.getValue().getAction());
- assertEquals(packages[0], intentCaptor.getValue().getPackage());
-
- // Validate the options.
- final BroadcastOptions bOptions = new BroadcastOptions(bundleCaptor.getValue());
- assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
- bOptions.getTemporaryAppAllowlistType());
- assertEquals(allowListDuration, bOptions.getTemporaryAppAllowlistDuration());
- assertEquals(PowerExemptionManager.REASON_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED,
- bOptions.getTemporaryAppAllowlistReasonCode());
- }
-
- @Test
- public void permissionRevokedDueToDenyList() {
- mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
-
- final String[] packages = {"example.package.1", "example.package.2"};
-
- final int appId1 = 232;
- final int appId2 = 431;
-
- final int userId1 = 42;
- final int userId2 = 53;
-
- registerAppIds(packages, new Integer[]{appId1, appId2});
-
- when(mActivityManagerInternal.getStartedUserIds()).thenReturn(new int[]{userId1, userId2});
-
- when(mPermissionManagerInternal.getAppOpPermissionPackages(
- SCHEDULE_EXACT_ALARM)).thenReturn(packages);
- mService.refreshExactAlarmCandidates();
-
- mService.mLastOpScheduleExactAlarm.put(UserHandle.getUid(userId1, appId1), MODE_ALLOWED);
- mService.mLastOpScheduleExactAlarm.put(UserHandle.getUid(userId2, appId1), MODE_DEFAULT);
- mService.mLastOpScheduleExactAlarm.put(UserHandle.getUid(userId1, appId2), MODE_IGNORED);
- mService.mLastOpScheduleExactAlarm.put(UserHandle.getUid(userId2, appId2), MODE_ERRORED);
-
- mService.handleChangesToExactAlarmDenyList(new ArraySet<>(packages), true);
-
- // Permission got revoked only for (appId1, userId2)
- verify(mService, never()).removeExactAlarmsOnPermissionRevoked(
- eq(UserHandle.getUid(userId1, appId1)), eq(packages[0]), eq(true));
- verify(mService, never()).removeExactAlarmsOnPermissionRevoked(
- eq(UserHandle.getUid(userId1, appId2)), eq(packages[1]), eq(true));
- verify(mService, never()).removeExactAlarmsOnPermissionRevoked(
- eq(UserHandle.getUid(userId2, appId2)), eq(packages[1]), eq(true));
-
- verify(mService).removeExactAlarmsOnPermissionRevoked(
- eq(UserHandle.getUid(userId2, appId1)), eq(packages[0]), eq(true));
- }
-
- @Test
public void opChangedPermissionRevoked() throws Exception {
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
mService.mLastOpScheduleExactAlarm.put(TEST_CALLING_UID, MODE_ALLOWED);
- mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED);
+ mockScheduleExactAlarmStatePreT(true, MODE_ERRORED);
mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE);
assertAndHandleMessageSync(REMOVE_EXACT_ALARMS);
@@ -3193,20 +2991,7 @@
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false);
mService.mLastOpScheduleExactAlarm.put(TEST_CALLING_UID, MODE_ALLOWED);
- mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED);
-
- mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE);
-
- verify(mService.mHandler, never()).sendMessageAtTime(
- argThat(m -> m.what == REMOVE_EXACT_ALARMS), anyLong());
- }
-
- @Test
- public void opChangedNoPermissionChangeDueToDenyList() throws Exception {
- mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false);
-
- mService.mLastOpScheduleExactAlarm.put(TEST_CALLING_UID, MODE_ERRORED);
- mockScheduleExactAlarmStatePreT(true, true, MODE_DEFAULT);
+ mockScheduleExactAlarmStatePreT(true, MODE_ERRORED);
mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE);
@@ -3222,7 +3007,7 @@
when(mActivityManagerInternal.getBootTimeTempAllowListDuration()).thenReturn(durationMs);
mService.mLastOpScheduleExactAlarm.put(TEST_CALLING_UID, MODE_ERRORED);
- mockScheduleExactAlarmStatePreT(true, false, MODE_ALLOWED);
+ mockScheduleExactAlarmStatePreT(true, MODE_ALLOWED);
mIAppOpsCallback.opChanged(OP_SCHEDULE_EXACT_ALARM, TEST_CALLING_UID, TEST_CALLING_PACKAGE);
final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
@@ -3482,7 +3267,7 @@
.putExtra(Intent.EXTRA_REPLACING, true);
mockUseExactAlarmState(false);
- mockScheduleExactAlarmStatePreT(true, false, MODE_ALLOWED);
+ mockScheduleExactAlarmStatePreT(true, MODE_ALLOWED);
mPackageChangesReceiver.onReceive(mMockContext, packageReplacedIntent);
assertAndHandleMessageSync(CHECK_EXACT_ALARM_PERMISSION_ON_UPDATE);
@@ -3490,7 +3275,7 @@
assertEquals(5, mService.mAlarmStore.size());
mockUseExactAlarmState(true);
- mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED);
+ mockScheduleExactAlarmStatePreT(true, MODE_ERRORED);
mPackageChangesReceiver.onReceive(mMockContext, packageReplacedIntent);
assertAndHandleMessageSync(CHECK_EXACT_ALARM_PERMISSION_ON_UPDATE);
@@ -3498,7 +3283,7 @@
assertEquals(5, mService.mAlarmStore.size());
mockUseExactAlarmState(false);
- mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED);
+ mockScheduleExactAlarmStatePreT(true, MODE_ERRORED);
mPackageChangesReceiver.onReceive(mMockContext, packageReplacedIntent);
assertAndHandleMessageSync(CHECK_EXACT_ALARM_PERMISSION_ON_UPDATE);
@@ -3653,16 +3438,16 @@
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true);
mockChangeEnabled(AlarmManager.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, false);
- mockScheduleExactAlarmStatePreT(true, true, MODE_DEFAULT);
+ mockScheduleExactAlarmStatePreT(true, MODE_DEFAULT);
+ assertTrue(mService.hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID));
+
+ mockScheduleExactAlarmStatePreT(false, MODE_ALLOWED);
assertFalse(mService.hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID));
- mockScheduleExactAlarmStatePreT(false, false, MODE_ALLOWED);
+ mockScheduleExactAlarmStatePreT(true, MODE_ERRORED);
assertFalse(mService.hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID));
- mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED);
- assertFalse(mService.hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID));
-
- mockScheduleExactAlarmStatePreT(true, false, MODE_ALLOWED);
+ mockScheduleExactAlarmStatePreT(true, MODE_ALLOWED);
assertTrue(mService.hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID));
}
@@ -3671,11 +3456,11 @@
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false);
mockScheduleExactAlarmState(true);
- mockScheduleExactAlarmStatePreT(true, false, MODE_ALLOWED);
+ mockScheduleExactAlarmStatePreT(true, MODE_ALLOWED);
assertFalse(mService.hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID));
mockScheduleExactAlarmState(false);
- mockScheduleExactAlarmStatePreT(true, false, MODE_ERRORED);
+ mockScheduleExactAlarmStatePreT(true, MODE_ERRORED);
assertFalse(mService.hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID));
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerInternalTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerInternalTest.java
index 64cc397..9ba4f5b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerInternalTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerInternalTest.java
@@ -218,4 +218,9 @@
assertEquals(errMsg, Thread.State.TERMINATED, getState());
}
}
+
+ // TODO: [b/302724778] Remove manual JNI load
+ static {
+ System.loadLibrary("mockingservicestestjni");
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 2bc66ac..40b5458 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -1210,4 +1210,9 @@
return returnValueForstartUserOnSecondaryDisplay;
}
}
+
+ // TODO: [b/302724778] Remove manual JNI load
+ static {
+ System.loadLibrary("mockingservicestestjni");
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java b/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java
index 1c0989c..9391d5b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java
@@ -338,4 +338,8 @@
}
}
+ // TODO: [b/302724778] Remove manual JNI load
+ static {
+ System.loadLibrary("mockingservicestestjni");
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
index d56229c..e15942b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
@@ -1126,4 +1126,9 @@
};
}
}
+
+ // TODO: [b/302724778] Remove manual JNI load
+ static {
+ System.loadLibrary("mockingservicestestjni");
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java b/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java
index 0abf46b..596a3f3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java
@@ -284,4 +284,9 @@
return app;
}
+
+ // TODO: [b/302724778] Remove manual JNI load
+ static {
+ System.loadLibrary("mockingservicestestjni");
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
index 434d200..dfb8fda 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
@@ -803,4 +803,9 @@
return mHandler;
}
}
+
+ // TODO: [b/302724778] Remove manual JNI load
+ static {
+ System.loadLibrary("mockingservicestestjni");
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ServiceTimeoutTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ServiceTimeoutTest.java
index fd1b068..7ec27be 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ServiceTimeoutTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ServiceTimeoutTest.java
@@ -201,4 +201,9 @@
return mActiveServices;
}
}
+
+ // TODO: [b/302724778] Remove manual JNI load
+ static {
+ System.loadLibrary("mockingservicestestjni");
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
index 2b56ea8..bded9b4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
@@ -34,6 +34,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.job.Flags.FLAG_RELAX_PREFETCH_CONNECTIVITY_CONSTRAINT_ONLY_ON_CHARGER;
import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX;
import static com.android.server.job.JobSchedulerService.RARE_INDEX;
import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
@@ -66,6 +67,7 @@
import android.os.Build;
import android.os.Looper;
import android.os.SystemClock;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.telephony.CellSignalStrength;
import android.telephony.SignalStrength;
import android.telephony.TelephonyCallback;
@@ -79,6 +81,7 @@
import com.android.server.net.NetworkPolicyManagerInternal;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -107,6 +110,9 @@
@Mock
private PackageManager mPackageManager;
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private Constants mConstants;
private FlexibilityController mFlexibilityController;
@@ -898,7 +904,8 @@
assertTrue(controller.isSatisfied(latePrefetchUnknownUp, net, caps, mConstants));
}
- // Metered network is only when prefetching, late, and in opportunistic quota
+ // Metered network is only when prefetching, charging*, late, and in opportunistic quota
+ // *Charging only when the flag is enabled
{
final Network net = mock(Network.class);
final NetworkCapabilities caps = createCapabilitiesBuilder()
@@ -910,6 +917,8 @@
assertFalse(controller.isSatisfied(latePrefetch, net, caps, mConstants));
assertFalse(controller.isSatisfied(latePrefetchUnknownDown, net, caps, mConstants));
assertFalse(controller.isSatisfied(latePrefetchUnknownUp, net, caps, mConstants));
+ mSetFlagsRule.disableFlags(FLAG_RELAX_PREFETCH_CONNECTIVITY_CONSTRAINT_ONLY_ON_CHARGER);
+ when(mService.isBatteryCharging()).thenReturn(false);
when(mNetPolicyManagerInternal.getSubscriptionOpportunisticQuota(
any(), eq(NetworkPolicyManagerInternal.QUOTA_TYPE_JOBS)))
@@ -918,6 +927,21 @@
// Only relax restrictions when we at least know the estimated download bytes.
assertFalse(controller.isSatisfied(latePrefetchUnknownDown, net, caps, mConstants));
assertTrue(controller.isSatisfied(latePrefetchUnknownUp, net, caps, mConstants));
+
+ mSetFlagsRule.enableFlags(FLAG_RELAX_PREFETCH_CONNECTIVITY_CONSTRAINT_ONLY_ON_CHARGER);
+ when(mNetPolicyManagerInternal.getSubscriptionOpportunisticQuota(
+ any(), eq(NetworkPolicyManagerInternal.QUOTA_TYPE_JOBS)))
+ .thenReturn(9876543210L);
+ assertFalse(controller.isSatisfied(latePrefetch, net, caps, mConstants));
+ // Only relax restrictions when we at least know the estimated download bytes.
+ assertFalse(controller.isSatisfied(latePrefetchUnknownDown, net, caps, mConstants));
+ assertFalse(controller.isSatisfied(latePrefetchUnknownUp, net, caps, mConstants));
+
+ when(mService.isBatteryCharging()).thenReturn(true);
+ assertTrue(controller.isSatisfied(latePrefetch, net, caps, mConstants));
+ // Only relax restrictions when we at least know the estimated download bytes.
+ assertFalse(controller.isSatisfied(latePrefetchUnknownDown, net, caps, mConstants));
+ assertTrue(controller.isSatisfied(latePrefetchUnknownUp, net, caps, mConstants));
}
}
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/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
index 6304270..305569e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -308,7 +308,6 @@
addDefaultProfileAndParent();
mUms.setBootUser(PROFILE_USER_ID);
-
// Boot user not switchable so return most recently in foreground.
assertWithMessage("getBootUser")
.that(mUmi.getBootUser(/* waitUntilSet= */ false)).isEqualTo(OTHER_USER_ID);
@@ -523,6 +522,24 @@
.isFalse();
}
+ @Test
+ public void testCreateUserWithLongName_TruncatesName() {
+ UserInfo user = mUms.createUserWithThrow(generateLongString(), USER_TYPE_FULL_SECONDARY, 0);
+ assertThat(user.name.length()).isEqualTo(500);
+ UserInfo user1 = mUms.createUserWithThrow("Test", USER_TYPE_FULL_SECONDARY, 0);
+ assertThat(user1.name.length()).isEqualTo(4);
+ }
+
+ private String generateLongString() {
+ String partialString = "Test Name Test Name Test Name Test Name Test Name Test Name Test "
+ + "Name Test Name Test Name Test Name "; //String of length 100
+ StringBuilder resultString = new StringBuilder();
+ for (int i = 0; i < 660; i++) {
+ resultString.append(partialString);
+ }
+ return resultString.toString();
+ }
+
private void removeNonSystemUsers() {
for (UserInfo user : mUms.getUsers(true)) {
if (!user.getUserHandle().isSystem()) {
diff --git a/services/tests/powerstatstests/Android.bp b/services/tests/powerstatstests/Android.bp
index 8ab4507..18a4f00 100644
--- a/services/tests/powerstatstests/Android.bp
+++ b/services/tests/powerstatstests/Android.bp
@@ -16,7 +16,7 @@
"coretests-aidl",
"platformprotosnano",
"junit",
- "truth-prebuilt",
+ "truth",
"androidx.test.runner",
"androidx.test.ext.junit",
"androidx.test.ext.truth",
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 20d8a5d..2ece8c7 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -2,6 +2,13 @@
// Build FrameworksServicesTests package
//########################################################################
+java_defaults {
+ name: "FrameworksServicesTests-jni-defaults",
+ jni_libs: [
+ "libservicestestjni",
+ ],
+}
+
package {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
@@ -13,6 +20,9 @@
android_test {
name: "FrameworksServicesTests",
+ defaults: [
+ "FrameworksServicesTests-jni-defaults",
+ ],
// Include all test java files.
srcs: [
@@ -51,7 +61,7 @@
"mockito-target-minus-junit4",
"platform-test-annotations",
"ShortcutManagerTestUtils",
- "truth-prebuilt",
+ "truth",
"testables",
"androidx.test.uiautomator_uiautomator",
"platformprotosnano",
@@ -62,7 +72,7 @@
// TODO: remove once Android migrates to JUnit 4.12,
// which provides assertThrows
"testng",
- "truth-prebuilt",
+ "truth",
"junit",
"junit-params",
"ActivityContext",
diff --git a/services/tests/servicestests/jni/Android.bp b/services/tests/servicestests/jni/Android.bp
new file mode 100644
index 0000000..174beb8
--- /dev/null
+++ b/services/tests/servicestests/jni/Android.bp
@@ -0,0 +1,58 @@
+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"],
+}
+
+cc_library_shared {
+ name: "libservicestestjni",
+
+ defaults: ["android.hardware.graphics.common-ndk_shared"],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+ "-Wthread-safety",
+ ],
+
+ srcs: [
+ ":lib_cachedAppOptimizer_native",
+ ":lib_gameManagerService_native",
+ ":lib_oomConnection_native",
+ "onload.cpp",
+ ],
+
+ include_dirs: [
+ "frameworks/base/libs",
+ "frameworks/native/services",
+ "frameworks/native/libs/math/include",
+ "frameworks/native/libs/ui/include",
+ "system/memory/libmeminfo/include",
+ ],
+
+ shared_libs: [
+ "libandroid",
+ "libandroid_runtime",
+ "libbase",
+ "libbinder",
+ "libgralloctypes",
+ "libgui",
+ "libhidlbase",
+ "liblog",
+ "libmeminfo",
+ "libmemevents",
+ "libnativehelper",
+ "libprocessgroup",
+ "libutils",
+ "libcutils",
+ "android.hardware.graphics.bufferqueue@1.0",
+ "android.hardware.graphics.bufferqueue@2.0",
+ "android.hardware.graphics.common@1.2",
+ "android.hardware.graphics.mapper@4.0",
+ "android.hidl.token@1.0-utils",
+ ],
+}
\ No newline at end of file
diff --git a/services/tests/servicestests/jni/onload.cpp b/services/tests/servicestests/jni/onload.cpp
new file mode 100644
index 0000000..f160b3d
--- /dev/null
+++ b/services/tests/servicestests/jni/onload.cpp
@@ -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.
+ */
+
+/*
+ * this is a mini native libaray for cached app optimizer tests to run properly. It
+ * loads all the native methods necessary.
+ */
+#include <nativehelper/JNIHelp.h>
+#include "jni.h"
+#include "utils/Log.h"
+#include "utils/misc.h"
+
+namespace android {
+int register_android_server_am_CachedAppOptimizer(JNIEnv* env);
+int register_android_server_app_GameManagerService(JNIEnv* env);
+int register_android_server_am_OomConnection(JNIEnv* env);
+};
+
+using namespace android;
+
+extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
+{
+ JNIEnv* env = NULL;
+ jint result = -1;
+
+ if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
+ ALOGE("GetEnv failed!");
+ return result;
+ }
+ ALOG_ASSERT(env, "Could not retrieve the env!");
+ register_android_server_am_CachedAppOptimizer(env);
+ register_android_server_app_GameManagerService(env);
+ register_android_server_am_OomConnection(env);
+ return JNI_VERSION_1_4;
+}
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/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 49f22ec..cf315a4 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -33,6 +33,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
@@ -53,12 +54,18 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.res.XmlResourceParser;
import android.graphics.drawable.Icon;
import android.hardware.display.DisplayManagerGlobal;
+import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.os.LocaleList;
import android.os.UserHandle;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
import android.testing.TestableContext;
import android.view.Display;
@@ -93,8 +100,13 @@
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
/**
* APCT tests for {@link AccessibilityManagerService}.
@@ -104,6 +116,10 @@
public final A11yTestableContext mTestableContext = new A11yTestableContext(
ApplicationProvider.getApplicationContext());
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+
private static final int ACTION_ID = 20;
private static final String LABEL = "label";
private static final String INTENT_ACTION = "TESTACTION";
@@ -204,6 +220,8 @@
mA11yms.getCurrentUserIdLocked());
when(mMockServiceInfo.getResolveInfo()).thenReturn(mMockResolveInfo);
mMockResolveInfo.serviceInfo = mock(ServiceInfo.class);
+ mMockResolveInfo.serviceInfo.packageName = "packageName";
+ mMockResolveInfo.serviceInfo.name = "className";
mMockResolveInfo.serviceInfo.applicationInfo = mock(ApplicationInfo.class);
when(mMockBinder.queryLocalInterface(any())).thenReturn(mMockServiceClient);
@@ -581,6 +599,73 @@
ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
}
+ @Test
+ @RequiresFlagsDisabled(Flags.FLAG_SCAN_PACKAGES_WITHOUT_LOCK)
+ // Test old behavior to validate lock detection for the old (locked access) case.
+ public void testPackageMonitorScanPackages_scansWhileHoldingLock() {
+ setupAccessibilityServiceConnection(0);
+ final AtomicReference<Set<Boolean>> lockState = collectLockStateWhilePackageScanning();
+ when(mMockPackageManager.queryIntentServicesAsUser(any(), anyInt(), anyInt()))
+ .thenReturn(List.of(mMockResolveInfo));
+ when(mMockSecurityPolicy.canRegisterService(any())).thenReturn(true);
+
+ final Intent packageIntent = new Intent(Intent.ACTION_PACKAGE_ADDED);
+ packageIntent.setData(Uri.parse("test://package"));
+ packageIntent.putExtra(Intent.EXTRA_USER_HANDLE, mA11yms.getCurrentUserIdLocked());
+ packageIntent.putExtra(Intent.EXTRA_REPLACING, true);
+ mA11yms.getPackageMonitor().doHandlePackageEvent(packageIntent);
+
+ assertThat(lockState.get()).containsExactly(true);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_SCAN_PACKAGES_WITHOUT_LOCK)
+ public void testPackageMonitorScanPackages_scansWithoutHoldingLock() {
+ setupAccessibilityServiceConnection(0);
+ final AtomicReference<Set<Boolean>> lockState = collectLockStateWhilePackageScanning();
+ when(mMockPackageManager.queryIntentServicesAsUser(any(), anyInt(), anyInt()))
+ .thenReturn(List.of(mMockResolveInfo));
+ when(mMockSecurityPolicy.canRegisterService(any())).thenReturn(true);
+
+ final Intent packageIntent = new Intent(Intent.ACTION_PACKAGE_ADDED);
+ packageIntent.setData(Uri.parse("test://package"));
+ packageIntent.putExtra(Intent.EXTRA_USER_HANDLE, mA11yms.getCurrentUserIdLocked());
+ packageIntent.putExtra(Intent.EXTRA_REPLACING, true);
+ mA11yms.getPackageMonitor().doHandlePackageEvent(packageIntent);
+
+ assertThat(lockState.get()).containsExactly(false);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_SCAN_PACKAGES_WITHOUT_LOCK)
+ public void testSwitchUserScanPackages_scansWithoutHoldingLock() {
+ setupAccessibilityServiceConnection(0);
+ final AtomicReference<Set<Boolean>> lockState = collectLockStateWhilePackageScanning();
+ when(mMockPackageManager.queryIntentServicesAsUser(any(), anyInt(), anyInt()))
+ .thenReturn(List.of(mMockResolveInfo));
+ when(mMockSecurityPolicy.canRegisterService(any())).thenReturn(true);
+
+ mA11yms.switchUser(mA11yms.getCurrentUserIdLocked() + 1);
+
+ assertThat(lockState.get()).containsExactly(false);
+ }
+
+ // Single package intents can trigger multiple PackageMonitor callbacks.
+ // Collect the state of the lock in a set, since tests only care if calls
+ // were all locked or all unlocked.
+ private AtomicReference<Set<Boolean>> collectLockStateWhilePackageScanning() {
+ final AtomicReference<Set<Boolean>> lockState =
+ new AtomicReference<>(new HashSet<Boolean>());
+ doAnswer((Answer<XmlResourceParser>) invocation -> {
+ lockState.updateAndGet(set -> {
+ set.add(mA11yms.unsafeIsLockHeld());
+ return set;
+ });
+ return null;
+ }).when(mMockResolveInfo.serviceInfo).loadXmlMetaData(any(), any());
+ return lockState;
+ }
+
private void mockManageAccessibilityGranted(TestableContext context) {
context.getTestablePermissions().setPermission(Manifest.permission.MANAGE_ACCESSIBILITY,
PackageManager.PERMISSION_GRANTED);
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/am/AnrHelperTest.java b/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
index acdfee9..c0051c6 100644
--- a/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AnrHelperTest.java
@@ -172,4 +172,9 @@
anyString(), any(), any(), any(), anyBoolean(), any(), eq(mAuxExecutorService),
anyBoolean(), anyBoolean(), any());
}
+
+ // TODO: [b/302724778] Remove manual JNI load
+ static {
+ System.loadLibrary("servicestestjni");
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/am/AnrTimerTest.java b/services/tests/servicestests/src/com/android/server/am/AnrTimerTest.java
index 9fdbdda..44d6760 100644
--- a/services/tests/servicestests/src/com/android/server/am/AnrTimerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AnrTimerTest.java
@@ -228,6 +228,7 @@
TestHandler mTestHandler;
TestInjector(int skip, boolean immediate) {
+ super(mHandler);
mTracker = new TestTracker(skip);
mImmediate = immediate;
}
@@ -237,7 +238,7 @@
}
@Override
- Handler getHandler(Handler.Callback callback) {
+ Handler newHandler(Handler.Callback callback) {
if (mTestHandler == null) {
mTestHandler = new TestHandler(mHandler.getLooper(), callback, mImmediate);
}
@@ -249,9 +250,20 @@
return mTestHandler;
}
- AnrTimer.CpuTracker getTracker() {
+ /**
+ * This override returns the tracker supplied in the constructor. It does not create a
+ * new one.
+ */
+ @Override
+ AnrTimer.CpuTracker newTracker() {
return mTracker;
}
+
+ /** For test purposes, always enable the feature. */
+ @Override
+ boolean isFeatureEnabled() {
+ return true;
+ }
}
// Tests
@@ -261,7 +273,6 @@
// 4. Start a couple of timers. Verify max active timers. Discard one and verify the active
// count drops by 1. Accept one and verify the active count drops by 1.
-
@Test
public void testSimpleTimeout() throws Exception {
// Create an immediate TestHandler.
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index 0230d77..e3e708e 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -47,6 +47,7 @@
import static org.mockito.ArgumentMatchers.eq;
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.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
@@ -64,6 +65,7 @@
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
+import android.hardware.biometrics.IBiometricPromptStatusListener;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceReceiver;
@@ -1751,6 +1753,45 @@
verifyNoMoreInteractions(callback);
}
+ @Test
+ public void testRegisterBiometricPromptOnKeyguardCallback_authenticationAlreadyStarted()
+ throws Exception {
+ final IBiometricPromptStatusListener callback =
+ mock(IBiometricPromptStatusListener.class);
+
+ setupAuthForOnly(TYPE_FACE, Authenticators.BIOMETRIC_STRONG);
+ invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+ true /* requireConfirmation */, null /* authenticators */);
+ mBiometricService.mImpl.registerBiometricPromptStatusListener(callback);
+
+ verify(callback).onBiometricPromptShowing();
+ }
+
+ @Test
+ public void testRegisterBiometricPromptOnKeyguardCallback_startAuth_dismissDialog()
+ throws Exception {
+ final IBiometricPromptStatusListener listener =
+ mock(IBiometricPromptStatusListener.class);
+ setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
+ mBiometricService.mImpl.registerBiometricPromptStatusListener(listener);
+ waitForIdle();
+
+ verify(listener).onBiometricPromptIdle();
+
+ invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+ true /* requireConfirmation */, null /* authenticators */);
+ waitForIdle();
+
+ verify(listener).onBiometricPromptShowing();
+
+ final byte[] hat = generateRandomHAT();
+ mBiometricService.mAuthSession.mSysuiReceiver.onDialogDismissed(
+ BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED, hat);
+ waitForIdle();
+
+ verify(listener, times(2)).onBiometricPromptIdle();
+ }
+
// Helper methods
private int invokeCanAuthenticate(BiometricService service, int authenticators)
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
index 9e5a047..3a3dd6e 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
@@ -36,6 +36,7 @@
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.content.ComponentName;
+import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.common.AuthenticateReason;
import android.hardware.biometrics.common.ICancellationSignal;
@@ -59,6 +60,7 @@
import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.face.UsageStats;
import org.junit.Before;
@@ -103,7 +105,7 @@
@Mock
private ClientMonitorCallback mCallback;
@Mock
- private Sensor.HalSessionCallback mHalSessionCallback;
+ private AidlResponseHandler mAidlResponseHandler;
@Mock
private ActivityTaskManager mActivityTaskManager;
@Mock
@@ -112,6 +114,8 @@
private AuthSessionCoordinator mAuthSessionCoordinator;
@Mock
private BiometricManager mBiometricManager;
+ @Mock
+ private LockoutTracker mLockoutTracker;
@Captor
private ArgumentCaptor<OperationContextExt> mOperationContextCaptor;
@Captor
@@ -236,27 +240,63 @@
verify(mCallback).onClientFinished(client, true);
}
+ @Test
+ public void authWithNoLockout() throws RemoteException {
+ when(mLockoutTracker.getLockoutModeForUser(anyInt())).thenReturn(
+ LockoutTracker.LOCKOUT_NONE);
+
+ final FaceAuthenticationClient client = createClientWithLockoutTracker(mLockoutTracker);
+ client.start(mCallback);
+
+ verify(mHal).authenticate(OP_ID);
+ }
+
+ @Test
+ public void authWithLockout() throws RemoteException {
+ when(mLockoutTracker.getLockoutModeForUser(anyInt())).thenReturn(
+ LockoutTracker.LOCKOUT_PERMANENT);
+
+ final FaceAuthenticationClient client = createClientWithLockoutTracker(mLockoutTracker);
+ client.start(mCallback);
+
+ verify(mClientMonitorCallbackConverter).onError(anyInt(), anyInt(),
+ eq(BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT), anyInt());
+ verify(mHal, never()).authenticate(anyInt());
+ }
+
private FaceAuthenticationClient createClient() throws RemoteException {
return createClient(2 /* version */, mClientMonitorCallbackConverter,
- false /* allowBackgroundAuthentication */);
+ false /* allowBackgroundAuthentication */,
+ null /* lockoutTracker */);
}
private FaceAuthenticationClient createClientWithNullListener() throws RemoteException {
return createClient(2 /* version */, null /* listener */,
- true /* allowBackgroundAuthentication */);
+ true /* allowBackgroundAuthentication */,
+ null /* lockoutTracker */);
}
private FaceAuthenticationClient createClient(int version) throws RemoteException {
return createClient(version, mClientMonitorCallbackConverter,
- false /* allowBackgroundAuthentication */);
+ false /* allowBackgroundAuthentication */,
+ null /* lockoutTracker */);
+ }
+
+ private FaceAuthenticationClient createClientWithLockoutTracker(LockoutTracker lockoutTracker)
+ throws RemoteException {
+ return createClient(0 /* version */,
+ mClientMonitorCallbackConverter,
+ true /* allowBackgroundAuthentication */,
+ lockoutTracker);
}
private FaceAuthenticationClient createClient(int version,
ClientMonitorCallbackConverter listener,
- boolean allowBackgroundAuthentication) throws RemoteException {
+ boolean allowBackgroundAuthentication,
+ LockoutTracker lockoutTracker) throws RemoteException {
when(mHal.getInterfaceVersion()).thenReturn(version);
- final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
+ final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mAidlResponseHandler);
final FaceAuthenticateOptions options = new FaceAuthenticateOptions.Builder()
.setOpPackageName("test-owner")
.setUserId(USER_ID)
@@ -270,7 +310,7 @@
false /* restricted */, options, 4 /* cookie */,
false /* requireConfirmation */,
mBiometricLogger, mBiometricContext, true /* isStrongBiometric */,
- mUsageStats, null /* mLockoutCache */, allowBackgroundAuthentication,
+ mUsageStats, lockoutTracker, allowBackgroundAuthentication,
null /* sensorPrivacyManager */, 0 /* biometricStrength */) {
@Override
protected ActivityTaskManager getActivityTaskManager() {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
index ade3e82..fbf0e13 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
@@ -87,7 +87,7 @@
@Mock
private ClientMonitorCallback mCallback;
@Mock
- private Sensor.HalSessionCallback mHalSessionCallback;
+ private AidlResponseHandler mAidlResponseHandler;
@Captor
private ArgumentCaptor<OperationContextExt> mOperationContextCaptor;
@Captor
@@ -170,7 +170,7 @@
private FaceDetectClient createClient(int version) throws RemoteException {
when(mHal.getInterfaceVersion()).thenReturn(version);
- final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
+ final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mAidlResponseHandler);
return new FaceDetectClient(mContext, () -> aidl, mToken,
99 /* requestId */, mClientMonitorCallbackConverter,
new FaceAuthenticateOptions.Builder()
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
index 54d116f..128f314 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
@@ -83,7 +83,7 @@
@Mock
private ClientMonitorCallback mCallback;
@Mock
- private Sensor.HalSessionCallback mHalSessionCallback;
+ private AidlResponseHandler mAidlResponseHandler;
@Captor
private ArgumentCaptor<OperationContextExt> mOperationContextCaptor;
@Captor
@@ -150,7 +150,7 @@
private FaceEnrollClient createClient(int version) throws RemoteException {
when(mHal.getInterfaceVersion()).thenReturn(version);
- final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
+ final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mAidlResponseHandler);
return new FaceEnrollClient(mContext, () -> aidl, mToken, mClientMonitorCallbackConverter,
USER_ID, HAT, "com.foo.bar", 44 /* requestId */,
mUtils, new int[0] /* disabledFeatures */, 6 /* timeoutSec */,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClientTest.java
new file mode 100644
index 0000000..c8bfaa9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClientTest.java
@@ -0,0 +1,102 @@
+/*
+ * 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.biometrics.sensors.face.aidl;
+
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.biometrics.face.ISession;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@Presubmit
+@SmallTest
+public class FaceGenerateChallengeClientTest {
+ private static final String TAG = "FaceGenerateChallengeClientTest";
+ private static final int USER_ID = 2;
+ private static final int SENSOR_ID = 4;
+ private static final long CHALLENGE = 200;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private AidlSession mAidlSession;
+ @Mock
+ private ISession mSession;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private ClientMonitorCallbackConverter mListener;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ private Context mContext;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+
+ private FaceGenerateChallengeClient mClient;
+
+ @Before
+ public void setUp() throws RemoteException {
+ when(mAidlSession.getSession()).thenReturn(mSession);
+ doAnswer(invocation -> {
+ mClient.onChallengeGenerated(SENSOR_ID, USER_ID, CHALLENGE);
+ return null;
+ }).when(mSession).generateChallenge();
+ }
+
+ @Test
+ public void generateChallenge() throws RemoteException {
+ createClient(mListener);
+ mClient.start(mCallback);
+
+ verify(mListener).onChallengeGenerated(SENSOR_ID, USER_ID, CHALLENGE);
+ verify(mCallback).onClientFinished(mClient, true);
+ }
+
+ @Test
+ public void generateChallenge_nullListener() {
+ createClient(null);
+ mClient.start(mCallback);
+
+ verify(mCallback).onClientFinished(mClient, false);
+ }
+
+ private void createClient(ClientMonitorCallbackConverter listener) {
+ mClient = new FaceGenerateChallengeClient(mContext, () -> mAidlSession, mToken, listener,
+ USER_ID, TAG, SENSOR_ID, mBiometricLogger, mBiometricContext);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClientTest.java
new file mode 100644
index 0000000..9d0c84e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClientTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.biometrics.sensors.face.aidl;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.biometrics.BiometricFaceConstants;
+import android.hardware.biometrics.face.ISession;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.TestableContext;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.List;
+
+@Presubmit
+@SmallTest
+public class FaceGetFeatureClientTest {
+ private static final String TAG = "FaceGetFeatureClientTest";
+ private static final int USER_ID = 2;
+ private static final int SENSOR_ID = 4;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private AidlSession mAidlSession;
+ @Mock
+ private ISession mSession;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private ClientMonitorCallbackConverter mListener;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getContext());
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+
+ private final int mFeature = BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION;
+ private FaceGetFeatureClient mClient;
+
+ @Before
+ public void setUp() throws RemoteException {
+ mClient = new FaceGetFeatureClient(mContext, () -> mAidlSession, mToken, mListener,
+ USER_ID, TAG, SENSOR_ID, mBiometricLogger, mBiometricContext, mFeature);
+
+ when(mAidlSession.getSession()).thenReturn(mSession);
+ doAnswer(invocation -> {
+ mClient.onFeatureGet(true, new byte[]{
+ AidlConversionUtils.convertFrameworkToAidlFeature(mFeature)});
+ return null;
+ }).when(mSession).getFeatures();
+ }
+
+ @Test
+ public void getFeature() throws RemoteException {
+ ArgumentCaptor<int[]> featuresToSend = ArgumentCaptor.forClass(int[].class);
+ ArgumentCaptor<boolean[]> featureState = ArgumentCaptor.forClass(boolean[].class);
+ mClient.start(mCallback);
+
+ verify(mListener).onFeatureGet(eq(true), featuresToSend.capture(),
+ featureState.capture());
+ assertThat(featuresToSend.getValue()).asList().containsExactlyElementsIn(List.of(mFeature));
+ assertThat(featureState.getValue()).asList().containsExactlyElementsIn(List.of(true));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClientTest.java
new file mode 100644
index 0000000..1b4c017
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClientTest.java
@@ -0,0 +1,187 @@
+/*
+ * 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.biometrics.sensors.face.aidl;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.face.ISession;
+import android.hardware.face.Face;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.annotation.NonNull;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@Presubmit
+@SmallTest
+public class FaceInternalCleanupClientTest {
+ private static final String TAG = "FaceInternalCleanupClientTest";
+ private static final int USER_ID = 2;
+ private static final int SENSOR_ID = 4;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private AidlSession mAidlSession;
+ @Mock
+ private ISession mSession;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ Context mContext;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+ @Mock
+ private BiometricUtils<Face> mBiometricUtils;
+ @Mock
+ private Map<Integer, Long> mAuthenticatorIds;
+
+ private final List<Face> mEnrolledList = new ArrayList<>();
+ private final int mBiometricId = 1;
+ private final Face mFace = new Face("face", mBiometricId, 1 /* deviceId */);
+ private FaceInternalCleanupClient mClient;
+ private List<Integer> mAddedIds;
+
+ @Before
+ public void setUp() throws RemoteException {
+ when(mAidlSession.getSession()).thenReturn(mSession);
+
+ mEnrolledList.add(mFace);
+ mAddedIds = new ArrayList<>();
+ mClient = new FaceInternalCleanupClient(mContext, () -> mAidlSession, USER_ID, TAG,
+ SENSOR_ID, mBiometricLogger, mBiometricContext, mBiometricUtils,
+ mAuthenticatorIds) {
+ @Override
+ protected void onAddUnknownTemplate(int userId,
+ @NonNull BiometricAuthenticator.Identifier identifier) {
+ mAddedIds.add(identifier.getBiometricId());
+ }
+ };
+ }
+
+ @Test
+ public void removesUnknownTemplate() throws Exception {
+ final List<Face> templates = List.of(
+ new Face("one", 1, 1),
+ new Face("two", 2, 1)
+ );
+ mClient.start(mCallback);
+ for (int i = templates.size() - 1; i >= 0; i--) {
+ mClient.getCurrentEnumerateClient().onEnumerationResult(templates.get(i), i);
+ }
+ for (int i = templates.size() - 1; i >= 0; i--) {
+ mClient.getCurrentRemoveClient().onRemoved(templates.get(i), 0);
+ }
+
+ assertThat(mAddedIds).isEmpty();
+ final ArgumentCaptor<int[]> captor = ArgumentCaptor.forClass(int[].class);
+
+ verify(mSession, times(2)).removeEnrollments(captor.capture());
+ assertThat(captor.getAllValues().stream()
+ .flatMap(x -> Arrays.stream(x).boxed())
+ .collect(Collectors.toList()))
+ .containsExactly(1, 2);
+ verify(mCallback).onClientFinished(eq(mClient), eq(true));
+ }
+
+ @Test
+ public void addsUnknownTemplateWhenVirtualIsEnabled() throws Exception {
+ mClient.setFavorHalEnrollments();
+ final List<Face> templates = List.of(
+ new Face("one", 1, 1),
+ new Face("two", 2, 1)
+ );
+ mClient.start(mCallback);
+ for (int i = templates.size() - 1; i >= 0; i--) {
+ mClient.getCurrentEnumerateClient().onEnumerationResult(templates.get(i), i);
+ }
+
+ assertThat(mAddedIds).containsExactly(1, 2);
+ verify(mSession, never()).removeEnrollments(any());
+ verify(mCallback).onClientFinished(eq(mClient), eq(true));
+ }
+
+ @Test
+ public void cleanupUnknownHalTemplatesAfterEnumerationWhenVirtualIsDisabled() {
+ final List<Face> templates = List.of(
+ new Face("one", 1, 1),
+ new Face("two", 2, 1),
+ new Face("three", 3, 1)
+ );
+ mClient.start(mCallback);
+ for (int i = templates.size() - 1; i >= 0; i--) {
+ mClient.getCurrentEnumerateClient().onEnumerationResult(templates.get(i), i);
+ }
+
+ // The first template is removed after enumeration
+ assertThat(mClient.getUnknownHALTemplates().size()).isEqualTo(2);
+
+ // Simulate finishing the removal of the first template.
+ // |remaining| is 0 because one FaceRemovalClient is associated with only one
+ // biometrics ID.
+ mClient.getCurrentRemoveClient().onRemoved(templates.get(0), 0);
+
+ assertThat(mClient.getUnknownHALTemplates().size()).isEqualTo(1);
+
+ // Simulate finishing the removal of the second template.
+ mClient.getCurrentRemoveClient().onRemoved(templates.get(1), 0);
+
+ assertThat(mClient.getUnknownHALTemplates()).isEmpty();
+ }
+
+ @Test
+ public void noUnknownTemplates() throws RemoteException {
+ mClient.start(mCallback);
+ mClient.getCurrentEnumerateClient().onEnumerationResult(null, 0);
+
+ verify(mSession).enumerateEnrollments();
+ assertThat(mClient.getUnknownHALTemplates().size()).isEqualTo(0);
+ verify(mSession, never()).removeEnrollments(any());
+ verify(mCallback).onClientFinished(mClient, true);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClientTest.java
new file mode 100644
index 0000000..8d74fd1
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClientTest.java
@@ -0,0 +1,169 @@
+/*
+ * 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.biometrics.sensors.face.aidl;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.biometrics.face.ISession;
+import android.hardware.face.Face;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Presubmit
+@SmallTest
+public class FaceInternalEnumerateClientTest {
+ private static final String TAG = "FaceInternalEnumerateClientTest";
+ private static final int USER_ID = 2;
+ private static final int SENSOR_ID = 4;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private AidlSession mAidlSession;
+ @Mock
+ private ISession mSession;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ Context mContext;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+ @Mock
+ private BiometricUtils<Face> mBiometricUtils;
+
+ private final int mBiometricId = 1;
+ private final Face mFace = new Face("face", mBiometricId, 1 /* deviceId */);
+ private FaceInternalEnumerateClient mClient;
+
+ @Before
+ public void setUp() {
+ when(mAidlSession.getSession()).thenReturn(mSession);
+
+ final List<Face> enrolled = new ArrayList<>();
+ enrolled.add(mFace);
+ mClient = new FaceInternalEnumerateClient(mContext, () -> mAidlSession, mToken, USER_ID,
+ TAG, enrolled, mBiometricUtils, SENSOR_ID, mBiometricLogger, mBiometricContext);
+ }
+
+ @Test
+ public void internalCleanupClient_noTemplatesRemaining() throws RemoteException {
+ doAnswer(invocation -> {
+ mClient.onEnumerationResult(mFace, 0);
+ return null;
+ }).when(mSession).enumerateEnrollments();
+
+ mClient.start(mCallback);
+
+ verify(mSession).enumerateEnrollments();
+ assertThat(mClient.getUnknownHALTemplates().size()).isEqualTo(0);
+ verify(mBiometricUtils, never()).removeBiometricForUser(any(), anyInt(), anyInt());
+ verify(mCallback).onClientFinished(mClient, true);
+ }
+
+ @Test
+ public void internalCleanupClient_nullIdentifier_remainingOne() throws RemoteException {
+ doAnswer(invocation -> {
+ mClient.onEnumerationResult(null, 1);
+ return null;
+ }).when(mSession).enumerateEnrollments();
+
+ mClient.start(mCallback);
+
+ verify(mSession).enumerateEnrollments();
+ assertThat(mClient.getUnknownHALTemplates().size()).isEqualTo(0);
+ verify(mBiometricUtils, never()).removeBiometricForUser(any(), anyInt(), anyInt());
+ verify(mCallback, never()).onClientFinished(mClient, true);
+ }
+
+ @Test
+ public void internalCleanupClient_nullIdentifier_noTemplatesRemaining() throws RemoteException {
+ doAnswer(invocation -> {
+ mClient.onEnumerationResult(null, 0);
+ return null;
+ }).when(mSession).enumerateEnrollments();
+
+ mClient.start(mCallback);
+
+ verify(mSession).enumerateEnrollments();
+ assertThat(mClient.getUnknownHALTemplates().size()).isEqualTo(0);
+ verify(mBiometricUtils).removeBiometricForUser(mContext, USER_ID, mBiometricId);
+ verify(mCallback).onClientFinished(mClient, true);
+ }
+
+ @Test
+ public void internalCleanupClient_templatesRemaining() throws RemoteException {
+ final Face identifier = new Face("face", 2, 1);
+ doAnswer(invocation -> {
+ mClient.onEnumerationResult(identifier, 1);
+ return null;
+ }).when(mSession).enumerateEnrollments();
+
+ mClient.start(mCallback);
+
+ verify(mSession).enumerateEnrollments();
+ assertThat(mClient.getUnknownHALTemplates().size()).isEqualTo(1);
+ verify(mBiometricUtils, never()).removeBiometricForUser(any(), anyInt(), anyInt());
+ verify(mCallback, never()).onClientFinished(mClient, true);
+ }
+
+ @Test
+ public void internalCleanupClient_differentIdentifier_noTemplatesRemaining()
+ throws RemoteException {
+ final Face identifier = new Face("face", 2, 1);
+ doAnswer(invocation -> {
+ mClient.onEnumerationResult(identifier, 0);
+ return null;
+ }).when(mSession).enumerateEnrollments();
+
+ mClient.start(mCallback);
+
+ verify(mSession).enumerateEnrollments();
+ assertThat(mClient.getUnknownHALTemplates().size()).isEqualTo(1);
+ verify(mBiometricUtils).removeBiometricForUser(mContext, USER_ID, mBiometricId);
+ verify(mCallback).onClientFinished(mClient, true);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClientTest.java
index 76a5acc..1d9e933 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClientTest.java
@@ -76,7 +76,7 @@
@Mock
private ClientMonitorCallback mCallback;
@Mock
- private Sensor.HalSessionCallback mHalSessionCallback;
+ private AidlResponseHandler mAidlResponseHandler;
@Mock
private BiometricUtils<Face> mUtils;
@Mock
@@ -115,7 +115,7 @@
private FaceRemovalClient createClient(int version, int[] biometricIds) throws RemoteException {
when(mHal.getInterfaceVersion()).thenReturn(version);
- final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
+ final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mAidlResponseHandler);
return new FaceRemovalClient(mContext, () -> aidl, mToken,
mClientMonitorCallbackConverter, biometricIds, USER_ID,
"own-it", mUtils /* utils */, 5 /* sensorId */, mBiometricLogger, mBiometricContext,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClientTest.java
new file mode 100644
index 0000000..dbbd69b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClientTest.java
@@ -0,0 +1,121 @@
+/*
+ * 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.biometrics.sensors.face.aidl;
+
+import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_STRONG;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.biometrics.face.ISession;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.AuthSessionCoordinator;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+import com.android.server.biometrics.sensors.LockoutCache;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.LockoutTracker;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@Presubmit
+@SmallTest
+public class FaceResetLockoutClientTest {
+ private static final String TAG = "FaceResetLockoutClientTest";
+ private static final int USER_ID = 2;
+ private static final int SENSOR_ID = 4;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private AidlSession mAidlSession;
+ @Mock
+ private ISession mSession;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ Context mContext;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+ private final byte[] mHardwareAuthToken = new byte[69];
+ @Mock
+ private LockoutCache mLockoutTracker;
+ @Mock
+ private LockoutResetDispatcher mLockoutResetDispatcher;
+ @Mock
+ private AuthSessionCoordinator mAuthSessionCoordinator;
+
+ private FaceResetLockoutClient mClient;
+
+ @Before
+ public void setUp() {
+ mClient = new FaceResetLockoutClient(mContext, () -> mAidlSession, USER_ID, TAG, SENSOR_ID,
+ mBiometricLogger, mBiometricContext, mHardwareAuthToken, mLockoutTracker,
+ mLockoutResetDispatcher, BIOMETRIC_STRONG);
+
+ when(mAidlSession.getSession()).thenReturn(mSession);
+ when(mBiometricContext.getAuthSessionCoordinator()).thenReturn(mAuthSessionCoordinator);
+ }
+
+ @Test
+ public void testResetLockout_onLockoutCleared() throws RemoteException {
+ doAnswer(invocation -> {
+ mClient.onLockoutCleared();
+ return null;
+ }).when(mSession).resetLockout(any());
+ mClient.start(mCallback);
+
+ verify(mSession).resetLockout(any());
+ verify(mAuthSessionCoordinator).resetLockoutFor(USER_ID, BIOMETRIC_STRONG, -1);
+ verify(mLockoutTracker).setLockoutModeForUser(USER_ID, LockoutTracker.LOCKOUT_NONE);
+ verify(mLockoutResetDispatcher).notifyLockoutResetCallbacks(SENSOR_ID);
+ verify(mCallback).onClientFinished(mClient, true);
+ }
+
+ @Test
+ public void testResetLockout_onError() throws RemoteException {
+ doAnswer(invocation -> {
+ mClient.onError(0, 0);
+ return null;
+ }).when(mSession).resetLockout(any());
+ mClient.start(mCallback);
+
+ verify(mSession).resetLockout(any());
+ verify(mAuthSessionCoordinator, never()).resetLockoutFor(USER_ID,
+ BIOMETRIC_STRONG, -1);
+ verify(mLockoutTracker, never()).setLockoutModeForUser(USER_ID,
+ LockoutTracker.LOCKOUT_NONE);
+ verify(mLockoutResetDispatcher, never()).notifyLockoutResetCallbacks(SENSOR_ID);
+ verify(mCallback).onClientFinished(mClient, false);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClientTest.java
new file mode 100644
index 0000000..fb5502a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClientTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.biometrics.sensors.face.aidl;
+
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.biometrics.face.ISession;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@Presubmit
+@SmallTest
+public class FaceRevokeChallengeClientTest {
+ private static final String TAG = "FaceRevokeChallengeClientTest";
+ private static final long CHALLENGE = 200L;
+ private static final int USER_ID = 2;
+ private static final int SENSOR_ID = 4;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private AidlSession mAidlSession;
+ @Mock
+ private ISession mSession;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ Context mContext;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+
+ private FaceRevokeChallengeClient mClient;
+
+ @Before
+ public void setUp() {
+ when(mAidlSession.getSession()).thenReturn(mSession);
+
+ mClient = new FaceRevokeChallengeClient(mContext, () -> mAidlSession, mToken, USER_ID, TAG,
+ SENSOR_ID, mBiometricLogger, mBiometricContext, CHALLENGE);
+ }
+
+ @Test
+ public void revokeChallenge() throws RemoteException {
+ doAnswer(invocation -> {
+ mClient.onChallengeRevoked(SENSOR_ID, USER_ID, CHALLENGE);
+ return null;
+ }).when(mSession).revokeChallenge(CHALLENGE);
+ mClient.start(mCallback);
+
+ verify(mSession).revokeChallenge(CHALLENGE);
+ verify(mCallback).onClientFinished(mClient, true);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClientTest.java
new file mode 100644
index 0000000..eb8cc9c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClientTest.java
@@ -0,0 +1,120 @@
+/*
+ * 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.biometrics.sensors.face.aidl;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.biometrics.BiometricFaceConstants;
+import android.hardware.biometrics.face.ISession;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.TestableContext;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@Presubmit
+@SmallTest
+public class FaceSetFeatureClientTest {
+ private static final String TAG = "FaceSetFeatureClientTest";
+ private static final int USER_ID = 2;
+ private static final int SENSOR_ID = 4;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private AidlSession mAidlSession;
+ @Mock
+ private ISession mSession;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private ClientMonitorCallbackConverter mListener;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+
+ private final int mFeature = BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION;
+ private final boolean mEnabled = true;
+ private final byte[] mHardwareAuthToken = new byte[69];
+ private FaceSetFeatureClient mClient;
+ TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getContext());
+
+ @Before
+ public void setUp() {
+ when(mAidlSession.getSession()).thenReturn(mSession);
+
+ mClient = new FaceSetFeatureClient(mContext, () -> mAidlSession, mToken, mListener, USER_ID,
+ TAG, SENSOR_ID, mBiometricLogger, mBiometricContext, mFeature, mEnabled,
+ mHardwareAuthToken);
+ }
+
+ @Test
+ public void setFeature_onFeatureSet() throws RemoteException {
+ doAnswer(invocation -> {
+ mClient.onFeatureSet(true);
+ return null;
+ }).when(mSession).setFeature(any(),
+ eq(AidlConversionUtils.convertFrameworkToAidlFeature(mFeature)), eq(mEnabled));
+ mClient.start(mCallback);
+
+ verify(mSession).setFeature(any(),
+ eq(AidlConversionUtils.convertFrameworkToAidlFeature(mFeature)),
+ eq(mEnabled));
+ verify(mListener).onFeatureSet(true, mFeature);
+ verify(mCallback).onClientFinished(mClient, true);
+ }
+
+ @Test
+ public void setFeature_onError() throws RemoteException {
+ doAnswer(invocation -> {
+ mClient.onError(0, 0);
+ return null;
+ }).when(mSession).setFeature(any(),
+ eq(AidlConversionUtils.convertFrameworkToAidlFeature(mFeature)),
+ eq(mEnabled));
+ mClient.start(mCallback);
+
+ verify(mSession).setFeature(any(),
+ eq(AidlConversionUtils.convertFrameworkToAidlFeature(mFeature)),
+ eq(mEnabled));
+ verify(mListener).onFeatureSet(false, mFeature);
+ verify(mCallback).onClientFinished(mClient, false);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
index be9f52e..7a293e8 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
@@ -74,7 +74,7 @@
@Mock
private UserAwareBiometricScheduler.UserSwitchCallback mUserSwitchCallback;
@Mock
- private Sensor.HalSessionCallback.Callback mHalSessionCallback;
+ private AidlResponseHandler.HardwareUnavailableCallback mHardwareUnavailableCallback;
@Mock
private LockoutResetDispatcher mLockoutResetDispatcher;
@Mock
@@ -94,7 +94,7 @@
private final LockoutCache mLockoutCache = new LockoutCache();
private UserAwareBiometricScheduler mScheduler;
- private Sensor.HalSessionCallback mHalCallback;
+ private AidlResponseHandler mHalCallback;
@Before
public void setUp() {
@@ -111,10 +111,9 @@
mBiometricService,
() -> USER_ID,
mUserSwitchCallback);
- mHalCallback = new Sensor.HalSessionCallback(mContext, new Handler(mLooper.getLooper()),
- TAG, mScheduler, SENSOR_ID,
- USER_ID, mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator,
- mHalSessionCallback);
+ mHalCallback = new AidlResponseHandler(mContext, mScheduler, SENSOR_ID, USER_ID,
+ mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator,
+ mHardwareUnavailableCallback);
}
@Test
@@ -153,11 +152,11 @@
sensorProps.commonProps.sensorId = 1;
final FaceSensorPropertiesInternal internalProp = new FaceSensorPropertiesInternal(
sensorProps.commonProps.sensorId, sensorProps.commonProps.sensorStrength,
- sensorProps.commonProps.maxEnrollmentsPerUser, null,
+ sensorProps.commonProps.maxEnrollmentsPerUser, null /* componentInfo */,
sensorProps.sensorType, sensorProps.supportsDetectInteraction,
sensorProps.halControlsPreview, false /* resetLockoutRequiresChallenge */);
- final Sensor sensor = new Sensor("SensorTest", mFaceProvider, mContext, null,
- internalProp, mLockoutResetDispatcher, mBiometricContext);
+ final Sensor sensor = new Sensor("SensorTest", mFaceProvider, mContext,
+ null /* handler */, internalProp, mLockoutResetDispatcher, mBiometricContext);
mScheduler.reset();
@@ -181,7 +180,7 @@
sensorProps.commonProps.sensorId = 1;
final FaceSensorPropertiesInternal internalProp = new FaceSensorPropertiesInternal(
sensorProps.commonProps.sensorId, sensorProps.commonProps.sensorStrength,
- sensorProps.commonProps.maxEnrollmentsPerUser, null,
+ sensorProps.commonProps.maxEnrollmentsPerUser, null /* componentInfo */,
sensorProps.sensorType, sensorProps.supportsDetectInteraction,
sensorProps.halControlsPreview, false /* resetLockoutRequiresChallenge */);
final Sensor sensor = new Sensor("SensorTest", mFaceProvider, mContext, null,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/AidlToHidlAdapterTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/AidlToHidlAdapterTest.java
new file mode 100644
index 0000000..9a40e8a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/AidlToHidlAdapterTest.java
@@ -0,0 +1,342 @@
+/*
+ * 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.biometrics.sensors.face.hidl;
+
+import static com.android.server.biometrics.sensors.face.hidl.AidlToHidlAdapter.ENROLL_TIMEOUT_SEC;
+import static com.android.server.biometrics.sensors.face.hidl.FaceGenerateChallengeClient.CHALLENGE_TIMEOUT_SEC;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.hardware.biometrics.BiometricFaceConstants;
+import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.biometrics.face.EnrollmentType;
+import android.hardware.biometrics.face.Feature;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.biometrics.face.V1_0.OptionalBool;
+import android.hardware.biometrics.face.V1_0.OptionalUint64;
+import android.hardware.biometrics.face.V1_0.Status;
+import android.hardware.face.Face;
+import android.hardware.face.FaceManager;
+import android.hardware.keymaster.HardwareAuthToken;
+import android.hardware.keymaster.Timestamp;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.TestableContext;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.sensors.face.aidl.AidlConversionUtils;
+import com.android.server.biometrics.sensors.face.aidl.AidlResponseHandler;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.time.Clock;
+import java.util.ArrayList;
+import java.util.List;
+
+@Presubmit
+@SmallTest
+public class AidlToHidlAdapterTest {
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private IBiometricsFace mSession;
+ @Mock
+ FaceManager mFaceManager;
+ @Mock
+ private AidlResponseHandler mAidlResponseHandler;
+ @Mock
+ private HardwareAuthToken mHardwareAuthToken;
+ @Mock
+ private Clock mClock;
+
+ private final long mChallenge = 100L;
+ private AidlToHidlAdapter mAidlToHidlAdapter;
+ private final Face mFace = new Face("face" /* name */, 1 /* faceId */, 0 /* deviceId */);
+ private final int mFeature = BiometricFaceConstants.FEATURE_REQUIRE_REQUIRE_DIVERSITY;
+ private final byte[] mFeatures = new byte[]{Feature.REQUIRE_ATTENTION};
+
+ @Before
+ public void setUp() throws RemoteException {
+ TestableContext testableContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getContext());
+ testableContext.addMockSystemService(FaceManager.class, mFaceManager);
+ mAidlToHidlAdapter = new AidlToHidlAdapter(testableContext, () -> mSession, 0 /* userId */,
+ mAidlResponseHandler, mClock);
+ mHardwareAuthToken.timestamp = new Timestamp();
+ mHardwareAuthToken.mac = new byte[10];
+ final OptionalUint64 result = new OptionalUint64();
+ result.status = Status.OK;
+ result.value = mChallenge;
+
+ when(mSession.generateChallenge(anyInt())).thenReturn(result);
+ when(mFaceManager.getEnrolledFaces(anyInt())).thenReturn(List.of(mFace));
+ }
+
+ @Test
+ public void testGenerateChallengeCache() throws RemoteException {
+ verify(mSession).setCallback(any());
+
+ final ArgumentCaptor<Long> challengeCaptor = ArgumentCaptor.forClass(Long.class);
+
+ mAidlToHidlAdapter.generateChallenge();
+
+ verify(mSession).generateChallenge(CHALLENGE_TIMEOUT_SEC);
+ verify(mAidlResponseHandler).onChallengeGenerated(challengeCaptor.capture());
+ assertThat(challengeCaptor.getValue()).isEqualTo(mChallenge);
+
+ forwardTime(10 /* seconds */);
+ mAidlToHidlAdapter.generateChallenge();
+ forwardTime(20 /* seconds */);
+ mAidlToHidlAdapter.generateChallenge();
+
+ //Confirms that the challenge is cached and the hal method is not called again
+ verifyNoMoreInteractions(mSession);
+ verify(mAidlResponseHandler, times(3))
+ .onChallengeGenerated(mChallenge);
+
+ forwardTime(60 /* seconds */);
+ mAidlToHidlAdapter.generateChallenge();
+
+ //HAL method called after challenge has timed out
+ verify(mSession, times(2)).generateChallenge(CHALLENGE_TIMEOUT_SEC);
+ }
+
+ @Test
+ public void testRevokeChallenge_waitsUntilEmpty() throws RemoteException {
+ for (int i = 0; i < 3; i++) {
+ mAidlToHidlAdapter.generateChallenge();
+ forwardTime(10 /* seconds */);
+ }
+ for (int i = 0; i < 3; i++) {
+ mAidlToHidlAdapter.revokeChallenge(0);
+ forwardTime((i + 1) * 10 /* seconds */);
+ }
+
+ verify(mSession).revokeChallenge();
+ }
+
+ @Test
+ public void testRevokeChallenge_timeout() throws RemoteException {
+ mAidlToHidlAdapter.generateChallenge();
+ mAidlToHidlAdapter.generateChallenge();
+ forwardTime(700);
+ mAidlToHidlAdapter.generateChallenge();
+ mAidlToHidlAdapter.revokeChallenge(0);
+
+ verify(mSession).revokeChallenge();
+ }
+
+ @Test
+ public void testEnroll() throws RemoteException {
+ ICancellationSignal cancellationSignal = mAidlToHidlAdapter.enroll(mHardwareAuthToken,
+ EnrollmentType.DEFAULT, mFeatures,
+ null /* previewSurface */);
+ ArgumentCaptor<ArrayList<Integer>> featureCaptor = ArgumentCaptor.forClass(ArrayList.class);
+
+ verify(mSession).enroll(any(), eq(ENROLL_TIMEOUT_SEC), featureCaptor.capture());
+
+ ArrayList<Integer> features = featureCaptor.getValue();
+
+ assertThat(features).containsExactly(
+ AidlConversionUtils.convertAidlToFrameworkFeature(mFeatures[0]));
+
+ cancellationSignal.cancel();
+
+ verify(mSession).cancel();
+ }
+
+ @Test
+ public void testAuthenticate() throws RemoteException {
+ final int operationId = 2;
+ ICancellationSignal cancellationSignal = mAidlToHidlAdapter.authenticate(operationId);
+
+ verify(mSession).authenticate(operationId);
+
+ cancellationSignal.cancel();
+
+ verify(mSession).cancel();
+ }
+
+ @Test
+ public void testDetectInteraction() throws RemoteException {
+ ICancellationSignal cancellationSignal = mAidlToHidlAdapter.detectInteraction();
+
+ verify(mSession).authenticate(0);
+
+ cancellationSignal.cancel();
+
+ verify(mSession).cancel();
+ }
+
+ @Test
+ public void testEnumerateEnrollments() throws RemoteException {
+ mAidlToHidlAdapter.enumerateEnrollments();
+
+ verify(mSession).enumerate();
+ }
+
+ @Test
+ public void testRemoveEnrollment() throws RemoteException {
+ final int[] enrollments = new int[]{1};
+ mAidlToHidlAdapter.removeEnrollments(enrollments);
+
+ verify(mSession).remove(enrollments[0]);
+ }
+
+ @Test
+ public void testGetFeatures_onResultSuccess() throws RemoteException {
+ final OptionalBool result = new OptionalBool();
+ result.status = Status.OK;
+ result.value = true;
+ ArgumentCaptor<byte[]> featureRetrieved = ArgumentCaptor.forClass(byte[].class);
+
+ when(mSession.getFeature(eq(mFeature), anyInt())).thenReturn(result);
+
+ mAidlToHidlAdapter.setFeature(mFeature);
+ mAidlToHidlAdapter.getFeatures();
+
+ verify(mSession).getFeature(eq(mFeature), anyInt());
+ verify(mAidlResponseHandler).onFeaturesRetrieved(featureRetrieved.capture());
+ assertThat(featureRetrieved.getValue()[0]).isEqualTo(
+ AidlConversionUtils.convertFrameworkToAidlFeature(mFeature));
+ }
+
+ @Test
+ public void testGetFeatures_onResultFailed() throws RemoteException {
+ final OptionalBool result = new OptionalBool();
+ result.status = Status.OK;
+ result.value = false;
+ ArgumentCaptor<byte[]> featureRetrieved = ArgumentCaptor.forClass(byte[].class);
+
+ when(mSession.getFeature(eq(mFeature), anyInt())).thenReturn(result);
+
+ mAidlToHidlAdapter.setFeature(mFeature);
+ mAidlToHidlAdapter.getFeatures();
+
+ verify(mSession).getFeature(eq(mFeature), anyInt());
+ verify(mAidlResponseHandler).onFeaturesRetrieved(featureRetrieved.capture());
+ assertThat(featureRetrieved.getValue().length).isEqualTo(0);
+ }
+
+ @Test
+ public void testGetFeatures_onStatusFailed() throws RemoteException {
+ final OptionalBool result = new OptionalBool();
+ result.status = Status.INTERNAL_ERROR;
+ result.value = false;
+
+ when(mSession.getFeature(eq(mFeature), anyInt())).thenReturn(result);
+
+ mAidlToHidlAdapter.setFeature(mFeature);
+ mAidlToHidlAdapter.getFeatures();
+
+ verify(mSession).getFeature(eq(mFeature), anyInt());
+ verify(mAidlResponseHandler, never()).onFeaturesRetrieved(any());
+ verify(mAidlResponseHandler).onError(BiometricFaceConstants.FACE_ERROR_UNKNOWN, 0);
+ }
+
+ @Test
+ public void testGetFeatures_featureNotSet() throws RemoteException {
+ mAidlToHidlAdapter.getFeatures();
+
+ verify(mSession, never()).getFeature(eq(mFeature), anyInt());
+ verify(mAidlResponseHandler, never()).onFeaturesRetrieved(any());
+ }
+
+ @Test
+ public void testSetFeatureSuccessful() throws RemoteException {
+ byte feature = Feature.REQUIRE_ATTENTION;
+ boolean enabled = true;
+
+ when(mSession.setFeature(anyInt(), anyBoolean(), any(), anyInt())).thenReturn(Status.OK);
+
+ mAidlToHidlAdapter.setFeature(mHardwareAuthToken, feature, enabled);
+
+ verify(mAidlResponseHandler).onFeatureSet(feature);
+ }
+
+ @Test
+ public void testSetFeatureFailed() throws RemoteException {
+ byte feature = Feature.REQUIRE_ATTENTION;
+ boolean enabled = true;
+
+ when(mSession.setFeature(anyInt(), anyBoolean(), any(), anyInt()))
+ .thenReturn(Status.INTERNAL_ERROR);
+
+ mAidlToHidlAdapter.setFeature(mHardwareAuthToken, feature, enabled);
+
+ verify(mAidlResponseHandler).onError(BiometricFaceConstants.FACE_ERROR_UNKNOWN,
+ 0 /* vendorCode */);
+ }
+
+ @Test
+ public void testGetAuthenticatorId() throws RemoteException {
+ final long authenticatorId = 2L;
+ final OptionalUint64 result = new OptionalUint64();
+ result.status = Status.OK;
+ result.value = authenticatorId;
+
+ when(mSession.getAuthenticatorId()).thenReturn(result);
+
+ mAidlToHidlAdapter.getAuthenticatorId();
+
+ verify(mSession).getAuthenticatorId();
+ verify(mAidlResponseHandler).onAuthenticatorIdRetrieved(authenticatorId);
+ }
+
+ @Test
+ public void testResetLockout() throws RemoteException {
+ mAidlToHidlAdapter.resetLockout(mHardwareAuthToken);
+
+ ArgumentCaptor<ArrayList> hatCaptor = ArgumentCaptor.forClass(ArrayList.class);
+
+ verify(mSession).resetLockout(hatCaptor.capture());
+
+ assertThat(hatCaptor.getValue()).containsExactlyElementsIn(processHAT(mHardwareAuthToken));
+ }
+
+ private ArrayList<Byte> processHAT(HardwareAuthToken hat) {
+ ArrayList<Byte> hardwareAuthToken = new ArrayList<>();
+ for (byte b : HardwareAuthTokenUtils.toByteArray(hat)) {
+ hardwareAuthToken.add(b);
+ }
+ return hardwareAuthToken;
+ }
+
+ private void forwardTime(long seconds) {
+ when(mClock.millis()).thenReturn(seconds * 1000);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
index 8a11e31..79a528c 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
@@ -53,11 +53,15 @@
import android.os.RemoteException;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.testing.TestableContext;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.server.biometrics.Flags;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.log.CallbackWithProbe;
@@ -66,6 +70,7 @@
import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.LockoutTracker;
import org.junit.Before;
import org.junit.Rule;
@@ -102,6 +107,9 @@
InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
@Rule
public final MockitoRule mockito = MockitoJUnit.rule();
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
@Mock
private ISession mHal;
@@ -124,7 +132,7 @@
@Mock
private ClientMonitorCallback mCallback;
@Mock
- private Sensor.HalSessionCallback mHalSessionCallback;
+ private AidlResponseHandler mAidlResponseHandler;
@Mock
private ActivityTaskManager mActivityTaskManager;
@Mock
@@ -135,6 +143,8 @@
private AuthSessionCoordinator mAuthSessionCoordinator;
@Mock
private Clock mClock;
+ @Mock
+ private LockoutTracker mLockoutTracker;
@Captor
private ArgumentCaptor<OperationContextExt> mOperationContextCaptor;
@Captor
@@ -425,37 +435,65 @@
verify(mCallback).onClientFinished(client, true);
}
+ @Test
+ public void testLockoutTracker_authSuccess() throws RemoteException {
+ final FingerprintAuthenticationClient client = createClient(1 /* version */,
+ true /* allowBackgroundAuthentication */, mClientMonitorCallbackConverter,
+ mLockoutTracker);
+ client.start(mCallback);
+ client.onAuthenticated(new Fingerprint("friendly", 1 /* fingerId */,
+ 2 /* deviceId */), true /* authenticated */, new ArrayList<>());
+
+ verify(mLockoutTracker).resetFailedAttemptsForUser(true, USER_ID);
+ verify(mLockoutTracker, never()).addFailedAttemptForUser(anyInt());
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL)
+ public void testLockoutTracker_authFailed() throws RemoteException {
+ final FingerprintAuthenticationClient client = createClient(1 /* version */,
+ true /* allowBackgroundAuthentication */, mClientMonitorCallbackConverter,
+ mLockoutTracker);
+ client.start(mCallback);
+ client.onAuthenticated(new Fingerprint("friendly", 1 /* fingerId */,
+ 2 /* deviceId */), false /* authenticated */, new ArrayList<>());
+
+ verify(mLockoutTracker, never()).resetFailedAttemptsForUser(anyBoolean(), anyInt());
+ verify(mLockoutTracker).addFailedAttemptForUser(USER_ID);
+ }
+
private FingerprintAuthenticationClient createClient() throws RemoteException {
return createClient(100 /* version */, true /* allowBackgroundAuthentication */,
- mClientMonitorCallbackConverter);
+ mClientMonitorCallbackConverter, null);
}
private FingerprintAuthenticationClient createClientWithoutBackgroundAuth()
throws RemoteException {
return createClient(100 /* version */, false /* allowBackgroundAuthentication */,
- mClientMonitorCallbackConverter);
+ mClientMonitorCallbackConverter, null);
}
private FingerprintAuthenticationClient createClient(int version) throws RemoteException {
return createClient(version, true /* allowBackgroundAuthentication */,
- mClientMonitorCallbackConverter);
+ mClientMonitorCallbackConverter, null);
}
private FingerprintAuthenticationClient createClientWithNullListener() throws RemoteException {
return createClient(100 /* version */, true /* allowBackgroundAuthentication */,
- null /* listener */);
+ null, /* listener */null);
}
private FingerprintAuthenticationClient createClient(int version,
- boolean allowBackgroundAuthentication, ClientMonitorCallbackConverter listener)
+ boolean allowBackgroundAuthentication, ClientMonitorCallbackConverter listener,
+ LockoutTracker lockoutTracker)
throws RemoteException {
when(mHal.getInterfaceVersion()).thenReturn(version);
- final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
+ final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mAidlResponseHandler);
final FingerprintAuthenticateOptions options = new FingerprintAuthenticateOptions.Builder()
.setOpPackageName("test-owner")
- .setUserId(5)
- .setSensorId(9)
+ .setUserId(USER_ID)
+ .setSensorId(SENSOR_ID)
.build();
return new FingerprintAuthenticationClient(mContext, () -> aidl, mToken,
REQUEST_ID, listener, OP_ID,
@@ -463,10 +501,11 @@
false /* requireConfirmation */,
mBiometricLogger, mBiometricContext,
true /* isStrongBiometric */,
- null /* taskStackListener */, null /* lockoutCache */,
+ null /* taskStackListener */,
mUdfpsOverlayController, mSideFpsController, allowBackgroundAuthentication,
mSensorProps,
- new Handler(mLooper.getLooper()), 0 /* biometricStrength */, mClock) {
+ new Handler(mLooper.getLooper()), 0 /* biometricStrength */, mClock,
+ lockoutTracker) {
@Override
protected ActivityTaskManager getActivityTaskManager() {
return mActivityTaskManager;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java
index 78d3a9d..a467c84 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java
@@ -83,7 +83,7 @@
@Mock
private ClientMonitorCallback mCallback;
@Mock
- private Sensor.HalSessionCallback mHalSessionCallback;
+ private AidlResponseHandler mAidlResponseHandler;
@Captor
private ArgumentCaptor<OperationContextExt> mOperationContextCaptor;
@Captor
@@ -150,7 +150,7 @@
@Test
public void testWhenListenerIsNull() {
- final AidlSession aidl = new AidlSession(0, mHal, USER_ID, mHalSessionCallback);
+ final AidlSession aidl = new AidlSession(0, mHal, USER_ID, mAidlResponseHandler);
final FingerprintDetectClient client = new FingerprintDetectClient(mContext, () -> aidl,
mToken, 6 /* requestId */, null /* listener */,
new FingerprintAuthenticateOptions.Builder()
@@ -173,7 +173,7 @@
private FingerprintDetectClient createClient(int version) throws RemoteException {
when(mHal.getInterfaceVersion()).thenReturn(version);
- final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
+ final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mAidlResponseHandler);
return new FingerprintDetectClient(mContext, () -> aidl, mToken,
6 /* requestId */, mClientMonitorCallbackConverter,
new FingerprintAuthenticateOptions.Builder()
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
index ef25380..c7eb1db 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
@@ -102,7 +102,7 @@
@Mock
private ClientMonitorCallback mCallback;
@Mock
- private Sensor.HalSessionCallback mHalSessionCallback;
+ private AidlResponseHandler mAidlResponseHandler;
@Mock
private Probe mLuxProbe;
@Captor
@@ -291,7 +291,7 @@
private FingerprintEnrollClient createClient(int version) throws RemoteException {
when(mHal.getInterfaceVersion()).thenReturn(version);
- final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
+ final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mAidlResponseHandler);
return new FingerprintEnrollClient(mContext, () -> aidl, mToken, REQUEST_ID,
mClientMonitorCallbackConverter, 0 /* userId */,
HAT, "owner", mBiometricUtils, 8 /* sensorId */,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClientTest.java
new file mode 100644
index 0000000..8409619
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClientTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.biometrics.sensors.fingerprint.aidl;
+
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@Presubmit
+@SmallTest
+public class FingerprintGenerateChallengeClientTest {
+ private static final String TAG = "FingerprintGenerateChallengeClientTest";
+ private static final int USER_ID = 2;
+ private static final int SENSOR_ID = 4;
+ private static final long CHALLENGE = 200;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private AidlSession mAidlSession;
+ @Mock
+ private ISession mSession;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private ClientMonitorCallbackConverter mListener;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ private Context mContext;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+
+ private FingerprintGenerateChallengeClient mClient;
+
+ @Before
+ public void setUp() {
+ when(mAidlSession.getSession()).thenReturn(mSession);
+
+ mClient = new FingerprintGenerateChallengeClient(mContext, () -> mAidlSession, mToken,
+ mListener, USER_ID, TAG, SENSOR_ID, mBiometricLogger, mBiometricContext);
+ }
+
+ @Test
+ public void generateChallenge() throws RemoteException {
+ doAnswer(invocation -> {
+ mClient.onChallengeGenerated(SENSOR_ID, USER_ID, CHALLENGE);
+ return null;
+ }).when(mSession).generateChallenge();
+ mClient.start(mCallback);
+
+ verify(mSession).generateChallenge();
+ verify(mListener).onChallengeGenerated(SENSOR_ID, USER_ID, CHALLENGE);
+ verify(mCallback).onClientFinished(mClient, true);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java
index 5806443..c9482ce 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java
@@ -28,6 +28,7 @@
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.fingerprint.ISession;
import android.hardware.fingerprint.Fingerprint;
+import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.testing.TestableContext;
@@ -41,7 +42,6 @@
import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
@@ -70,9 +70,9 @@
InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
@Mock
- private AidlSession mAidlSession;
+ ISession mSession;
@Mock
- private ISession mSession;
+ private AidlSession mAidlSession;
@Mock
private BiometricLogger mLogger;
@Mock
@@ -87,15 +87,16 @@
@Before
public void setup() {
- when(mAidlSession.getSession()).thenReturn(mSession);
mAddedIds = new ArrayList<>();
+
+ when(mAidlSession.getSession()).thenReturn(mSession);
}
- @Ignore("TODO(b/229015801): verify cleanup behavior")
@Test
public void removesUnknownTemplate() throws Exception {
mClient = createClient();
+ final ArgumentCaptor<int[]> captor = ArgumentCaptor.forClass(int[].class);
final List<Fingerprint> templates = List.of(
new Fingerprint("one", 1, 1),
new Fingerprint("two", 2, 1)
@@ -108,8 +109,8 @@
mClient.getCurrentRemoveClient().onRemoved(templates.get(i), 0);
}
+ verify(mSession).enumerateEnrollments();
assertThat(mAddedIds).isEmpty();
- final ArgumentCaptor<int[]> captor = ArgumentCaptor.forClass(int[].class);
verify(mSession, times(2)).removeEnrollments(captor.capture());
assertThat(captor.getAllValues().stream()
.flatMap(x -> Arrays.stream(x).boxed())
@@ -132,13 +133,15 @@
mClient.getCurrentEnumerateClient().onEnumerationResult(templates.get(i), i);
}
+ verify(mSession).enumerateEnrollments();
assertThat(mAddedIds).containsExactly(1, 2);
verify(mSession, never()).removeEnrollments(any());
verify(mCallback).onClientFinished(eq(mClient), eq(true));
}
@Test
- public void cleanupUnknownHalTemplatesAfterEnumerationWhenVirtualIsDisabled() {
+ public void cleanupUnknownHalTemplatesAfterEnumerationWhenVirtualIsDisabled()
+ throws RemoteException {
mClient = createClient();
final List<Fingerprint> templates = List.of(
@@ -150,6 +153,8 @@
for (int i = templates.size() - 1; i >= 0; i--) {
mClient.getCurrentEnumerateClient().onEnumerationResult(templates.get(i), i);
}
+
+ verify(mSession).enumerateEnrollments();
// The first template is removed after enumeration
assertThat(mClient.getUnknownHALTemplates().size()).isEqualTo(2);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClientTest.java
new file mode 100644
index 0000000..723f916
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClientTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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.biometrics.sensors.fingerprint.aidl;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.fingerprint.Fingerprint;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+@Presubmit
+@SmallTest
+public class FingerprintInternalEnumerateClientTest {
+ private static final String TAG = "FingerprintInternalEnumerateClientTest";
+ private static final int USER_ID = 2;
+ private static final int SENSOR_ID = 4;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private AidlSession mAidlSession;
+ @Mock
+ private ISession mSession;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ private Context mContext;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+ @Mock
+ private BiometricUtils<Fingerprint> mBiometricUtils;
+
+ private FingerprintInternalEnumerateClient mClient;
+
+ @Before
+ public void setUp() {
+ when(mAidlSession.getSession()).thenReturn(mSession);
+
+ List<Fingerprint> enrolled = new ArrayList<>();
+ enrolled.add(new Fingerprint("one", 1, 1));
+ mClient = new FingerprintInternalEnumerateClient(mContext, () -> mAidlSession, mToken,
+ USER_ID, TAG, enrolled, mBiometricUtils, SENSOR_ID, mBiometricLogger,
+ mBiometricContext);
+ }
+
+ @Test
+ public void internalEnumerate_unknownTemplates() throws RemoteException {
+ doAnswer(invocation -> {
+ mClient.onEnumerationResult(new Fingerprint("two", 2, 1), 1);
+ mClient.onEnumerationResult(new Fingerprint("three", 3, 1), 0);
+ return null;
+ }).when(mSession).enumerateEnrollments();
+ mClient.start(mCallback);
+
+ verify(mSession).enumerateEnrollments();
+ assertThat(mClient.getUnknownHALTemplates().stream()
+ .flatMap(x -> Stream.of(x.getBiometricId()))
+ .collect(Collectors.toList())).containsExactly(2, 3);
+ verify(mBiometricUtils).removeBiometricForUser(mContext, USER_ID, 1);
+ verify(mCallback).onClientFinished(mClient, true);
+ }
+
+ @Test
+ public void internalEnumerate_noUnknownTemplates() throws RemoteException {
+ doAnswer(invocation -> {
+ mClient.onEnumerationResult(new Fingerprint("one", 1, 1), 0);
+ return null;
+ }).when(mSession).enumerateEnrollments();
+ mClient.start(mCallback);
+
+ verify(mSession).enumerateEnrollments();
+ assertThat(mClient.getUnknownHALTemplates().size()).isEqualTo(0);
+ verify(mBiometricUtils, never()).removeBiometricForUser(any(), anyInt(), anyInt());
+ verify(mCallback).onClientFinished(mClient, true);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClientTest.java
new file mode 100644
index 0000000..64f07e2
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClientTest.java
@@ -0,0 +1,147 @@
+/*
+ * 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.biometrics.sensors.fingerprint.aidl;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.fingerprint.Fingerprint;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@Presubmit
+@SmallTest
+public class FingerprintRemovalClientTest {
+ private static final String TAG = "FingerprintRemovalClientTest";
+ private static final int USER_ID = 2;
+ private static final int SENSOR_ID = 4;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private AidlSession mAidlSession;
+ @Mock
+ private ISession mSession;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private ClientMonitorCallbackConverter mListener;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ private Context mContext;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+ @Mock
+ private BiometricUtils<Fingerprint> mBiometricUtils;
+ @Mock
+ private Map<Integer, Long> mAuthenticatorIds;
+
+ private FingerprintRemovalClient mClient;
+ private int[] mBiometricIds = new int[]{1, 2};
+
+ @Before
+ public void setUp() {
+ when(mAidlSession.getSession()).thenReturn(mSession);
+
+ mClient = new FingerprintRemovalClient(mContext, () -> mAidlSession, mToken, mListener,
+ mBiometricIds, USER_ID, TAG, mBiometricUtils, SENSOR_ID,
+ mBiometricLogger, mBiometricContext, mAuthenticatorIds);
+ }
+
+ @Test
+ public void removalMultipleFingerprints() throws RemoteException {
+ when(mBiometricUtils.getBiometricsForUser(any(), anyInt())).thenReturn(
+ List.of(new Fingerprint("three", 3, 1)));
+ doAnswer(invocation -> {
+ mClient.onRemoved(new Fingerprint("one", 1, 1), 1);
+ mClient.onRemoved(new Fingerprint("two", 2, 1), 0);
+ return null;
+ }).when(mSession).removeEnrollments(mBiometricIds);
+ mClient.start(mCallback);
+
+ verify(mSession).removeEnrollments(mBiometricIds);
+ verify(mBiometricUtils, times(2)).removeBiometricForUser(eq(mContext),
+ eq(USER_ID), anyInt());
+ verifyNoMoreInteractions(mAuthenticatorIds);
+ verify(mListener, times(2)).onRemoved(any(), anyInt());
+ verify(mCallback).onClientFinished(mClient, true);
+ }
+
+ @Test
+ public void removeFingerprint_nullIdentifier() throws RemoteException {
+ doAnswer(invocation -> {
+ mClient.onRemoved(null, 0);
+ return null;
+ }).when(mSession).removeEnrollments(mBiometricIds);
+ mClient.start(mCallback);
+
+ verify(mSession).removeEnrollments(mBiometricIds);
+ verify(mListener).onError(anyInt(), anyInt(), anyInt(), anyInt());
+ verify(mCallback).onClientFinished(mClient, false);
+ }
+
+ @Test
+ public void removeFingerprints_noFingerprintEnrolled() throws RemoteException {
+ doAnswer(invocation -> {
+ mClient.onRemoved(new Fingerprint("one", 1, 1), 1);
+ mClient.onRemoved(new Fingerprint("two", 2, 1), 0);
+ return null;
+ }).when(mSession).removeEnrollments(mBiometricIds);
+ when(mBiometricUtils.getBiometricsForUser(any(), anyInt())).thenReturn(new ArrayList<>());
+
+ mClient.start(mCallback);
+
+ verify(mSession).removeEnrollments(mBiometricIds);
+ verify(mBiometricUtils, times(2)).removeBiometricForUser(eq(mContext),
+ eq(USER_ID), anyInt());
+ verify(mAuthenticatorIds).put(USER_ID, 0L);
+ verify(mListener, times(2)).onRemoved(any(), anyInt());
+ verify(mCallback).onClientFinished(mClient, true);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClientTest.java
new file mode 100644
index 0000000..a4746de
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClientTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.biometrics.sensors.fingerprint.aidl;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.AuthSessionCoordinator;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.LockoutTracker;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@Presubmit
+@SmallTest
+public class FingerprintResetLockoutClientTest {
+ private static final String TAG = "FingerprintResetLockoutClientTest";
+ private static final int USER_ID = 2;
+ private static final int SENSOR_ID = 4;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private AidlSession mAidlSession;
+ @Mock
+ private ISession mSession;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ private Context mContext;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+ @Mock
+ private LockoutTracker mLockoutTracker;
+ @Mock
+ private LockoutResetDispatcher mLockoutResetDispatcher;
+ @Mock
+ private AuthSessionCoordinator mAuthSessionCoordinator;
+
+ private FingerprintResetLockoutClient mClient;
+
+ @Before
+ public void setUp() {
+ when(mBiometricContext.getAuthSessionCoordinator()).thenReturn(mAuthSessionCoordinator);
+ when(mAidlSession.getSession()).thenReturn(mSession);
+
+ mClient = new FingerprintResetLockoutClient(mContext, () -> mAidlSession, USER_ID, TAG,
+ SENSOR_ID, mBiometricLogger, mBiometricContext, new byte[69],
+ mLockoutTracker, mLockoutResetDispatcher,
+ BiometricManager.Authenticators.BIOMETRIC_STRONG);
+ }
+
+ @Test
+ public void resetLockout_onLockoutCleared() throws RemoteException {
+ doAnswer(invocation -> {
+ mClient.onLockoutCleared();
+ return null;
+ }).when(mSession).resetLockout(any());
+ mClient.start(mCallback);
+
+ verify(mSession).resetLockout(any());
+ verify(mLockoutTracker).setLockoutModeForUser(USER_ID, LockoutTracker.LOCKOUT_NONE);
+ verify(mLockoutTracker).resetFailedAttemptsForUser(true, USER_ID);
+ verify(mLockoutResetDispatcher).notifyLockoutResetCallbacks(SENSOR_ID);
+ verify(mAuthSessionCoordinator).resetLockoutFor(eq(USER_ID),
+ eq(BiometricManager.Authenticators.BIOMETRIC_STRONG), anyLong());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClientTest.java
new file mode 100644
index 0000000..f19b2f7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClientTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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.biometrics.sensors.fingerprint.aidl;
+
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@Presubmit
+@SmallTest
+public class FingerprintRevokeChallengeClientTest {
+ private static final String TAG = "FingerprintRevokeChallengeClientTest";
+ private static final int USER_ID = 2;
+ private static final int SENSOR_ID = 4;
+ private static final long CHALLENGE = 200;
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private AidlSession mAidlSession;
+ @Mock
+ private ISession mSession;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private ClientMonitorCallback mCallback;
+ @Mock
+ private Context mContext;
+ @Mock
+ private BiometricLogger mBiometricLogger;
+ @Mock
+ private BiometricContext mBiometricContext;
+
+ private FingerprintRevokeChallengeClient mClient;
+
+ @Before
+ public void setUp() {
+ when(mAidlSession.getSession()).thenReturn(mSession);
+
+ mClient = new FingerprintRevokeChallengeClient(mContext, () -> mAidlSession, mToken,
+ USER_ID, TAG, SENSOR_ID, mBiometricLogger, mBiometricContext, CHALLENGE);
+ }
+
+ @Test
+ public void revokeChallenge_sameChallenge() throws RemoteException {
+ doAnswer(invocation -> {
+ mClient.onChallengeRevoked(CHALLENGE);
+ return null;
+ }).when(mSession).revokeChallenge(CHALLENGE);
+ mClient.start(mCallback);
+
+ verify(mSession).revokeChallenge(CHALLENGE);
+ verify(mCallback).onClientFinished(mClient, true);
+ }
+
+ @Test
+ public void revokeChallenge_differentChallenge() throws RemoteException {
+ doAnswer(invocation -> {
+ mClient.onChallengeRevoked(CHALLENGE + 1);
+ return null;
+ }).when(mSession).revokeChallenge(CHALLENGE);
+ mClient.start(mCallback);
+
+ verify(mSession).revokeChallenge(CHALLENGE);
+ verify(mCallback).onClientFinished(mClient, false);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
index 15d7601..4102600 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
@@ -75,7 +75,7 @@
@Mock
private UserAwareBiometricScheduler.UserSwitchCallback mUserSwitchCallback;
@Mock
- private Sensor.HalSessionCallback.Callback mHalSessionCallback;
+ private AidlResponseHandler.HardwareUnavailableCallback mHardwareUnavailableCallback;
@Mock
private LockoutResetDispatcher mLockoutResetDispatcher;
@Mock
@@ -97,7 +97,7 @@
private final LockoutCache mLockoutCache = new LockoutCache();
private UserAwareBiometricScheduler mScheduler;
- private Sensor.HalSessionCallback mHalCallback;
+ private AidlResponseHandler mHalCallback;
@Before
public void setUp() {
@@ -113,10 +113,9 @@
mBiometricService,
() -> USER_ID,
mUserSwitchCallback);
- mHalCallback = new Sensor.HalSessionCallback(mContext, new Handler(mLooper.getLooper()),
- TAG, mScheduler, SENSOR_ID,
- USER_ID, mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator,
- mHalSessionCallback);
+ mHalCallback = new AidlResponseHandler(mContext, mScheduler, SENSOR_ID, USER_ID,
+ mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator,
+ mHardwareUnavailableCallback);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/AidlToHidlAdapterTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/AidlToHidlAdapterTest.java
new file mode 100644
index 0000000..b78ba82
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/AidlToHidlAdapterTest.java
@@ -0,0 +1,148 @@
+/*
+ * 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.biometrics.sensors.fingerprint.hidl;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.keymaster.HardwareAuthToken;
+import android.hardware.keymaster.Timestamp;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.biometrics.sensors.fingerprint.aidl.AidlResponseHandler;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@Presubmit
+@SmallTest
+public class AidlToHidlAdapterTest {
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private IBiometricsFingerprint mSession;
+ @Mock
+ private AidlResponseHandler mAidlResponseHandler;
+ @Mock
+ private HardwareAuthToken mHardwareAuthToken;
+
+ private final long mChallenge = 100L;
+ private final int mUserId = 0;
+ private AidlToHidlAdapter mAidlToHidlAdapter;
+
+ @Before
+ public void setUp() {
+ mAidlToHidlAdapter = new AidlToHidlAdapter(() -> mSession, mUserId,
+ mAidlResponseHandler);
+ mHardwareAuthToken.timestamp = new Timestamp();
+ mHardwareAuthToken.mac = new byte[10];
+ }
+
+ @Test
+ public void testGenerateChallenge() throws RemoteException {
+ when(mSession.preEnroll()).thenReturn(mChallenge);
+ mAidlToHidlAdapter.generateChallenge();
+
+ verify(mSession).preEnroll();
+ verify(mAidlResponseHandler).onChallengeGenerated(mChallenge);
+ }
+
+ @Test
+ public void testRevokeChallenge() throws RemoteException {
+ mAidlToHidlAdapter.revokeChallenge(mChallenge);
+
+ verify(mSession).postEnroll();
+ verify(mAidlResponseHandler).onChallengeRevoked(0L);
+ }
+
+ @Test
+ public void testEnroll() throws RemoteException {
+ final ICancellationSignal cancellationSignal =
+ mAidlToHidlAdapter.enroll(mHardwareAuthToken);
+
+ verify(mSession).enroll(any(), anyInt(), eq(AidlToHidlAdapter.ENROLL_TIMEOUT_SEC));
+
+ cancellationSignal.cancel();
+
+ verify(mSession).cancel();
+ }
+
+ @Test
+ public void testAuthenticate() throws RemoteException {
+ final int operationId = 2;
+ final ICancellationSignal cancellationSignal = mAidlToHidlAdapter.authenticate(operationId);
+
+ verify(mSession).authenticate(operationId, mUserId);
+
+ cancellationSignal.cancel();
+
+ verify(mSession).cancel();
+ }
+
+ @Test
+ public void testDetectInteraction() throws RemoteException {
+ final ICancellationSignal cancellationSignal = mAidlToHidlAdapter.detectInteraction();
+
+ verify(mSession).authenticate(0 /* operationId */, mUserId);
+
+ cancellationSignal.cancel();
+
+ verify(mSession).cancel();
+ }
+
+ @Test
+ public void testEnumerateEnrollments() throws RemoteException {
+ mAidlToHidlAdapter.enumerateEnrollments();
+
+ verify(mSession).enumerate();
+ }
+
+ @Test
+ public void testRemoveEnrollment() throws RemoteException {
+ final int[] enrollmentIds = new int[]{1};
+ mAidlToHidlAdapter.removeEnrollments(enrollmentIds);
+
+ verify(mSession).remove(mUserId, enrollmentIds[0]);
+ }
+
+ @Test
+ public void testRemoveMultipleEnrollments() throws RemoteException {
+ final int[] enrollmentIds = new int[]{1, 2};
+ mAidlToHidlAdapter.removeEnrollments(enrollmentIds);
+
+ verify(mSession).remove(mUserId, 0);
+ }
+
+ @Test
+ public void testResetLockout() throws RemoteException {
+ mAidlToHidlAdapter.resetLockout(mHardwareAuthToken);
+
+ verify(mAidlResponseHandler).onLockoutCleared();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index 61b30a0..e8cbcf9 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -233,6 +233,7 @@
private VirtualDeviceManagerService mVdms;
private VirtualDeviceManagerInternal mLocalService;
private VirtualDeviceManagerService.VirtualDeviceManagerImpl mVdm;
+ private VirtualDeviceManagerService.VirtualDeviceManagerNativeImpl mVdmNative;
private VirtualDeviceLog mVirtualDeviceLog;
@Mock
private InputController.NativeWrapper mNativeWrapperMock;
@@ -340,6 +341,7 @@
mSetFlagsRule.disableFlags(Flags.FLAG_DYNAMIC_POLICY);
mSetFlagsRule.disableFlags(Flags.FLAG_STREAM_PERMISSIONS);
mSetFlagsRule.disableFlags(Flags.FLAG_VDM_CUSTOM_HOME);
+ mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_NATIVE_VDM);
doReturn(true).when(mInputManagerInternalMock).setVirtualMousePointerDisplayId(anyInt());
doNothing().when(mInputManagerInternalMock).setPointerAcceleration(anyFloat(), anyInt());
@@ -384,6 +386,7 @@
mVdms = new VirtualDeviceManagerService(mContext);
mLocalService = mVdms.getLocalServiceInstance();
mVdm = mVdms.new VirtualDeviceManagerImpl();
+ mVdmNative = mVdms.new VirtualDeviceManagerNativeImpl();
mVirtualDeviceLog = new VirtualDeviceLog(mContext);
mDeviceImpl = createVirtualDevice(VIRTUAL_DEVICE_ID_1, DEVICE_OWNER_UID_1);
mSensorController = mDeviceImpl.getSensorControllerForTest();
@@ -440,24 +443,32 @@
public void getDevicePolicy_invalidDeviceId_returnsDefault() {
assertThat(mVdm.getDevicePolicy(DEVICE_ID_INVALID, POLICY_TYPE_SENSORS))
.isEqualTo(DEVICE_POLICY_DEFAULT);
+ assertThat(mVdmNative.getDevicePolicy(DEVICE_ID_INVALID, POLICY_TYPE_SENSORS))
+ .isEqualTo(DEVICE_POLICY_DEFAULT);
}
@Test
public void getDevicePolicy_defaultDeviceId_returnsDefault() {
assertThat(mVdm.getDevicePolicy(DEVICE_ID_DEFAULT, POLICY_TYPE_SENSORS))
.isEqualTo(DEVICE_POLICY_DEFAULT);
+ assertThat(mVdmNative.getDevicePolicy(DEVICE_ID_DEFAULT, POLICY_TYPE_SENSORS))
+ .isEqualTo(DEVICE_POLICY_DEFAULT);
}
@Test
public void getDevicePolicy_nonExistentDeviceId_returnsDefault() {
assertThat(mVdm.getDevicePolicy(mDeviceImpl.getDeviceId() + 1, POLICY_TYPE_SENSORS))
.isEqualTo(DEVICE_POLICY_DEFAULT);
+ assertThat(mVdmNative.getDevicePolicy(mDeviceImpl.getDeviceId() + 1, POLICY_TYPE_SENSORS))
+ .isEqualTo(DEVICE_POLICY_DEFAULT);
}
@Test
public void getDevicePolicy_unspecifiedPolicy_returnsDefault() {
assertThat(mVdm.getDevicePolicy(mDeviceImpl.getDeviceId(), POLICY_TYPE_SENSORS))
.isEqualTo(DEVICE_POLICY_DEFAULT);
+ assertThat(mVdmNative.getDevicePolicy(mDeviceImpl.getDeviceId(), POLICY_TYPE_SENSORS))
+ .isEqualTo(DEVICE_POLICY_DEFAULT);
}
@Test
@@ -472,6 +483,8 @@
assertThat(mVdm.getDevicePolicy(mDeviceImpl.getDeviceId(), POLICY_TYPE_SENSORS))
.isEqualTo(DEVICE_POLICY_CUSTOM);
+ assertThat(mVdmNative.getDevicePolicy(mDeviceImpl.getDeviceId(), POLICY_TYPE_SENSORS))
+ .isEqualTo(DEVICE_POLICY_CUSTOM);
}
@Test
@@ -567,8 +580,8 @@
@Test
public void getDeviceIdsForUid_noRunningApps_returnsNull() {
- Set<Integer> deviceIds = mLocalService.getDeviceIdsForUid(UID_1);
- assertThat(deviceIds).isEmpty();
+ assertThat(mLocalService.getDeviceIdsForUid(UID_1)).isEmpty();
+ assertThat(mVdmNative.getDeviceIdsForUid(UID_1)).isEmpty();
}
@Test
@@ -577,8 +590,8 @@
mDeviceImpl.getDisplayWindowPolicyControllerForTest(DISPLAY_ID_1).onRunningAppsChanged(
Sets.newArraySet(UID_2));
- Set<Integer> deviceIds = mLocalService.getDeviceIdsForUid(UID_1);
- assertThat(deviceIds).isEmpty();
+ assertThat(mLocalService.getDeviceIdsForUid(UID_1)).isEmpty();
+ assertThat(mVdmNative.getDeviceIdsForUid(UID_1)).isEmpty();
}
@Test
@@ -587,8 +600,9 @@
mDeviceImpl.getDisplayWindowPolicyControllerForTest(DISPLAY_ID_1).onRunningAppsChanged(
Sets.newArraySet(UID_1));
- Set<Integer> deviceIds = mLocalService.getDeviceIdsForUid(UID_1);
- assertThat(deviceIds).containsExactly(mDeviceImpl.getDeviceId());
+ int deviceId = mDeviceImpl.getDeviceId();
+ assertThat(mLocalService.getDeviceIdsForUid(UID_1)).containsExactly(deviceId);
+ assertThat(mVdmNative.getDeviceIdsForUid(UID_1)).asList().containsExactly(deviceId);
}
@Test
@@ -598,8 +612,9 @@
mDeviceImpl.getDisplayWindowPolicyControllerForTest(DISPLAY_ID_1).onRunningAppsChanged(
Sets.newArraySet(UID_1, UID_2));
- Set<Integer> deviceIds = mLocalService.getDeviceIdsForUid(UID_1);
- assertThat(deviceIds).containsExactly(mDeviceImpl.getDeviceId());
+ int deviceId = mDeviceImpl.getDeviceId();
+ assertThat(mLocalService.getDeviceIdsForUid(UID_1)).containsExactly(deviceId);
+ assertThat(mVdmNative.getDeviceIdsForUid(UID_1)).asList().containsExactly(deviceId);
}
@Test
@@ -611,8 +626,9 @@
secondDevice.getDisplayWindowPolicyControllerForTest(DISPLAY_ID_2).onRunningAppsChanged(
Sets.newArraySet(UID_1));
- Set<Integer> deviceIds = mLocalService.getDeviceIdsForUid(UID_1);
- assertThat(deviceIds).containsExactly(secondDevice.getDeviceId());
+ int deviceId = secondDevice.getDeviceId();
+ assertThat(mLocalService.getDeviceIdsForUid(UID_1)).containsExactly(deviceId);
+ assertThat(mVdmNative.getDeviceIdsForUid(UID_1)).asList().containsExactly(deviceId);
}
@Test
@@ -628,8 +644,9 @@
secondDevice.getDisplayWindowPolicyControllerForTest(DISPLAY_ID_2).onRunningAppsChanged(
Sets.newArraySet(UID_1, UID_2));
- Set<Integer> deviceIds = mLocalService.getDeviceIdsForUid(UID_1);
- assertThat(deviceIds).containsExactly(
+ assertThat(mLocalService.getDeviceIdsForUid(UID_1)).containsExactly(
+ mDeviceImpl.getDeviceId(), secondDevice.getDeviceId());
+ assertThat(mVdmNative.getDeviceIdsForUid(UID_1)).asList().containsExactly(
mDeviceImpl.getDeviceId(), secondDevice.getDeviceId());
}
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..12d6161 100644
--- a/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
@@ -16,6 +16,8 @@
package com.android.server.contentcapture;
+import static android.view.contentprotection.flags.Flags.FLAG_PARSE_GROUPS_CONFIG_ENABLED;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -33,6 +35,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.service.contentcapture.ContentCaptureServiceInfo;
import android.view.contentcapture.ContentCaptureEvent;
@@ -56,6 +59,8 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.util.List;
+
/**
* Test for {@link ContentCaptureManagerService}.
*
@@ -84,6 +89,8 @@
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Mock private UserManagerInternal mMockUserManagerInternal;
@Mock private ContentProtectionBlocklistManager mMockContentProtectionBlocklistManager;
@@ -436,6 +443,82 @@
verify(mMockRemoteContentProtectionService).onLoginDetected(PARCELED_EVENTS);
}
+ @Test
+ public void parseContentProtectionGroupsConfig_disabled_null() {
+ mSetFlagsRule.disableFlags(FLAG_PARSE_GROUPS_CONFIG_ENABLED);
+ ContentCaptureManagerService service = new ContentCaptureManagerService(sContext);
+
+ assertThat(service.parseContentProtectionGroupsConfig(null)).isEmpty();
+ }
+
+ @Test
+ public void parseContentProtectionGroupsConfig_disabled_empty() {
+ mSetFlagsRule.disableFlags(FLAG_PARSE_GROUPS_CONFIG_ENABLED);
+ ContentCaptureManagerService service = new ContentCaptureManagerService(sContext);
+
+ assertThat(service.parseContentProtectionGroupsConfig("")).isEmpty();
+ }
+
+ @Test
+ public void parseContentProtectionGroupsConfig_disabled_notEmpty() {
+ mSetFlagsRule.disableFlags(FLAG_PARSE_GROUPS_CONFIG_ENABLED);
+ ContentCaptureManagerService service = new ContentCaptureManagerService(sContext);
+
+ assertThat(service.parseContentProtectionGroupsConfig("a")).isEmpty();
+ }
+
+ @Test
+ public void parseContentProtectionGroupsConfig_enabled_null() {
+ mSetFlagsRule.enableFlags(FLAG_PARSE_GROUPS_CONFIG_ENABLED);
+ ContentCaptureManagerService service = new ContentCaptureManagerService(sContext);
+
+ assertThat(service.parseContentProtectionGroupsConfig(null)).isEmpty();
+ }
+
+ @Test
+ public void parseContentProtectionGroupsConfig_enabled_empty() {
+ mSetFlagsRule.enableFlags(FLAG_PARSE_GROUPS_CONFIG_ENABLED);
+ ContentCaptureManagerService service = new ContentCaptureManagerService(sContext);
+
+ assertThat(service.parseContentProtectionGroupsConfig("")).isEmpty();
+ }
+
+ @Test
+ public void parseContentProtectionGroupsConfig_enabled_singleValue() {
+ mSetFlagsRule.enableFlags(FLAG_PARSE_GROUPS_CONFIG_ENABLED);
+ ContentCaptureManagerService service = new ContentCaptureManagerService(sContext);
+
+ assertThat(service.parseContentProtectionGroupsConfig("a"))
+ .isEqualTo(List.of(List.of("a")));
+ }
+
+ @Test
+ public void parseContentProtectionGroupsConfig_enabled_multipleValues() {
+ mSetFlagsRule.enableFlags(FLAG_PARSE_GROUPS_CONFIG_ENABLED);
+ ContentCaptureManagerService service = new ContentCaptureManagerService(sContext);
+
+ assertThat(service.parseContentProtectionGroupsConfig("a,b"))
+ .isEqualTo(List.of(List.of("a", "b")));
+ }
+
+ @Test
+ public void parseContentProtectionGroupsConfig_enabled_multipleGroups() {
+ mSetFlagsRule.enableFlags(FLAG_PARSE_GROUPS_CONFIG_ENABLED);
+ ContentCaptureManagerService service = new ContentCaptureManagerService(sContext);
+
+ assertThat(service.parseContentProtectionGroupsConfig("a,b;c;d,e"))
+ .isEqualTo(List.of(List.of("a", "b"), List.of("c"), List.of("d", "e")));
+ }
+
+ @Test
+ public void parseContentProtectionGroupsConfig_enabled_emptyValues() {
+ mSetFlagsRule.enableFlags(FLAG_PARSE_GROUPS_CONFIG_ENABLED);
+ ContentCaptureManagerService service = new ContentCaptureManagerService(sContext);
+
+ assertThat(service.parseContentProtectionGroupsConfig("a;b;;;c;,d,,e,;,;"))
+ .isEqualTo(List.of(List.of("a"), List.of("b"), List.of("c"), List.of("d", "e")));
+ }
+
private class TestContentCaptureManagerService extends ContentCaptureManagerService {
TestContentCaptureManagerService() {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index 9b32a80..de3cfbf 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -54,8 +54,8 @@
import com.android.internal.widget.LockSettingsInternal;
import com.android.server.AlarmManagerInternal;
import com.android.server.LocalServices;
-import com.android.server.PersistentDataBlockManagerInternal;
import com.android.server.net.NetworkPolicyManagerInternal;
+import com.android.server.pdb.PersistentDataBlockManagerInternal;
import com.android.server.pm.PackageManagerLocal;
import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index 16fdfb1..76aa40c 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -77,7 +77,7 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockSettingsInternal;
import com.android.server.AlarmManagerInternal;
-import com.android.server.PersistentDataBlockManagerInternal;
+import com.android.server.pdb.PersistentDataBlockManagerInternal;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.pm.PackageManagerLocal;
import com.android.server.pm.UserManagerInternal;
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/graphics/fonts/UpdatableFontDirTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
index 184c976..3de167e 100644
--- a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
+++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
@@ -336,7 +336,8 @@
return new FontConfig(Collections.emptyList(),
Collections.emptyList(),
Collections.singletonList(new FontConfig.NamedFamilyList(
- Collections.singletonList(family), "sans-serif")), 0, 1);
+ Collections.singletonList(family), "sans-serif")),
+ Collections.emptyList(), 0, 1);
};
UpdatableFontDir dirForPreparation = new UpdatableFontDir(
@@ -499,7 +500,8 @@
Collections.emptyList(),
Collections.emptyList(),
Collections.singletonList(new FontConfig.NamedFamilyList(
- Collections.singletonList(family), "sans-serif")), 0, 1);
+ Collections.singletonList(family), "sans-serif")),
+ Collections.emptyList(), 0, 1);
});
dir.loadFontFileMap();
@@ -651,7 +653,8 @@
FontFamily.Builder.VARIABLE_FONT_FAMILY_TYPE_NONE);
return new FontConfig(Collections.emptyList(), Collections.emptyList(),
Collections.singletonList(new FontConfig.NamedFamilyList(
- Collections.singletonList(family), "sans-serif")), 0, 1);
+ Collections.singletonList(family), "sans-serif")),
+ Collections.emptyList(), 0, 1);
});
dir.loadFontFileMap();
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
index a029db9..fa3c7a4c 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
@@ -22,7 +22,7 @@
import android.content.Context;
-import com.android.server.PersistentDataBlockManagerInternal;
+import com.android.server.pdb.PersistentDataBlockManagerInternal;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
index 23f14f8..02b86db 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
@@ -53,8 +53,8 @@
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.util.test.FakeSettingsProviderRule;
-import com.android.server.PersistentDataBlockManagerInternal;
import com.android.server.locksettings.LockSettingsStorage.PersistentData;
+import com.android.server.pdb.PersistentDataBlockManagerInternal;
import org.junit.After;
import org.junit.Before;
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
index d85768d..f94aff7 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
@@ -26,6 +26,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CAPTURING_IN_PROGRESS;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -66,6 +67,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
import com.android.server.testutils.OffsettableClock;
import com.android.server.wm.WindowManagerInternal;
@@ -128,6 +130,14 @@
}
};
+ private final MediaProjectionManagerService.Injector mMediaProjectionMetricsLoggerInjector =
+ new MediaProjectionManagerService.Injector() {
+ @Override
+ MediaProjectionMetricsLogger mediaProjectionMetricsLogger() {
+ return mMediaProjectionMetricsLogger;
+ }
+ };
+
private Context mContext;
private MediaProjectionManagerService mService;
private OffsettableClock mClock;
@@ -142,6 +152,8 @@
private PackageManager mPackageManager;
@Mock
private IMediaProjectionWatcherCallback mWatcherCallback;
+ @Mock
+ private MediaProjectionMetricsLogger mMediaProjectionMetricsLogger;
@Captor
private ArgumentCaptor<ContentRecordingSession> mSessionCaptor;
@@ -734,6 +746,25 @@
}
@Test
+ public void setContentRecordingSession_success_logsCaptureInProgress()
+ throws Exception {
+ mService.addCallback(mWatcherCallback);
+ MediaProjectionManagerService service = new MediaProjectionManagerService(mContext, mMediaProjectionMetricsLoggerInjector);
+ MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
+ projection.start(mIMediaProjectionCallback);
+ doReturn(true).when(mWindowManagerInternal).setContentRecordingSession(
+ any(ContentRecordingSession.class));
+
+ service.setContentRecordingSession(DISPLAY_SESSION);
+
+ verify(mMediaProjectionMetricsLogger).notifyProjectionStateChange(
+ projection.uid,
+ MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CAPTURING_IN_PROGRESS,
+ FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN
+ );
+ }
+
+ @Test
public void setContentRecordingSession_notifiesListenersOnCallbackLooper()
throws Exception {
mService = new MediaProjectionManagerService(mContext, mTestLooperInjector);
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/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
index 9f75cf8..253592c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
@@ -43,6 +43,7 @@
import android.app.PropertyInvalidatedCache;
import android.content.pm.UserInfo;
import android.content.pm.UserInfo.UserInfoFlag;
+import android.multiuser.Flags;
import android.os.Looper;
import android.os.Parcel;
import android.os.UserHandle;
@@ -124,18 +125,34 @@
mUserManagerService.putUserInfo(data.info);
- // Set a global and user restriction so they get written out to the user file.
+ //Local restrictions are written to the user specific files and global restrictions
+ // are written to the SYSTEM user file.
setUserRestrictions(data.info.id, globalRestriction, localRestriction, true);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(baos);
mUserManagerService.writeUserLP(data, out);
- byte[] bytes = baos.toByteArray();
+ byte[] secondaryUserBytes = baos.toByteArray();
+ baos.reset();
+
+ byte[] systemUserBytes = new byte[0];
+ if (Flags.saveGlobalAndGuestRestrictionsOnSystemUserXmlReadOnly()) {
+ UserData systemUserData = new UserData();
+ systemUserData.info = mUserManagerService.getUserInfo(UserHandle.USER_SYSTEM);
+ mUserManagerService.writeUserLP(systemUserData, baos);
+ systemUserBytes = baos.toByteArray();
+ }
// Clear the restrictions to see if they are properly read in from the user file.
setUserRestrictions(data.info.id, globalRestriction, localRestriction, false);
- mUserManagerService.readUserLP(data.info.id, new ByteArrayInputStream(bytes));
+ //read the secondary and SYSTEM user file to fetch local/global device policy restrictions.
+ mUserManagerService.readUserLP(data.info.id, new ByteArrayInputStream(secondaryUserBytes));
+ if (Flags.saveGlobalAndGuestRestrictionsOnSystemUserXmlReadOnly()) {
+ mUserManagerService.readUserLP(UserHandle.USER_SYSTEM,
+ new ByteArrayInputStream(systemUserBytes));
+ }
+
assertTrue(mUserManagerService.hasUserRestrictionOnAnyUser(globalRestriction));
assertTrue(mUserManagerService.hasUserRestrictionOnAnyUser(localRestriction));
}
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 1d37f9d..8891413 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -39,10 +39,12 @@
"hamcrest-library",
"servicestests-utils",
"testables",
- "truth-prebuilt",
+ "truth",
// 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/NotificationBitmapJobServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationBitmapJobServiceTest.java
index 312057ee..348d1bf 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationBitmapJobServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationBitmapJobServiceTest.java
@@ -44,6 +44,12 @@
import org.mockito.Mock;
import java.lang.reflect.Field;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.ZonedDateTime;
+import java.time.ZoneId;
@RunWith(AndroidTestingRunner.class)
public class NotificationBitmapJobServiceTest extends UiServiceTestCase {
@@ -103,17 +109,39 @@
@Test
public void testGetTimeUntilRemoval_beforeToday2am_returnTimeUntilToday2am() {
- final long timeUntilRemoval = mJobService.getTimeUntilRemoval(/* now= */ 1,
- /* today2AM= */ 2, /* tomorrow2AM= */ 26);
+ ZoneId zoneId = ZoneId.systemDefault();
+ ZonedDateTime now = Instant.now().atZone(zoneId);
+ LocalDate today = now.toLocalDate();
- assertThat(timeUntilRemoval).isEqualTo(1);
+ LocalTime oneAM = LocalTime.of(/* hour= */ 1, /* minute= */ 0);
+ LocalTime twoAM = LocalTime.of(/* hour= */ 2, /* minute= */ 0);
+
+ ZonedDateTime today1AM = ZonedDateTime.of(today, oneAM, zoneId);
+ ZonedDateTime today2AM = ZonedDateTime.of(today, twoAM, zoneId);
+ ZonedDateTime tomorrow2AM = today2AM.plusDays(1);
+
+ final long msUntilRemoval = mJobService.getTimeUntilRemoval(
+ /* now= */ today1AM, today2AM, tomorrow2AM);
+
+ assertThat(msUntilRemoval).isEqualTo(Duration.ofHours(1).toMillis());
}
@Test
public void testGetTimeUntilRemoval_afterToday2am_returnTimeUntilTomorrow2am() {
- final long timeUntilRemoval = mJobService.getTimeUntilRemoval(/* now= */ 3,
- /* today2AM= */ 2, /* tomorrow2AM= */ 26);
+ ZoneId zoneId = ZoneId.systemDefault();
+ ZonedDateTime now = Instant.now().atZone(zoneId);
+ LocalDate today = now.toLocalDate();
- assertThat(timeUntilRemoval).isEqualTo(23);
+ LocalTime threeAM = LocalTime.of(/* hour= */ 3, /* minute= */ 0);
+ LocalTime twoAM = LocalTime.of(/* hour= */ 2, /* minute= */ 0);
+
+ ZonedDateTime today3AM = ZonedDateTime.of(today, threeAM, zoneId);
+ ZonedDateTime today2AM = ZonedDateTime.of(today, twoAM, zoneId);
+ ZonedDateTime tomorrow2AM = today2AM.plusDays(1);
+
+ final long msUntilRemoval = mJobService.getTimeUntilRemoval(/* now= */ today3AM,
+ today2AM, tomorrow2AM);
+
+ assertThat(msUntilRemoval).isEqualTo(Duration.ofHours(23).toMillis());
}
}
\ No newline at end of file
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryJobServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryJobServiceTest.java
index d758e71..3499a12 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryJobServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryJobServiceTest.java
@@ -71,10 +71,10 @@
@Before
public void setUp() throws Exception {
mJobService = new NotificationHistoryJobService();
+ mJobService.attachBaseContext(mContext);
+ mJobService.onCreate();
+ mJobService.onBind(/* intent= */ null); // Create JobServiceEngine within JobService.
- final Field field = JobService.class.getDeclaredField("mEngine");
- field.setAccessible(true);
- field.set(mJobService, mock(JobServiceEngine.class));
mContext.addMockSystemService(JobScheduler.class, mMockJobScheduler);
// add NotificationManagerInternal to LocalServices
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index 7a55143..53ca704 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -23,9 +23,7 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
@@ -49,12 +47,10 @@
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
-import android.os.Parcel;
import android.os.RemoteException;
import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
-import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.NotificationRankingUpdate;
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
@@ -158,81 +154,6 @@
}
}
- // Tests parceling of NotificationRankingUpdate, and by extension, RankingMap and Ranking.
- @Test
- public void testRankingUpdate_parcel() {
- NotificationRankingUpdate nru = generateUpdate();
- Parcel parcel = Parcel.obtain();
- nru.writeToParcel(parcel, 0);
- parcel.setDataPosition(0);
- NotificationRankingUpdate nru1 = NotificationRankingUpdate.CREATOR.createFromParcel(parcel);
- assertEquals(nru, nru1);
- }
-
- // Tests parceling of RankingMap and RankingMap.equals
- @Test
- public void testRankingMap_parcel() {
- RankingMap rmap = generateUpdate().getRankingMap();
- Parcel parcel = Parcel.obtain();
- rmap.writeToParcel(parcel, 0);
- parcel.setDataPosition(0);
- RankingMap rmap1 = RankingMap.CREATOR.createFromParcel(parcel);
-
- detailedAssertEquals(rmap, rmap1);
- assertEquals(rmap, rmap1);
- }
-
- // Tests parceling of Ranking and Ranking.equals
- @Test
- public void testRanking_parcel() {
- Ranking ranking = generateUpdate().getRankingMap().getRawRankingObject(mKeys[0]);
- Parcel parcel = Parcel.obtain();
- ranking.writeToParcel(parcel, 0);
- parcel.setDataPosition(0);
- Ranking ranking1 = new Ranking(parcel);
- detailedAssertEquals("rankings differ: ", ranking, ranking1);
- assertEquals(ranking, ranking1);
- }
-
- // Tests NotificationRankingUpdate.equals(), and by extension, RankingMap and Ranking.
- @Test
- public void testRankingUpdate_equals() {
- NotificationRankingUpdate nru = generateUpdate();
- NotificationRankingUpdate nru2 = generateUpdate();
- detailedAssertEquals(nru, nru2);
- assertEquals(nru, nru2);
- Ranking tweak = nru2.getRankingMap().getRawRankingObject(mKeys[0]);
- tweak.populate(
- tweak.getKey(),
- tweak.getRank(),
- !tweak.matchesInterruptionFilter(), // note the inversion here!
- tweak.getLockscreenVisibilityOverride(),
- tweak.getSuppressedVisualEffects(),
- tweak.getImportance(),
- tweak.getImportanceExplanation(),
- tweak.getOverrideGroupKey(),
- tweak.getChannel(),
- (ArrayList) tweak.getAdditionalPeople(),
- (ArrayList) tweak.getSnoozeCriteria(),
- tweak.canShowBadge(),
- tweak.getUserSentiment(),
- tweak.isSuspended(),
- tweak.getLastAudiblyAlertedMillis(),
- tweak.isNoisy(),
- (ArrayList) tweak.getSmartActions(),
- (ArrayList) tweak.getSmartReplies(),
- tweak.canBubble(),
- tweak.isTextChanged(),
- tweak.isConversation(),
- tweak.getConversationShortcutInfo(),
- tweak.getRankingAdjustment(),
- tweak.isBubble(),
- tweak.getProposedImportance(),
- tweak.hasSensitiveContent()
- );
- assertNotEquals(nru, nru2);
- }
-
@Test
public void testLegacyIcons_preM() {
TestListenerService service = new TestListenerService();
@@ -275,7 +196,6 @@
assertNull(n.largeIcon);
}
-
// Test data
private String[] mKeys = new String[] { "key", "key1", "key2", "key3", "key4"};
@@ -461,48 +381,6 @@
}
}
- private void detailedAssertEquals(NotificationRankingUpdate a, NotificationRankingUpdate b) {
- assertEquals(a.getRankingMap(), b.getRankingMap());
- }
-
- private void detailedAssertEquals(String comment, Ranking a, Ranking b) {
- assertEquals(comment, a.getKey(), b.getKey());
- assertEquals(comment, a.getRank(), b.getRank());
- assertEquals(comment, a.matchesInterruptionFilter(), b.matchesInterruptionFilter());
- assertEquals(comment, a.getLockscreenVisibilityOverride(), b.getLockscreenVisibilityOverride());
- assertEquals(comment, a.getSuppressedVisualEffects(), b.getSuppressedVisualEffects());
- assertEquals(comment, a.getImportance(), b.getImportance());
- assertEquals(comment, a.getImportanceExplanation(), b.getImportanceExplanation());
- assertEquals(comment, a.getOverrideGroupKey(), b.getOverrideGroupKey());
- assertEquals(comment, a.getChannel(), b.getChannel());
- assertEquals(comment, a.getAdditionalPeople(), b.getAdditionalPeople());
- assertEquals(comment, a.getSnoozeCriteria(), b.getSnoozeCriteria());
- assertEquals(comment, a.canShowBadge(), b.canShowBadge());
- assertEquals(comment, a.getUserSentiment(), b.getUserSentiment());
- assertEquals(comment, a.isSuspended(), b.isSuspended());
- assertEquals(comment, a.getLastAudiblyAlertedMillis(), b.getLastAudiblyAlertedMillis());
- assertEquals(comment, a.isNoisy(), b.isNoisy());
- assertEquals(comment, a.getSmartReplies(), b.getSmartReplies());
- assertEquals(comment, a.canBubble(), b.canBubble());
- assertEquals(comment, a.isConversation(), b.isConversation());
- assertEquals(comment, a.getConversationShortcutInfo().getId(),
- b.getConversationShortcutInfo().getId());
- assertActionsEqual(a.getSmartActions(), b.getSmartActions());
- assertEquals(a.getProposedImportance(), b.getProposedImportance());
- assertEquals(a.hasSensitiveContent(), b.hasSensitiveContent());
- }
-
- private void detailedAssertEquals(RankingMap a, RankingMap b) {
- Ranking arank = new Ranking();
- Ranking brank = new Ranking();
- assertArrayEquals(a.getOrderedKeys(), b.getOrderedKeys());
- for (String key : a.getOrderedKeys()) {
- a.getRanking(key, arank);
- b.getRanking(key, brank);
- detailedAssertEquals("ranking for key <" + key + ">", arank, brank);
- }
- }
-
public static class TestListenerService extends NotificationListenerService {
private final IBinder binder = new LocalBinder();
public int targetSdk = 0;
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 c98d235..3d4b4a6 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -91,7 +91,7 @@
import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER;
import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER;
import static com.android.server.am.PendingIntentRecord.FLAG_SERVICE_SENDER;
-import static com.android.server.notification.NotificationManagerService.BITMAP_EXPIRATION_TIME_MS;
+import static com.android.server.notification.NotificationManagerService.BITMAP_DURATION;
import static com.android.server.notification.NotificationManagerService.DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE;
import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_ADJUSTED;
import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED;
@@ -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);
@@ -11381,7 +11385,7 @@
long timePostedMs = System.currentTimeMillis();
if (isExpired) {
- timePostedMs -= BITMAP_EXPIRATION_TIME_MS;
+ timePostedMs -= BITMAP_DURATION.toMillis();
}
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
notification, UserHandle.getUserHandleForUid(mUid), null, timePostedMs);
diff --git a/services/tests/vibrator/Android.bp b/services/tests/vibrator/Android.bp
index ca5cfa5..9544106 100644
--- a/services/tests/vibrator/Android.bp
+++ b/services/tests/vibrator/Android.bp
@@ -27,6 +27,7 @@
"androidx.test.runner",
"androidx.test.rules",
"androidx.test.ext.junit",
+ "flag-junit",
"frameworks-base-testutils",
"frameworks-services-vibrator-testutils",
"junit",
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackCustomizationTest.java b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackCustomizationTest.java
index bc826a3..04158c4 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackCustomizationTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackCustomizationTest.java
@@ -31,6 +31,8 @@
import android.content.res.Resources;
import android.os.VibrationEffect;
import android.os.VibratorInfo;
+import android.os.vibrator.Flags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.util.AtomicFile;
import android.util.SparseArray;
@@ -49,6 +51,8 @@
import java.io.FileOutputStream;
public class HapticFeedbackCustomizationTest {
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Rule public MockitoRule rule = MockitoJUnit.rule();
// Pairs of valid vibration XML along with their equivalent VibrationEffect.
@@ -77,6 +81,7 @@
@Before
public void setUp() {
when(mVibratorInfoMock.areVibrationFeaturesSupported(any())).thenReturn(true);
+ mSetFlagsRule.enableFlags(Flags.FLAG_HAPTIC_FEEDBACK_VIBRATION_OEM_CUSTOMIZATION_ENABLED);
}
@Test
@@ -87,6 +92,21 @@
}
@Test
+ public void testParseCustomizations_featureFlagDisabled_returnsNull() throws Exception {
+ mSetFlagsRule.disableFlags(Flags.FLAG_HAPTIC_FEEDBACK_VIBRATION_OEM_CUSTOMIZATION_ENABLED);
+ // Valid customization XML.
+ String xml = "<haptic-feedback-constants>"
+ + "<constant id=\"10\">"
+ + COMPOSITION_VIBRATION_XML
+ + "</constant>"
+ + "</haptic-feedback-constants>";
+ setupCustomizationFile(xml);
+
+ assertThat(HapticFeedbackCustomization.loadVibrations(mResourcesMock, mVibratorInfoMock))
+ .isNull();
+ }
+
+ @Test
public void testParseCustomizations_oneVibrationCustomization_success() throws Exception {
String xml = "<haptic-feedback-constants>"
+ "<constant id=\"10\">"
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
index a91bd2b..0003555 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
@@ -16,6 +16,8 @@
package com.android.server.vibrator;
+import static android.os.VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
+import static android.os.VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF;
import static android.os.VibrationEffect.Composition.PRIMITIVE_CLICK;
import static android.os.VibrationEffect.Composition.PRIMITIVE_TICK;
import static android.os.VibrationEffect.EFFECT_TEXTURE_TICK;
@@ -24,8 +26,13 @@
import static android.view.HapticFeedbackConstants.CONTEXT_CLICK;
import static android.view.HapticFeedbackConstants.SAFE_MODE_ENABLED;
import static android.view.HapticFeedbackConstants.TEXT_HANDLE_MOVE;
+import static android.view.HapticFeedbackConstants.SCROLL_ITEM_FOCUS;
+import static android.view.HapticFeedbackConstants.SCROLL_LIMIT;
+import static android.view.HapticFeedbackConstants.SCROLL_TICK;
+
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.Mockito.when;
@@ -37,6 +44,7 @@
import android.os.VibratorInfo;
import android.util.AtomicFile;
import android.util.SparseArray;
+import android.view.flags.FeatureFlags;
import androidx.test.InstrumentationRegistry;
@@ -59,23 +67,25 @@
private static final VibrationEffect PRIMITIVE_CLICK_EFFECT =
VibrationEffect.startComposition().addPrimitive(PRIMITIVE_CLICK, 0.3497f).compose();
+ private static final int[] SCROLL_FEEDBACK_CONSTANTS =
+ new int[] {SCROLL_ITEM_FOCUS, SCROLL_LIMIT, SCROLL_TICK};
private Context mContext = InstrumentationRegistry.getContext();
private VibratorInfo mVibratorInfo = VibratorInfo.EMPTY_VIBRATOR_INFO;
@Mock private Resources mResourcesMock;
+ @Mock private FeatureFlags mViewFeatureFlags;
@Test
public void testNonExistentCustomization_useDefault() throws Exception {
// No customization file is set.
- HapticFeedbackVibrationProvider hapticProvider =
- new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo);
+ HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
assertThat(hapticProvider.getVibrationForHapticFeedback(CONTEXT_CLICK))
.isEqualTo(VibrationEffect.get(EFFECT_TICK));
// The customization file specifies no customization.
setupCustomizationFile("<haptic-feedback-constants></haptic-feedback-constants>");
- hapticProvider = new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo);
+ hapticProvider = createProviderWithDefaultCustomizations();
assertThat(hapticProvider.getVibrationForHapticFeedback(CONTEXT_CLICK))
.isEqualTo(VibrationEffect.get(EFFECT_TICK));
@@ -84,8 +94,7 @@
@Test
public void testExceptionParsingCustomizations_useDefault() throws Exception {
setupCustomizationFile("<bad-xml></bad-xml>");
- HapticFeedbackVibrationProvider hapticProvider =
- new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo);
+ HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
assertThat(hapticProvider.getVibrationForHapticFeedback(CONTEXT_CLICK))
.isEqualTo(VibrationEffect.get(EFFECT_TICK));
@@ -97,8 +106,7 @@
SparseArray<VibrationEffect> customizations = new SparseArray<>();
customizations.put(CONTEXT_CLICK, PRIMITIVE_CLICK_EFFECT);
- HapticFeedbackVibrationProvider hapticProvider =
- new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo, customizations);
+ HapticFeedbackVibrationProvider hapticProvider = createProvider(customizations);
// The override for `CONTEXT_CLICK` is used.
assertThat(hapticProvider.getVibrationForHapticFeedback(CONTEXT_CLICK))
@@ -118,8 +126,7 @@
+ "</haptic-feedback-constants>";
setupCustomizationFile(xml);
- HapticFeedbackVibrationProvider hapticProvider =
- new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo);
+ HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
// The override for `CONTEXT_CLICK` is not used because the vibration is not supported.
assertThat(hapticProvider.getVibrationForHapticFeedback(CONTEXT_CLICK))
@@ -137,15 +144,12 @@
customizations.put(TEXT_HANDLE_MOVE, PRIMITIVE_CLICK_EFFECT);
// Test with a customization available for `TEXT_HANDLE_MOVE`.
- HapticFeedbackVibrationProvider hapticProvider =
- new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo, customizations);
+ HapticFeedbackVibrationProvider hapticProvider = createProvider(customizations);
assertThat(hapticProvider.getVibrationForHapticFeedback(TEXT_HANDLE_MOVE)).isNull();
// Test with no customization available for `TEXT_HANDLE_MOVE`.
- hapticProvider =
- new HapticFeedbackVibrationProvider(
- mResourcesMock, mVibratorInfo, /* hapticCustomizations= */ null);
+ hapticProvider = createProvider(/* customizations= */ null);
assertThat(hapticProvider.getVibrationForHapticFeedback(TEXT_HANDLE_MOVE)).isNull();
}
@@ -158,16 +162,13 @@
customizations.put(TEXT_HANDLE_MOVE, PRIMITIVE_CLICK_EFFECT);
// Test with a customization available for `TEXT_HANDLE_MOVE`.
- HapticFeedbackVibrationProvider hapticProvider =
- new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo, customizations);
+ HapticFeedbackVibrationProvider hapticProvider = createProvider(customizations);
assertThat(hapticProvider.getVibrationForHapticFeedback(TEXT_HANDLE_MOVE))
.isEqualTo(PRIMITIVE_CLICK_EFFECT);
// Test with no customization available for `TEXT_HANDLE_MOVE`.
- hapticProvider =
- new HapticFeedbackVibrationProvider(
- mResourcesMock, mVibratorInfo, /* hapticCustomizations= */ null);
+ hapticProvider = createProvider(/* customizations= */ null);
assertThat(hapticProvider.getVibrationForHapticFeedback(TEXT_HANDLE_MOVE))
.isEqualTo(VibrationEffect.get(EFFECT_TEXTURE_TICK));
@@ -181,15 +182,13 @@
SparseArray<VibrationEffect> customizations = new SparseArray<>();
customizations.put(SAFE_MODE_ENABLED, PRIMITIVE_CLICK_EFFECT);
- HapticFeedbackVibrationProvider hapticProvider =
- new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo, customizations);
+ HapticFeedbackVibrationProvider hapticProvider = createProvider(customizations);
assertThat(hapticProvider.getVibrationForHapticFeedback(SAFE_MODE_ENABLED))
.isEqualTo(PRIMITIVE_CLICK_EFFECT);
mockSafeModeEnabledVibration(null);
- hapticProvider =
- new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo, customizations);
+ hapticProvider = createProvider(customizations);
assertThat(hapticProvider.getVibrationForHapticFeedback(SAFE_MODE_ENABLED))
.isEqualTo(PRIMITIVE_CLICK_EFFECT);
@@ -199,9 +198,7 @@
public void testNoValidCustomizationPresentForSafeModeEnabled_resourceBasedVibrationUsed()
throws Exception {
mockSafeModeEnabledVibration(10, 20, 30, 40);
- HapticFeedbackVibrationProvider hapticProvider =
- new HapticFeedbackVibrationProvider(
- mResourcesMock, mVibratorInfo, /* hapticCustomizations= */ null);
+ HapticFeedbackVibrationProvider hapticProvider = createProvider(/* customizations= */ null);
assertThat(hapticProvider.getVibrationForHapticFeedback(SAFE_MODE_ENABLED))
.isEqualTo(VibrationEffect.createWaveform(new long[] {10, 20, 30, 40}, -1));
@@ -211,35 +208,65 @@
public void testNoValidCustomizationAndResourcePresentForSafeModeEnabled_noVibrationUsed()
throws Exception {
mockSafeModeEnabledVibration(null);
- HapticFeedbackVibrationProvider hapticProvider =
- new HapticFeedbackVibrationProvider(
- mResourcesMock, mVibratorInfo, /* hapticCustomizations= */ null);
+ HapticFeedbackVibrationProvider hapticProvider = createProvider(/* customizations= */ null);
assertThat(hapticProvider.getVibrationForHapticFeedback(SAFE_MODE_ENABLED)).isNull();
}
@Test
public void testVibrationAttribute_forNotBypassingIntensitySettings() {
- HapticFeedbackVibrationProvider hapticProvider =
- new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo);
+ HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
SAFE_MODE_ENABLED, /* bypassVibrationIntensitySetting= */ false);
- assertThat(attrs.getFlags() & VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)
- .isEqualTo(0);
+ assertThat(attrs.isFlagSet(FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)).isFalse();
}
@Test
public void testVibrationAttribute_forByassingIntensitySettings() {
- HapticFeedbackVibrationProvider hapticProvider =
- new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo);
+ HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
SAFE_MODE_ENABLED, /* bypassVibrationIntensitySetting= */ true);
- assertThat(attrs.getFlags() & VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)
- .isNotEqualTo(0);
+ assertThat(attrs.isFlagSet(FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)).isTrue();
+ }
+
+ @Test
+ public void testVibrationAttribute_scrollFeedback_scrollApiFlagOn_bypassInterruptPolicy() {
+ when(mViewFeatureFlags.scrollFeedbackApi()).thenReturn(true);
+ HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+
+ for (int effectId : SCROLL_FEEDBACK_CONSTANTS) {
+ VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
+ effectId, /* bypassVibrationIntensitySetting= */ false);
+ assertWithMessage("Expected FLAG_BYPASS_INTERRUPTION_POLICY for effect " + effectId)
+ .that(attrs.isFlagSet(FLAG_BYPASS_INTERRUPTION_POLICY)).isTrue();
+ }
+ }
+
+ @Test
+ public void testVibrationAttribute_scrollFeedback_scrollApiFlagOff_noBypassInterruptPolicy() {
+ when(mViewFeatureFlags.scrollFeedbackApi()).thenReturn(false);
+ HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+
+ for (int effectId : SCROLL_FEEDBACK_CONSTANTS) {
+ VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
+ effectId, /* bypassVibrationIntensitySetting= */ false);
+ assertWithMessage("Expected no FLAG_BYPASS_INTERRUPTION_POLICY for effect " + effectId)
+ .that(attrs.isFlagSet(FLAG_BYPASS_INTERRUPTION_POLICY)).isFalse();
+ }
+ }
+
+ private HapticFeedbackVibrationProvider createProviderWithDefaultCustomizations() {
+ return createProvider(/* customizations= */ null);
+ }
+
+ private HapticFeedbackVibrationProvider createProvider(
+ SparseArray<VibrationEffect> customizations) {
+ return new HapticFeedbackVibrationProvider(
+ mResourcesMock, mVibratorInfo, customizations, mViewFeatureFlags);
}
private void mockVibratorPrimitiveSupport(int... supportedPrimitives) {
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index 0eec9cd..40e0e84 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -31,6 +31,7 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
@@ -46,6 +47,7 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.res.Resources;
import android.hardware.input.IInputManager;
@@ -87,6 +89,7 @@
import android.view.Display;
import android.view.HapticFeedbackConstants;
import android.view.InputDevice;
+import android.view.flags.FeatureFlags;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.FlakyTest;
@@ -172,6 +175,8 @@
private VirtualDeviceManagerInternal mVirtualDeviceManagerInternalMock;
@Mock
private AudioManager mAudioManagerMock;
+ @Mock
+ private FeatureFlags mViewFeatureFlags;
private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>();
@@ -321,7 +326,8 @@
HapticFeedbackVibrationProvider createHapticFeedbackVibrationProvider(
Resources resources, VibratorInfo vibratorInfo) {
return new HapticFeedbackVibrationProvider(
- resources, vibratorInfo, mHapticFeedbackVibrationMap);
+ resources, vibratorInfo, mHapticFeedbackVibrationMap,
+ mViewFeatureFlags);
}
});
return mService;
@@ -649,6 +655,42 @@
}
@Test
+ public void vibrate_withoutBypassFlagsPermissions_bypassFlagsNotApplied() throws Exception {
+ denyPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS);
+ denyPermission(android.Manifest.permission.MODIFY_PHONE_STATE);
+ denyPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING);
+
+ assertCanVibrateWithBypassFlags(false);
+ }
+
+ @Test
+ public void vibrate_withSecureSettingsPermission_bypassFlagsApplied() throws Exception {
+ grantPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS);
+ denyPermission(android.Manifest.permission.MODIFY_PHONE_STATE);
+ denyPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING);
+
+ assertCanVibrateWithBypassFlags(true);
+ }
+
+ @Test
+ public void vibrate_withModifyPhoneStatePermission_bypassFlagsApplied() throws Exception {
+ denyPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS);
+ grantPermission(android.Manifest.permission.MODIFY_PHONE_STATE);
+ denyPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING);
+
+ assertCanVibrateWithBypassFlags(true);
+ }
+
+ @Test
+ public void vibrate_withModifyAudioRoutingPermission_bypassFlagsApplied() throws Exception {
+ denyPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS);
+ denyPermission(android.Manifest.permission.MODIFY_PHONE_STATE);
+ grantPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING);
+
+ assertCanVibrateWithBypassFlags(true);
+ }
+
+ @Test
public void vibrate_withRingtone_usesRingerModeSettings() throws Exception {
mockVibrators(1);
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
@@ -1166,7 +1208,7 @@
}
@Test
- public void vibrate_withTriggerCallback_finishesVibration() throws Exception {
+ public void vibrate_withriggerCallback_finishesVibration() throws Exception {
mockCapabilities(IVibratorManager.CAP_SYNC, IVibratorManager.CAP_PREPARE_COMPOSE);
mockVibrators(1, 2);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
@@ -1303,10 +1345,18 @@
}
@Test
- public void performHapticFeedback_doesNotRequirePermission() throws Exception {
+ public void performHapticFeedback_doesNotRequireVibrateOrBypassPermissions() throws Exception {
+ // Deny permissions that would have been required for regular vibrations, and check that
+ // the vibration proceed as expected to verify that haptic feedback does not need these
+ // permissions.
denyPermission(android.Manifest.permission.VIBRATE);
+ denyPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS);
+ denyPermission(android.Manifest.permission.MODIFY_PHONE_STATE);
+ denyPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING);
+ // Flag override to enable the scroll feedack constants to bypass interruption policies.
+ when(mViewFeatureFlags.scrollFeedbackApi()).thenReturn(true);
mHapticFeedbackVibrationMap.put(
- HapticFeedbackConstants.KEYBOARD_TAP,
+ HapticFeedbackConstants.SCROLL_TICK,
VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
mockVibrators(1);
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
@@ -1315,13 +1365,16 @@
HalVibration vibration =
performHapticFeedbackAndWaitUntilFinished(
- service, HapticFeedbackConstants.KEYBOARD_TAP, /* always= */ true);
+ service, HapticFeedbackConstants.SCROLL_TICK, /* always= */ true);
List<VibrationEffectSegment> playedSegments = fakeVibrator.getAllEffectSegments();
assertEquals(1, playedSegments.size());
PrebakedSegment segment = (PrebakedSegment) playedSegments.get(0);
assertEquals(VibrationEffect.EFFECT_CLICK, segment.getEffectId());
- assertEquals(VibrationAttributes.USAGE_TOUCH, vibration.callerInfo.attrs.getUsage());
+ VibrationAttributes attrs = vibration.callerInfo.attrs;
+ assertEquals(VibrationAttributes.USAGE_HARDWARE_FEEDBACK, attrs.getUsage());
+ assertTrue(attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF));
+ assertTrue(attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY));
}
@Test
@@ -2261,6 +2314,31 @@
assertNull(metrics.halUnsupportedEffectsUsed);
}
+ private void assertCanVibrateWithBypassFlags(boolean expectedCanApplyBypassFlags)
+ throws Exception {
+ mockVibrators(1);
+ mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+ VibratorManagerService service = createSystemReadyService();
+
+ HalVibration vibration = vibrateAndWaitUntilFinished(
+ service,
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK),
+ new VibrationAttributes.Builder()
+ .setUsage(VibrationAttributes.USAGE_TOUCH)
+ .setFlags(
+ VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF
+ | VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY)
+ .build());
+
+ VibrationAttributes attrs = vibration.callerInfo.attrs;
+ assertEquals(
+ expectedCanApplyBypassFlags,
+ attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF));
+ assertEquals(
+ expectedCanApplyBypassFlags,
+ attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY));
+ }
+
private VibrationEffectSegment expectedPrebaked(int effectId) {
return expectedPrebaked(effectId, VibrationEffect.EFFECT_STRENGTH_MEDIUM);
}
@@ -2327,12 +2405,13 @@
return vib;
}
- private void vibrateAndWaitUntilFinished(VibratorManagerService service, VibrationEffect effect,
- VibrationAttributes attrs) throws InterruptedException {
- vibrateAndWaitUntilFinished(service, CombinedVibration.createParallel(effect), attrs);
+ private HalVibration vibrateAndWaitUntilFinished(VibratorManagerService service,
+ VibrationEffect effect, VibrationAttributes attrs) throws InterruptedException {
+ return vibrateAndWaitUntilFinished(
+ service, CombinedVibration.createParallel(effect), attrs);
}
- private void vibrateAndWaitUntilFinished(VibratorManagerService service,
+ private HalVibration vibrateAndWaitUntilFinished(VibratorManagerService service,
CombinedVibration effect, VibrationAttributes attrs) throws InterruptedException {
HalVibration vib =
service.vibrateWithPermissionCheck(UID, Display.DEFAULT_DISPLAY, PACKAGE_NAME,
@@ -2340,6 +2419,8 @@
if (vib != null) {
vib.waitForEnd();
}
+
+ return vib;
}
private void vibrate(VibratorManagerService service, VibrationEffect effect,
@@ -2368,7 +2449,15 @@
return predicateResult;
}
+ private void grantPermission(String permission) {
+ when(mContextSpy.checkCallingOrSelfPermission(permission))
+ .thenReturn(PackageManager.PERMISSION_GRANTED);
+ doNothing().when(mContextSpy).enforceCallingOrSelfPermission(eq(permission), anyString());
+ }
+
private void denyPermission(String permission) {
+ when(mContextSpy.checkCallingOrSelfPermission(permission))
+ .thenReturn(PackageManager.PERMISSION_DENIED);
doThrow(new SecurityException()).when(mContextSpy)
.enforceCallingOrSelfPermission(eq(permission), anyString());
}
diff --git a/services/tests/voiceinteractiontests/Android.bp b/services/tests/voiceinteractiontests/Android.bp
index e704ebf..744cb63 100644
--- a/services/tests/voiceinteractiontests/Android.bp
+++ b/services/tests/voiceinteractiontests/Android.bp
@@ -43,7 +43,7 @@
"services.soundtrigger",
"servicestests-core-utils",
"servicestests-utils-mockito-extended",
- "truth-prebuilt",
+ "truth",
],
libs: [
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index c2812a1..af39b2f 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -57,12 +57,14 @@
"platform-test-annotations",
"servicestests-utils",
"testng",
- "truth-prebuilt",
+ "truth",
"testables",
"hamcrest-library",
"platform-compat-test-rules",
"CtsSurfaceValidatorLib",
"service-sdksandbox.impl",
+ "com.android.window.flags.window-aconfig-java",
+ "flag-junit",
],
libs: [
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index 42e3383..762e23c 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -47,6 +47,8 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MANAGE_MEDIA_PROJECTION"/>
+ <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"/>
+ <uses-permission android:name="android.permission.MONITOR_INPUT"/>
<!-- TODO: Remove largeHeap hack when memory leak is fixed (b/123984854) -->
<application android:debuggable="true"
@@ -104,6 +106,11 @@
android:showWhenLocked="true"
android:turnScreenOn="true" />
+ <activity android:name="android.app.Activity"
+ android:exported="true"
+ android:showWhenLocked="true"
+ android:turnScreenOn="true" />
+
<activity
android:name="androidx.test.core.app.InstrumentationActivityInvoker$EmptyActivity"
android:exported="true">
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
index 0a7bb00..71098aa 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
@@ -20,6 +20,7 @@
import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_ALL_APPS;
import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_ASSIST;
import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_NOTIFICATION_PANEL;
+import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL;
import android.platform.test.annotations.Presubmit;
import android.view.KeyEvent;
@@ -284,6 +285,16 @@
KeyboardLogEvent.APP_SWITCH, KeyEvent.KEYCODE_H, META_ON}};
}
+ @Keep
+ private static Object[][] shortPressOnSettingsTestArguments() {
+ // testName, testKeys, shortPressOnSettingsBehavior, expectedLogEvent, expectedKey,
+ // expectedModifierState
+ return new Object[][]{
+ {"SETTINGS key -> Toggle Notification panel", new int[]{KeyEvent.KEYCODE_SETTINGS},
+ SHORT_PRESS_SETTINGS_NOTIFICATION_PANEL,
+ KeyboardLogEvent.TOGGLE_NOTIFICATION_PANEL, KeyEvent.KEYCODE_SETTINGS, 0}};
+ }
+
@Before
public void setUp() {
setUpPhoneWindowManager(/*supportSettingsUpdate*/ true);
@@ -294,6 +305,7 @@
mPhoneWindowManager.overrideEnableBugReportTrigger(true);
mPhoneWindowManager.overrideStatusBarManagerInternal();
mPhoneWindowManager.overrideStartActivity();
+ mPhoneWindowManager.overrideSendBroadcast();
mPhoneWindowManager.overrideUserSetupComplete();
mPhoneWindowManager.setupAssistForLaunch();
mPhoneWindowManager.overrideTogglePanel();
@@ -330,4 +342,15 @@
mPhoneWindowManager.assertShortcutLogged(VENDOR_ID, PRODUCT_ID, expectedLogEvent,
expectedKey, expectedModifierState, "Failed while executing " + testName);
}
+
+ @Test
+ @Parameters(method = "shortPressOnSettingsTestArguments")
+ public void testShortPressOnSettings(String testName, int[] testKeys,
+ int shortPressOnSettingsBehavior, KeyboardLogEvent expectedLogEvent, int expectedKey,
+ int expectedModifierState) {
+ mPhoneWindowManager.overrideShortPressOnSettingsBehavior(shortPressOnSettingsBehavior);
+ sendKeyCombination(testKeys, 0 /* duration */);
+ mPhoneWindowManager.assertShortcutLogged(VENDOR_ID, PRODUCT_ID, expectedLogEvent,
+ expectedKey, expectedModifierState, "Failed while executing " + testName);
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index ef28ffa..2244dbe 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -375,6 +375,10 @@
mPhoneWindowManager.mDoubleTapOnHomeBehavior = behavior;
}
+ void overrideShortPressOnSettingsBehavior(int behavior) {
+ mPhoneWindowManager.mShortPressOnSettingsBehavior = behavior;
+ }
+
void overrideCanStartDreaming(boolean canDream) {
doReturn(canDream).when(mDreamManagerInternal).canStartDreaming(anyBoolean());
}
@@ -484,6 +488,10 @@
doNothing().when(mContext).startActivityAsUser(any(), any(), any());
}
+ void overrideSendBroadcast() {
+ doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any());
+ }
+
void overrideUserSetupComplete() {
doReturn(true).when(mPhoneWindowManager).isUserSetupComplete();
}
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/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 3bc6450..c241033 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -549,10 +549,12 @@
// Let's pretend that the app has crashed.
firstActivity.app.setThread(null);
- mRootWindowContainer.finishTopCrashedActivities(firstActivity.app, "test");
+ final Task finishedTask = mRootWindowContainer.finishTopCrashedActivities(
+ firstActivity.app, "test");
// Verify that the root task was removed.
assertEquals(originalRootTaskCount, defaultTaskDisplayArea.getRootTaskCount());
+ assertEquals(rootTask, finishedTask);
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java b/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java
index 6c48a69..9f43a17 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java
@@ -94,6 +94,16 @@
}
@Test
+ public void test_selectiveCloneLunchRemoteTransition() {
+ final RemoteTransition transition = mock(RemoteTransition.class);
+ final SafeActivityOptions clone = new SafeActivityOptions(
+ ActivityOptions.makeRemoteTransition(transition))
+ .selectiveCloneLaunchOptions();
+
+ assertSame(clone.getOriginalOptions().getRemoteTransition(), transition);
+ }
+
+ @Test
public void test_getOptions() {
// Mock everything necessary
MockitoSession mockingSession = mockitoSession()
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java
index c1d5147..8119fd4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java
@@ -138,11 +138,11 @@
IWindow window = IWindow.Stub.asInterface(mActivity.mSurfaceView.getWindowToken());
WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(window,
- mScvh1.getFocusGrantToken(), true);
+ mScvh1.getInputTransferToken(), true);
assertTrue("Failed to gain focus for view1", waitForWindowFocus(mView1, true));
WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(window,
- mScvh2.getFocusGrantToken(), true);
+ mScvh2.getInputTransferToken(), true);
assertTrue("Failed to gain focus for view2", waitForWindowFocus(mView2, true));
}
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/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java b/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java
new file mode 100644
index 0000000..ac49839
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright 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 static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.os.IBinder;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.server.wm.BuildUtils;
+import android.server.wm.CtsWindowInfoUtils;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.WindowManager;
+
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.window.flags.Flags;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestName;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@Presubmit
+public class TrustedOverlayTests {
+ private static final String TAG = "TrustedOverlayTests";
+ private static final long TIMEOUT_S = 5L * BuildUtils.HW_TIMEOUT_MULTIPLIER;
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ @Rule
+ public TestName mName = new TestName();
+
+ @Rule
+ public final ActivityScenarioRule<Activity> mActivityRule = new ActivityScenarioRule<>(
+ Activity.class);
+
+ private Instrumentation mInstrumentation;
+ private Activity mActivity;
+
+ @Before
+ public void setup() {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mActivityRule.getScenario().onActivity(activity -> {
+ mActivity = activity;
+ });
+ }
+
+ @RequiresFlagsDisabled(Flags.FLAG_SURFACE_TRUSTED_OVERLAY)
+ @Test
+ public void setTrustedOverlayInputWindow() throws InterruptedException {
+ testTrustedOverlayChildHelper(false);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_SURFACE_TRUSTED_OVERLAY)
+ public void setTrustedOverlayChildLayer() throws InterruptedException {
+ testTrustedOverlayChildHelper(true);
+ }
+
+ /**
+ * b/300659960 where setting spy window and trusted overlay were not happening in the same
+ * transaction causing the system to crash. This ensures there are no synchronization issues
+ * setting both spy window and trusted overlay.
+ */
+ @Test
+ public void setSpyWindowDoesntCrash() throws InterruptedException {
+ IBinder[] tokens = new IBinder[1];
+ CountDownLatch hostTokenReady = new CountDownLatch(1);
+ mInstrumentation.runOnMainSync(() -> {
+ WindowManager.LayoutParams params = mActivity.getWindow().getAttributes();
+ params.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_SPY;
+ params.privateFlags |= PRIVATE_FLAG_TRUSTED_OVERLAY;
+ mActivity.getWindow().setAttributes(params);
+
+ View rootView = mActivity.getWindow().getDecorView();
+ if (rootView.isAttachedToWindow()) {
+ tokens[0] = rootView.getWindowToken();
+ hostTokenReady.countDown();
+ } else {
+ rootView.getViewTreeObserver().addOnWindowAttachListener(
+ new ViewTreeObserver.OnWindowAttachListener() {
+ @Override
+ public void onWindowAttached() {
+ tokens[0] = rootView.getWindowToken();
+ hostTokenReady.countDown();
+ }
+
+ @Override
+ public void onWindowDetached() {
+ }
+ });
+ }
+ });
+
+ assertTrue("Failed to wait for host to get added",
+ hostTokenReady.await(TIMEOUT_S, TimeUnit.SECONDS));
+
+ boolean[] foundTrusted = new boolean[1];
+ CtsWindowInfoUtils.waitForWindowInfos(
+ windowInfos -> {
+ for (var windowInfo : windowInfos) {
+ if (windowInfo.windowToken == tokens[0] && windowInfo.isTrustedOverlay) {
+ foundTrusted[0] = true;
+ return true;
+ }
+ }
+ return false;
+ }, TIMEOUT_S, TimeUnit.SECONDS);
+
+ if (!foundTrusted[0]) {
+ CtsWindowInfoUtils.dumpWindowsOnScreen(TAG, mName.getMethodName());
+ }
+
+ assertTrue("Failed to find window or was not marked trusted", foundTrusted[0]);
+ }
+
+ private void testTrustedOverlayChildHelper(boolean expectedTrustedChild)
+ throws InterruptedException {
+ IBinder[] tokens = new IBinder[2];
+ CountDownLatch hostTokenReady = new CountDownLatch(1);
+ mInstrumentation.runOnMainSync(() -> {
+ mActivity.getWindow().addPrivateFlags(PRIVATE_FLAG_TRUSTED_OVERLAY);
+ View rootView = mActivity.getWindow().getDecorView();
+ if (rootView.isAttachedToWindow()) {
+ tokens[0] = rootView.getWindowToken();
+ hostTokenReady.countDown();
+ } else {
+ rootView.getViewTreeObserver().addOnWindowAttachListener(
+ new ViewTreeObserver.OnWindowAttachListener() {
+ @Override
+ public void onWindowAttached() {
+ tokens[0] = rootView.getWindowToken();
+ hostTokenReady.countDown();
+ }
+
+ @Override
+ public void onWindowDetached() {
+ }
+ });
+ }
+ });
+
+ assertTrue("Failed to wait for host to get added",
+ hostTokenReady.await(TIMEOUT_S, TimeUnit.SECONDS));
+
+ mInstrumentation.runOnMainSync(() -> {
+ WindowManager wm = mActivity.getSystemService(WindowManager.class);
+
+ View childView = new View(mActivity) {
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ tokens[1] = getWindowToken();
+ }
+ };
+ WindowManager.LayoutParams params = new WindowManager.LayoutParams();
+ params.token = tokens[0];
+ params.type = TYPE_APPLICATION_PANEL;
+ wm.addView(childView, params);
+ });
+
+ boolean[] foundTrusted = new boolean[2];
+
+ CtsWindowInfoUtils.waitForWindowInfos(
+ windowInfos -> {
+ for (var windowInfo : windowInfos) {
+ if (windowInfo.windowToken == tokens[0]
+ && windowInfo.isTrustedOverlay) {
+ foundTrusted[0] = true;
+ } else if (windowInfo.windowToken == tokens[1]
+ && windowInfo.isTrustedOverlay) {
+ foundTrusted[1] = true;
+ }
+ }
+ return foundTrusted[0] && foundTrusted[1];
+ }, TIMEOUT_S, TimeUnit.SECONDS);
+
+ if (!foundTrusted[0] || !foundTrusted[1]) {
+ CtsWindowInfoUtils.dumpWindowsOnScreen(TAG, mName.getMethodName());
+ }
+
+ assertTrue("Failed to find parent window or was not marked trusted", foundTrusted[0]);
+ assertEquals("Failed to find child window or was not marked trusted", expectedTrustedChild,
+ foundTrusted[1]);
+ }
+}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsHandlerThread.java b/services/usage/java/com/android/server/usage/UsageStatsHandlerThread.java
deleted file mode 100644
index 6801c94..0000000
--- a/services/usage/java/com/android/server/usage/UsageStatsHandlerThread.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.usage;
-
-import android.os.Handler;
-import android.os.HandlerExecutor;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Process;
-import android.os.Trace;
-
-import java.util.concurrent.Executor;
-
-/**
- * Shared singleton default priority thread for usage stats message handling.
- *
- * @see com.android.internal.os.BackgroundThread
- */
-public final class UsageStatsHandlerThread extends HandlerThread {
- private static final long SLOW_DISPATCH_THRESHOLD_MS = 10_000;
- private static final long SLOW_DELIVERY_THRESHOLD_MS = 30_000;
- private static UsageStatsHandlerThread sInstance;
- private static Handler sHandler;
- private static Executor sHandlerExecutor;
-
- private UsageStatsHandlerThread() {
- super("usagestats.default", Process.THREAD_PRIORITY_DEFAULT);
- }
-
- private static void ensureThreadLocked() {
- if (sInstance == null) {
- sInstance = new UsageStatsHandlerThread();
- sInstance.start();
- final Looper looper = sInstance.getLooper();
- looper.setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
- looper.setSlowLogThresholdMs(
- SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
- sHandler = new Handler(sInstance.getLooper());
- sHandlerExecutor = new HandlerExecutor(sHandler);
- }
- }
-
- /** Returns the UsageStatsHandlerThread singleton */
- public static UsageStatsHandlerThread get() {
- synchronized (UsageStatsHandlerThread.class) {
- ensureThreadLocked();
- return sInstance;
- }
- }
-
- /** Returns the singleton handler for UsageStatsHandlerThread */
- public static Handler getHandler() {
- synchronized (UsageStatsHandlerThread.class) {
- ensureThreadLocked();
- return sHandler;
- }
- }
-
- /** Returns the singleton handler executor for UsageStatsHandlerThread */
- public static Executor getExecutor() {
- synchronized (UsageStatsHandlerThread.class) {
- ensureThreadLocked();
- return sHandlerExecutor;
- }
- }
-}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 18c960e..f3bf026 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -332,8 +332,7 @@
mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
mPackageManager = getContext().getPackageManager();
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
-
- mHandler = new H(UsageStatsHandlerThread.get().getLooper());
+ mHandler = new H(BackgroundThread.get().getLooper());
mIoHandler = new Handler(IoThread.get().getLooper(), mIoHandlerCallback);
mAppStandby = mInjector.getAppStandbyController(getContext());
@@ -495,9 +494,12 @@
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "loadPendingEvents");
loadPendingEventsLocked(userId, pendingEvents);
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
- final LinkedList<Event> eventsInMem = mReportedEvents.get(userId);
- if (eventsInMem != null) {
- pendingEvents.addAll(eventsInMem);
+ synchronized (mReportedEvents) {
+ final LinkedList<Event> eventsInMem = mReportedEvents.get(userId);
+ if (eventsInMem != null) {
+ pendingEvents.addAll(eventsInMem);
+ mReportedEvents.remove(userId);
+ }
}
boolean needToFlush = !pendingEvents.isEmpty();
@@ -518,8 +520,7 @@
mIoHandler.obtainMessage(MSG_HANDLE_LAUNCH_TIME_ON_USER_UNLOCK,
userId, 0).sendToTarget();
- // Remove all the stats stored in memory and in system DE.
- mReportedEvents.remove(userId);
+ // Remove all the stats stored in system DE.
deleteRecursively(new File(Environment.getDataSystemDeDirectory(userId), "usagestats"));
// Force a flush to disk for the current user to ensure important events are persisted.
@@ -916,6 +917,7 @@
}
}
+ @GuardedBy({"mLock", "mReportedEvents"})
private void persistPendingEventsLocked(int userId) {
final LinkedList<Event> pendingEvents = mReportedEvents.get(userId);
if (pendingEvents == null || pendingEvents.isEmpty()) {
@@ -1019,7 +1021,7 @@
+ UserUsageStatsService.eventToString(event.mEventType);
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, traceTag);
}
- synchronized (mLock) {
+ synchronized (mReportedEvents) {
LinkedList<Event> events = mReportedEvents.get(userId);
if (events == null) {
events = new LinkedList<>();
@@ -1943,16 +1945,19 @@
idpw.println();
}
} else {
- final LinkedList<Event> pendingEvents = mReportedEvents.get(userId);
- if (pendingEvents != null && !pendingEvents.isEmpty()) {
- final int eventCount = pendingEvents.size();
- idpw.println("Pending events: count=" + eventCount);
- idpw.increaseIndent();
- for (int idx = 0; idx < eventCount; idx++) {
- UserUsageStatsService.printEvent(idpw, pendingEvents.get(idx), true);
+ synchronized (mReportedEvents) {
+ final LinkedList<Event> pendingEvents = mReportedEvents.get(userId);
+ if (pendingEvents != null && !pendingEvents.isEmpty()) {
+ final int eventCount = pendingEvents.size();
+ idpw.println("Pending events: count=" + eventCount);
+ idpw.increaseIndent();
+ for (int idx = 0; idx < eventCount; idx++) {
+ UserUsageStatsService.printEvent(idpw, pendingEvents.get(idx),
+ true);
+ }
+ idpw.decreaseIndent();
+ idpw.println();
}
- idpw.decreaseIndent();
- idpw.println();
}
}
idpw.decreaseIndent();
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 7d9b379..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} */
@@ -1894,7 +1895,7 @@
* Tones are both played locally for the user to hear and sent to the network to be relayed
* to the remote device.
* <p>
- * You must ensure that any call to {@link #playDtmfTone(char}) is followed by a matching
+ * You must ensure that any call to {@link #playDtmfTone(char)} is followed by a matching
* call to {@link #stopDtmfTone()} and that each tone is stopped before a new one is started.
* The play and stop commands are relayed to the underlying
* {@link android.telecom.ConnectionService} as executed; implementations may not correctly
diff --git a/telecomm/java/android/telecom/CallControl.java b/telecomm/java/android/telecom/CallControl.java
index 50f2ad4..24d3918 100644
--- a/telecomm/java/android/telecom/CallControl.java
+++ b/telecomm/java/android/telecom/CallControl.java
@@ -225,7 +225,7 @@
/**
* Request start a call streaming session. On receiving valid request, telecom will bind to
- * the {@link CallStreamingService} implemented by a general call streaming sender. So that the
+ * the {@code CallStreamingService} implemented by a general call streaming sender. So that the
* call streaming sender can perform streaming local device audio to another remote device and
* control the call during streaming.
*
diff --git a/telecomm/java/android/telecom/CallControlCallback.java b/telecomm/java/android/telecom/CallControlCallback.java
index eac2e64..0166022 100644
--- a/telecomm/java/android/telecom/CallControlCallback.java
+++ b/telecomm/java/android/telecom/CallControlCallback.java
@@ -69,7 +69,7 @@
/**
* Telecom is informing the client to answer an incoming call and set it to active.
*
- * @param videoState see {@link android.telecom.CallAttributes.CallType} for valid states
+ * @param videoState the video state
* @param wasCompleted The {@link Consumer} to be completed. If the client can answer the call
* on their end, {@link Consumer#accept(Object)} should be called with
* {@link Boolean#TRUE}.
diff --git a/telecomm/java/android/telecom/PhoneAccountSuggestion.java b/telecomm/java/android/telecom/PhoneAccountSuggestion.java
index d9f89d5..c83804f 100644
--- a/telecomm/java/android/telecom/PhoneAccountSuggestion.java
+++ b/telecomm/java/android/telecom/PhoneAccountSuggestion.java
@@ -68,7 +68,7 @@
/**
* Creates a new instance of {@link PhoneAccountSuggestion}. This constructor is intended for
- * use by apps implementing a {@link PhoneAccountSuggestionService}, and generally should not be
+ * use by apps implementing a {@code PhoneAccountSuggestionService}, and generally should not be
* used by dialer apps other than for testing purposes.
*
* @param handle The {@link PhoneAccountHandle} for this suggestion.
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/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 98bbb40..7a0bf90 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -5073,7 +5073,6 @@
* MMTEL and RCS.
* <p>
* The default value for this configuration is {@code false}.
- * @see android.telephony.ims.SipDelegateManager
*/
public static final String KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL =
KEY_PREFIX + "ims_single_registration_required_bool";
@@ -5648,8 +5647,8 @@
* <li>{@link #KEY_CAPABILITY_TYPE_SMS_INT_ARRAY}</li>
* <li>{@link #KEY_CAPABILITY_TYPE_CALL_COMPOSER_INT_ARRAY}</li>
* </ul>
- * <p> The values are defined in
- * {@link ImsRegistrationImplBase.ImsRegistrationTech}
+ * <p> The values are defined as {@code REGISTRATION_TECH_*} constants in
+ * {@link android.telephony.ims.stub.ImsRegistrationImplBase}.
*
* changing mmtel_requires_provisioning_bundle requires changes to
* carrier_volte_provisioning_required_bool and vice versa
@@ -5661,12 +5660,12 @@
/**
* List of different RAT technologies on which Provisioning for Voice calling (IR.92)
* is supported.
- * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE
* <p>Possible values are,
- * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_LTE}
- * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_IWLAN}
- * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_CROSS_SIM}
- * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_NR}
+ * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_LTE}
+ * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+ * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM}
+ * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_NR}
+ * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE
*/
public static final String KEY_CAPABILITY_TYPE_VOICE_INT_ARRAY =
KEY_PREFIX + "capability_type_voice_int_array";
@@ -5674,12 +5673,12 @@
/**
* List of different RAT technologies on which Provisioning for Video Telephony (IR.94)
* is supported.
- * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO
* <p>Possible values are,
- * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_LTE}
- * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_IWLAN}
- * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_CROSS_SIM}
- * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_NR}
+ * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_LTE}
+ * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+ * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM}
+ * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_NR}
+ * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO
*/
public static final String KEY_CAPABILITY_TYPE_VIDEO_INT_ARRAY =
KEY_PREFIX + "capability_type_video_int_array";
@@ -5687,24 +5686,24 @@
/**
* List of different RAT technologies on which Provisioning for XCAP over Ut for
* supplementary services. (IR.92) is supported.
- * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT
* <p>Possible values are,
- * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_LTE}
- * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_IWLAN}
- * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_CROSS_SIM}
- * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_NR}
+ * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_LTE}
+ * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+ * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM}
+ * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_NR}
+ * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT
*/
public static final String KEY_CAPABILITY_TYPE_UT_INT_ARRAY =
KEY_PREFIX + "capability_type_ut_int_array";
/**
* List of different RAT technologies on which Provisioning for SMS (IR.92) is supported.
- * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS
* <p>Possible values are,
- * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_LTE}
- * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_IWLAN}
- * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_CROSS_SIM}
- * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_NR}
+ * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_LTE}
+ * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+ * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM}
+ * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_NR}
+ * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS
*/
public static final String KEY_CAPABILITY_TYPE_SMS_INT_ARRAY =
KEY_PREFIX + "capability_type_sms_int_array";
@@ -5712,12 +5711,12 @@
/**
* List of different RAT technologies on which Provisioning for Call Composer
* (section 2.4 of RCC.20) is supported.
- * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_CALL_COMPOSER
* <p>Possible values are,
- * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_LTE}
- * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_IWLAN}
- * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_CROSS_SIM}
- * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_NR}
+ * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_LTE}
+ * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+ * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM}
+ * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_NR}
+ * @see MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_CALL_COMPOSER
*/
public static final String KEY_CAPABILITY_TYPE_CALL_COMPOSER_INT_ARRAY =
KEY_PREFIX + "capability_type_call_composer_int_array";
@@ -5732,8 +5731,8 @@
* <li>{@link #KEY_CAPABILITY_TYPE_OPTIONS_UCE_INT_ARRAY}</li>
* <li>{@link #KEY_CAPABILITY_TYPE_PRESENCE_UCE_INT_ARRAY}</li>
* </ul>
- * <p> The values are defined in
- * {@link ImsRegistrationImplBase.ImsRegistrationTech}
+ * <p> The values are defined as {@code REGISTRATION_TECH_*} constants in
+ * {@link android.telephony.ims.stub.ImsRegistrationImplBase}.
*/
public static final String KEY_RCS_REQUIRES_PROVISIONING_BUNDLE =
KEY_PREFIX + "rcs_requires_provisioning_bundle";
@@ -5742,12 +5741,11 @@
* This carrier supports User Capability Exchange using SIP OPTIONS as defined by the
* framework. If set, the RcsFeature should support capability exchange using SIP OPTIONS.
* If not set, this RcsFeature should not service capability requests.
- * @see RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE
* <p>Possible values are,
- * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_LTE}
- * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_IWLAN}
- * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_CROSS_SIM}
- * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_NR}
+ * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_LTE}
+ * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+ * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM}
+ * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_NR}
*/
public static final String KEY_CAPABILITY_TYPE_OPTIONS_UCE_INT_ARRAY =
KEY_PREFIX + "capability_type_options_uce_int_array";
@@ -5757,12 +5755,11 @@
* framework. If set, the RcsFeature should support capability exchange using a presence
* server. If not set, this RcsFeature should not publish capabilities or service capability
* requests using presence.
- * @see RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE
* <p>Possible values are,
- * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_LTE}
- * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_IWLAN}
- * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_CROSS_SIM}
- * {@link ImsRegistrationImplBase.ImsRegistrationTech#REGISTRATION_TECH_NR}
+ * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_LTE}
+ * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+ * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM}
+ * {@link android.telephony.ims.stub.ImsRegistrationImplBase#REGISTRATION_TECH_NR}
*/
public static final String KEY_CAPABILITY_TYPE_PRESENCE_UCE_INT_ARRAY =
KEY_PREFIX + "capability_type_presence_uce_int_array";
@@ -9640,7 +9637,7 @@
/**
* A list of premium capabilities the carrier supports. Applications can prompt users to
* purchase these premium capabilities from their carrier for a performance boost.
- * Valid values are any of {@link TelephonyManager.PremiumCapability}.
+ * Valid values are any of {@link TelephonyManager}'s {@code PREMIUM_CAPABILITY_*} constants.
*
* This is empty by default, indicating that no premium capabilities are supported.
*
@@ -10526,6 +10523,8 @@
auto_data_switch_rat_signal_score_string_bundle.putIntArray(
"LTE", new int[]{3731, 5965, 8618, 11179, 13384});
auto_data_switch_rat_signal_score_string_bundle.putIntArray(
+ "LTE_CA", new int[]{3831, 6065, 8718, 11379, 13484});
+ auto_data_switch_rat_signal_score_string_bundle.putIntArray(
"NR_SA", new int[]{5288, 6795, 6955, 7562, 9713});
auto_data_switch_rat_signal_score_string_bundle.putIntArray(
"NR_NSA", new int[]{5463, 6827, 8029, 9007, 9428});
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 631013f..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 */
@@ -625,7 +629,7 @@
}
/**
- * @return The access network technology {@link NetworkType}.
+ * @return The access network technology network type..
*/
public @NetworkType int getAccessNetworkTechnology() {
return mAccessNetworkTechnology;
@@ -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/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index fa5fd87..8e90fe7 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1364,7 +1364,7 @@
public static class OnSubscriptionsChangedListener {
/**
- * After {@link Build.VERSION_CODES.Q}, it is no longer necessary to instantiate a
+ * After {@link Build.VERSION_CODES#Q}, it is no longer necessary to instantiate a
* Handler inside of the OnSubscriptionsChangedListener in all cases, so it will only
* be done for callers that do not supply an Executor.
*/
@@ -1388,13 +1388,14 @@
/**
* Create an OnSubscriptionsChangedListener.
*
- * For callers targeting {@link Build.VERSION_CODES.P} or earlier, this can only be called
+ * For callers targeting {@link Build.VERSION_CODES#P} or earlier, this can only be called
* on a thread that already has a prepared Looper. Callers targeting Q or later should
* subsequently use {@link SubscriptionManager#addOnSubscriptionsChangedListener(
* Executor, OnSubscriptionsChangedListener)}.
*
- * On OS versions prior to {@link Build.VERSION_CODES.V} callers should assume that this
- * call will fail if invoked on a thread that does not already have a prepared looper.
+ * On OS versions prior to {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} callers should
+ * assume that this call will fail if invoked on a thread that does not already have a
+ * prepared looper.
*/
public OnSubscriptionsChangedListener() {
mCreatorLooper = Looper.myLooper();
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 234ca91..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";
@@ -13132,8 +13135,8 @@
* </ul>
*
* @param executor The executor on which the result listener will be called.
- * @param resultListener {@link Consumer} that will be called with the result fetched
- * from the radio of type {@link CarrierRestrictionStatus}
+ * @param resultListener {@link Consumer} that will be called with the carrier restriction
+ * status result fetched from the radio
* @throws SecurityException if the caller does not have the required permission/privileges or
* if the caller is not pre-registered.
*/
@@ -13465,7 +13468,7 @@
public static final int DATA_ENABLED_REASON_THERMAL = 3;
/**
- * To indicate data was enabled or disabled due to {@link MobileDataPolicy} overrides.
+ * To indicate data was enabled or disabled due to mobile data policy overrides.
* Note that this is not a valid reason for {@link #setDataEnabledForReason(int, boolean)} and
* is only used to indicate that data enabled was changed due to an override.
*/
@@ -14571,7 +14574,7 @@
* @param needValidation whether validation is needed before switch happens.
* @param executor The executor of where the callback will execute.
* @param callback Callback will be triggered once it succeeds or failed.
- * See {@link TelephonyManager.SetOpportunisticSubscriptionResult}
+ * See the {@code SET_OPPORTUNISTIC_SUB_*} constants
* for more details. Pass null if don't care about the result.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
@@ -15036,6 +15039,7 @@
* @hide
*/
@TestApi
+ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public static final int HAL_SERVICE_SATELLITE = 8;
/** @hide */
@@ -17516,7 +17520,7 @@
public @interface PurchasePremiumCapabilityResult {}
/**
- * Returns the purchase result {@link PurchasePremiumCapabilityResult} as a String.
+ * Returns the purchase result as a String.
*
* @param result The purchase premium capability result.
* @return The purchase result as a String.
@@ -17570,7 +17574,6 @@
* @param capability The premium capability to purchase.
* @param executor The callback executor for the response.
* @param callback The result of the purchase request.
- * One of {@link PurchasePremiumCapabilityResult}.
* @throws SecurityException if the caller does not hold permissions
* READ_BASIC_PHONE_STATE or INTERNET.
* @see #isPremiumCapabilityAvailableForPurchase(int) to check whether the capability is valid.
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 26c17a4..e9af486 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -532,7 +532,7 @@
/**
* Returns the default MTU (Maximum Transmission Unit) size in bytes of the IPv4 routes brought
* up by this APN setting. Note this value will only be used when MTU size is not provided
- * in {@link DataCallResponse#getMtuV4()} during network bring up.
+ * in {@code DataCallResponse#getMtuV4()} during network bring up.
*
* @return the MTU size in bytes of the route.
*/
@@ -542,7 +542,7 @@
/**
* Returns the MTU size of the IPv6 mobile interface to which the APN connected. Note this value
- * will only be used when MTU size is not provided in {@link DataCallResponse#getMtuV6()}
+ * will only be used when MTU size is not provided in {@code DataCallResponse#getMtuV6()}
* during network bring up.
*
* @return the MTU size in bytes of the route.
@@ -1787,7 +1787,7 @@
/**
* Set the default MTU (Maximum Transmission Unit) size in bytes of the IPv4 routes brought
* up by this APN setting. Note this value will only be used when MTU size is not provided
- * in {@link DataCallResponse#getMtuV4()} during network bring up.
+ * in {@code DataCallResponse#getMtuV4()} during network bring up.
*
* @param mtuV4 the MTU size in bytes of the route.
*/
@@ -1799,7 +1799,7 @@
/**
* Set the default MTU (Maximum Transmission Unit) size in bytes of the IPv6 routes brought
* up by this APN setting. Note this value will only be used when MTU size is not provided
- * in {@link DataCallResponse#getMtuV6()} during network bring up.
+ * in {@code DataCallResponse#getMtuV6()} during network bring up.
*
* @param mtuV6 the MTU size in bytes of the route.
*/
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index ed46276..b9a7d43 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -249,7 +249,7 @@
*
* <p>{@link #EXTRA_USE_QR_SCANNER} not set or set to false: The LPA should try to get an
* activation code from the carrier app by binding to the carrier app service implementing
- * {@link android.service.euicc.EuiccService#ACTION_BIND_CARRIER_PROVISIONING_SERVICE}.
+ * {@code android.service.euicc.EuiccService#ACTION_BIND_CARRIER_PROVISIONING_SERVICE}.
* <p>{@link #EXTRA_USE_QR_SCANNER} set to true: The LPA should launch a QR scanner for the user
* to scan an eSIM profile QR code.
*
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index caee4e2..2172d7d 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -561,7 +561,7 @@
* @param c The MmTel {@link CapabilityCallback} to be registered.
* @see #unregisterMmTelCapabilityCallback(CapabilityCallback)
* @throws ImsException if the subscription associated with this callback is valid, but
- * the {@link ImsService} associated with the subscription is not available. This can happen if
+ * the {@code ImsService} associated with the subscription is not available. This can happen if
* the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
* reason.
*/
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index 4439e5c..2b49bcd 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -247,7 +247,7 @@
* @param c The {@link RegistrationManager.RegistrationCallback} to be added.
* @see #unregisterImsRegistrationCallback(RegistrationManager.RegistrationCallback)
* @throws ImsException if the subscription associated with this callback is valid, but
- * the {@link ImsService} associated with the subscription is not available. This can happen if
+ * the {@code ImsService} associated with the subscription is not available. This can happen if
* the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
* reason.
*/
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index 37a6a7e..1c5d1e9 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -1469,9 +1469,8 @@
/**
* Get the provisioning status for the IMS MmTel capability specified.
*
- * If provisioning is not required for the queried
- * {@link MmTelFeature.MmTelCapabilities.MmTelCapability} and
- * {@link ImsRegistrationImplBase.ImsRegistrationTech} combination specified, this method will
+ * If provisioning is not required for the queried {@code capability} and
+ * {@code tech} combination specified, this method will
* always return {@code true}.
*
* <p> Requires Permission:
@@ -1503,7 +1502,7 @@
* Get the provisioning status for the IMS RCS capability specified.
*
* If provisioning is not required for the queried
- * {@link ImsRcsManager.RcsImsCapabilityFlag} or if the device does not support IMS
+ * {@code capability} or if the device does not support IMS
* this method will always return {@code true}.
*
* @see CarrierConfigManager.Ims#KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL
@@ -1533,7 +1532,7 @@
* Get the provisioning status for the IMS RCS capability specified.
*
* If provisioning is not required for the queried
- * {@link ImsRcsManager.RcsImsCapabilityFlag} or if the device does not support IMS
+ * {@code capability} or if the device does not support IMS
* this method will always return {@code true}.
*
* <p> Requires Permission:
diff --git a/telephony/java/android/telephony/ims/RegistrationManager.java b/telephony/java/android/telephony/ims/RegistrationManager.java
index 873ce60..b528866 100644
--- a/telephony/java/android/telephony/ims/RegistrationManager.java
+++ b/telephony/java/android/telephony/ims/RegistrationManager.java
@@ -31,7 +31,6 @@
import android.telephony.AccessNetworkConstants;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.ims.aidl.IImsRegistrationCallback;
-import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.util.Log;
@@ -42,7 +41,7 @@
import java.util.function.Consumer;
/**
- * Manages IMS Service registration state for associated {@link ImsFeature}s.
+ * Manages IMS Service registration state for associated {@code ImsFeature}s.
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
public interface RegistrationManager {
@@ -394,7 +393,7 @@
* @param c The {@link RegistrationCallback} to be added.
* @see #unregisterImsRegistrationCallback(RegistrationCallback)
* @throws ImsException if the subscription associated with this callback is valid, but
- * the {@link ImsService} associated with the subscription is not available. This can happen if
+ * the {@code ImsService} associated with the subscription is not available. This can happen if
* the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
* reason.
*/
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/BatteryStatsPerfTest/Android.bp b/tests/BatteryStatsPerfTest/Android.bp
index 5233a5b..c2a7015 100644
--- a/tests/BatteryStatsPerfTest/Android.bp
+++ b/tests/BatteryStatsPerfTest/Android.bp
@@ -27,7 +27,7 @@
static_libs: [
"androidx.test.rules",
"apct-perftests-utils",
- "truth-prebuilt",
+ "truth",
],
platform_apis: true,
certificate: "platform",
diff --git a/tests/BinaryTransparencyHostTest/Android.bp b/tests/BinaryTransparencyHostTest/Android.bp
index 615990f..38cb9869 100644
--- a/tests/BinaryTransparencyHostTest/Android.bp
+++ b/tests/BinaryTransparencyHostTest/Android.bp
@@ -30,7 +30,7 @@
"compatibility-host-util",
],
static_libs: [
- "truth-prebuilt",
+ "truth",
],
data: [
":BinaryTransparencyTestApp",
diff --git a/tests/BlobStoreTestUtils/Android.bp b/tests/BlobStoreTestUtils/Android.bp
index c4faf7f..1fb73e2 100644
--- a/tests/BlobStoreTestUtils/Android.bp
+++ b/tests/BlobStoreTestUtils/Android.bp
@@ -22,12 +22,12 @@
}
java_library {
- name: "BlobStoreTestUtils",
- srcs: ["src/**/*.java"],
- static_libs: [
- "truth-prebuilt",
- "androidx.test.uiautomator_uiautomator",
- "androidx.test.ext.junit",
- ],
- sdk_version: "test_current",
+ name: "BlobStoreTestUtils",
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "truth",
+ "androidx.test.uiautomator_uiautomator",
+ "androidx.test.ext.junit",
+ ],
+ sdk_version: "test_current",
}
diff --git a/tests/ChoreographerTests/Android.bp b/tests/ChoreographerTests/Android.bp
index ca30267..5d49120 100644
--- a/tests/ChoreographerTests/Android.bp
+++ b/tests/ChoreographerTests/Android.bp
@@ -34,7 +34,7 @@
"androidx.test.rules",
"compatibility-device-util-axt",
"com.google.android.material_material",
- "truth-prebuilt",
+ "truth",
],
jni_libs: [
"libchoreographertests_jni",
diff --git a/tests/CtsSurfaceControlTestsStaging/Android.bp b/tests/CtsSurfaceControlTestsStaging/Android.bp
index 6809521..96e4a9e 100644
--- a/tests/CtsSurfaceControlTestsStaging/Android.bp
+++ b/tests/CtsSurfaceControlTestsStaging/Android.bp
@@ -37,7 +37,7 @@
"compatibility-device-util-axt",
"com.google.android.material_material",
"SurfaceFlingerProperties",
- "truth-prebuilt",
+ "truth",
],
resource_dirs: ["src/main/res"],
certificate: "platform",
diff --git a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
index 79348d1..ae7c2a9 100644
--- a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
+++ b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
@@ -247,7 +247,7 @@
int rc = 0;
try (SurfaceControl.Transaction transaction = new SurfaceControl.Transaction()) {
- transaction.setFrameRateCategory(mSurfaceControl, category);
+ transaction.setFrameRateCategory(mSurfaceControl, category, false);
transaction.apply();
}
return rc;
diff --git a/tests/DynamicCodeLoggerIntegrationTests/Android.bp b/tests/DynamicCodeLoggerIntegrationTests/Android.bp
index 448d46f..3f2c808 100644
--- a/tests/DynamicCodeLoggerIntegrationTests/Android.bp
+++ b/tests/DynamicCodeLoggerIntegrationTests/Android.bp
@@ -47,7 +47,7 @@
static_libs: [
"androidx.test.rules",
- "truth-prebuilt",
+ "truth",
],
compile_multilib: "both",
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index a2ae56e..82aa85d 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -236,7 +236,7 @@
static_libs: [
"flickerlib",
"flickerlib-helpers",
- "truth-prebuilt",
+ "truth",
"app-helpers-core",
],
}
@@ -255,7 +255,7 @@
"flickerlib",
"flickerlib-apphelpers",
"flickerlib-helpers",
- "truth-prebuilt",
+ "truth",
"app-helpers-core",
"wm-flicker-window-extensions",
],
diff --git a/tests/FlickerTests/AndroidTestTemplate.xml b/tests/FlickerTests/AndroidTestTemplate.xml
index 63acddf..0f47980 100644
--- a/tests/FlickerTests/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/AndroidTestTemplate.xml
@@ -61,9 +61,7 @@
<option name="shell-timeout" value="6600s"/>
<option name="test-timeout" value="6600s"/>
<option name="hidden-api-checks" value="false"/>
- <!-- TODO(b/288396763): re-enable when PerfettoListener is fixed
<option name="device-listeners" value="android.device.collectors.PerfettoListener"/>
- -->
<!-- PerfettoListener related arguments -->
<option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/>
<option name="instrumentation-arg"
diff --git a/tests/FsVerityTest/TEST_MAPPING b/tests/FsVerityTest/TEST_MAPPING
index 39944be..7d59d77 100644
--- a/tests/FsVerityTest/TEST_MAPPING
+++ b/tests/FsVerityTest/TEST_MAPPING
@@ -1,12 +1,7 @@
{
- "postsubmit": [
+ "presubmit": [
{
"name": "FsVerityTest"
- },
- // nextgen test only runs during postsubmit.
- {
- "name": "FsVerityTest",
- "keywords": ["nextgen"]
}
]
}
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index 365e00e..cf2d5d6 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -9,6 +9,10 @@
android_test {
name: "InputTests",
+ defaults: [
+ // For ExtendedMockito dependencies.
+ "modules-utils-testable-device-config-defaults",
+ ],
srcs: [
"src/**/*.java",
"src/**/*.kt",
@@ -35,7 +39,7 @@
"services.core.unboosted",
"testables",
"testng",
- "truth-prebuilt",
+ "truth",
],
libs: [
"android.test.mock",
diff --git a/tests/Input/src/com/android/server/input/FocusEventDebugViewTest.java b/tests/Input/src/com/android/server/input/FocusEventDebugViewTest.java
deleted file mode 100644
index 1b98887..0000000
--- a/tests/Input/src/com/android/server/input/FocusEventDebugViewTest.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright 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.input;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.view.InputChannel;
-import android.view.InputDevice;
-import android.view.MotionEvent;
-import android.view.MotionEvent.PointerCoords;
-import android.view.MotionEvent.PointerProperties;
-import android.view.ViewConfiguration;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Build/Install/Run:
- * atest FocusEventDebugViewTest
- */
-@RunWith(AndroidJUnit4.class)
-public class FocusEventDebugViewTest {
-
- private FocusEventDebugView mFocusEventDebugView;
- private FocusEventDebugView.RotaryInputValueView mRotaryInputValueView;
- private FocusEventDebugView.RotaryInputGraphView mRotaryInputGraphView;
- private float mScaledVerticalScrollFactor;
-
- @Before
- public void setUp() throws Exception {
- Context context = InstrumentationRegistry.getContext();
- mScaledVerticalScrollFactor =
- ViewConfiguration.get(context).getScaledVerticalScrollFactor();
- InputManagerService mockService = mock(InputManagerService.class);
- when(mockService.monitorInput(anyString(), anyInt()))
- .thenReturn(InputChannel.openInputChannelPair("FocusEventDebugViewTest")[1]);
-
- mRotaryInputValueView = new FocusEventDebugView.RotaryInputValueView(context);
- mRotaryInputGraphView = new FocusEventDebugView.RotaryInputGraphView(context);
- mFocusEventDebugView = new FocusEventDebugView(context, mockService,
- () -> mRotaryInputValueView, () -> mRotaryInputGraphView);
- }
-
- @Test
- public void startsRotaryInputValueViewWithDefaultValue() {
- assertEquals("+0.0", mRotaryInputValueView.getText());
- }
-
- @Test
- public void startsRotaryInputGraphViewWithDefaultFrameCenter() {
- assertEquals(0, mRotaryInputGraphView.getFrameCenterPosition(), 0.01);
- }
-
- @Test
- public void handleRotaryInput_updatesRotaryInputValueViewWithScrollValue() {
- mFocusEventDebugView.handleUpdateShowRotaryInput(true);
-
- mFocusEventDebugView.handleRotaryInput(createRotaryMotionEvent(0.5f));
-
- assertEquals(String.format("+%.1f", 0.5f * mScaledVerticalScrollFactor),
- mRotaryInputValueView.getText());
- }
-
- @Test
- public void handleRotaryInput_translatesRotaryInputGraphViewWithHighScrollValue() {
- mFocusEventDebugView.handleUpdateShowRotaryInput(true);
-
- mFocusEventDebugView.handleRotaryInput(createRotaryMotionEvent(1000f));
-
- assertTrue(mRotaryInputGraphView.getFrameCenterPosition() > 0);
- }
-
- @Test
- public void updateActivityStatus_setsAndRemovesColorFilter() {
- // It should not be active initially.
- assertNull(mRotaryInputValueView.getBackground().getColorFilter());
-
- mRotaryInputValueView.updateActivityStatus(true);
- // It should be active after rotary input.
- assertNotNull(mRotaryInputValueView.getBackground().getColorFilter());
-
- mRotaryInputValueView.updateActivityStatus(false);
- // It should not be active after waiting for mUpdateActivityStatusCallback.
- assertNull(mRotaryInputValueView.getBackground().getColorFilter());
- }
-
- private MotionEvent createRotaryMotionEvent(float scrollAxisValue) {
- PointerCoords pointerCoords = new PointerCoords();
- pointerCoords.setAxisValue(MotionEvent.AXIS_SCROLL, scrollAxisValue);
- PointerProperties pointerProperties = new PointerProperties();
-
- return MotionEvent.obtain(
- /* downTime */ 0,
- /* eventTime */ 0,
- /* action */ MotionEvent.ACTION_SCROLL,
- /* pointerCount */ 1,
- /* pointerProperties */ new PointerProperties[] {pointerProperties},
- /* pointerCoords */ new PointerCoords[] {pointerCoords},
- /* metaState */ 0,
- /* buttonState */ 0,
- /* xPrecision */ 0,
- /* yPrecision */ 0,
- /* deviceId */ 0,
- /* edgeFlags */ 0,
- /* source */ InputDevice.SOURCE_ROTARY_ENCODER,
- /* flags */ 0
- );
- }
-}
diff --git a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
index b6477510..44de6a6 100644
--- a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
@@ -32,11 +32,16 @@
import android.os.test.TestLooper
import android.platform.test.annotations.Presubmit
import android.provider.Settings
+import android.util.proto.ProtoOutputStream
import android.view.InputDevice
import android.view.inputmethod.InputMethodInfo
import android.view.inputmethod.InputMethodSubtype
import androidx.test.core.R
import androidx.test.core.app.ApplicationProvider
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.internal.os.KeyboardConfiguredProto
+import com.android.internal.util.FrameworkStatsLog
+import com.android.modules.utils.testing.ExtendedMockitoRule
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotEquals
@@ -46,9 +51,9 @@
import org.junit.Before
import org.junit.Rule
import org.junit.Test
+import org.mockito.ArgumentMatchers
import org.mockito.Mock
import org.mockito.Mockito
-import org.mockito.junit.MockitoJUnit
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.io.IOException
@@ -95,16 +100,23 @@
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
+ const val LAYOUT_TYPE_DEFAULT = 0
}
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)
- @get:Rule
- val rule = MockitoJUnit.rule()!!
+ @JvmField
+ @Rule
+ val extendedMockitoRule = ExtendedMockitoRule.Builder(this)
+ .mockStatic(FrameworkStatsLog::class.java).build()!!
@Mock
private lateinit var iInputManager: IInputManager
@@ -145,7 +157,9 @@
override fun finishWrite(fos: FileOutputStream?, success: Boolean) {}
})
testLooper = TestLooper()
- keyboardLayoutManager = KeyboardLayoutManager(context, native, dataStore, testLooper.looper)
+ keyboardLayoutManager = Mockito.spy(
+ KeyboardLayoutManager(context, native, dataStore, testLooper.looper)
+ )
setupInputDevices()
setupBroadcastReceiver()
setupIme()
@@ -698,7 +712,7 @@
assertCorrectLayout(
keyboardDevice,
createImeSubtypeForLanguageTag("de"),
- createLayoutDescriptor("keyboard_layout_german")
+ GERMAN_LAYOUT_DESCRIPTOR
)
assertCorrectLayout(
keyboardDevice,
@@ -763,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,
@@ -827,6 +841,100 @@
}
}
+ @Test
+ fun testConfigurationLogged_onInputDeviceAdded_VirtualKeyboardBasedSelection() {
+ val imeInfos = listOf(
+ KeyboardLayoutManager.ImeInfo(0, imeInfo,
+ createImeSubtypeForLanguageTagAndLayoutType("de-Latn", "qwertz")))
+ Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping
+ NewSettingsApiFlag(true).use {
+ keyboardLayoutManager.onInputDeviceAdded(keyboardDevice.id)
+ ExtendedMockito.verify {
+ FrameworkStatsLog.write(
+ ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED),
+ ArgumentMatchers.anyBoolean(),
+ ArgumentMatchers.eq(keyboardDevice.vendorId),
+ ArgumentMatchers.eq(keyboardDevice.productId),
+ ArgumentMatchers.eq(createByteArray(
+ KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
+ LAYOUT_TYPE_DEFAULT,
+ GERMAN_LAYOUT_NAME,
+ KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD,
+ "de-Latn",
+ LAYOUT_TYPE_QWERTZ))
+ )
+ }
+ }
+ }
+
+ @Test
+ fun testConfigurationLogged_onInputDeviceAdded_DeviceBasedSelection() {
+ val imeInfos = listOf(
+ KeyboardLayoutManager.ImeInfo(0, imeInfo,
+ createImeSubtypeForLanguageTagAndLayoutType("de-Latn", "qwertz")))
+ Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping
+ NewSettingsApiFlag(true).use {
+ keyboardLayoutManager.onInputDeviceAdded(englishQwertyKeyboardDevice.id)
+ ExtendedMockito.verify {
+ FrameworkStatsLog.write(
+ ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED),
+ ArgumentMatchers.anyBoolean(),
+ ArgumentMatchers.eq(englishQwertyKeyboardDevice.vendorId),
+ ArgumentMatchers.eq(englishQwertyKeyboardDevice.productId),
+ ArgumentMatchers.eq(createByteArray(
+ "en",
+ LAYOUT_TYPE_QWERTY,
+ ENGLISH_US_LAYOUT_NAME,
+ KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE,
+ "de-Latn",
+ LAYOUT_TYPE_QWERTZ))
+ )
+ }
+ }
+ }
+
+ @Test
+ fun testConfigurationLogged_onInputDeviceAdded_DefaultSelection() {
+ val imeInfos = listOf(KeyboardLayoutManager.ImeInfo(0, imeInfo, createImeSubtype()))
+ Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping
+ NewSettingsApiFlag(true).use {
+ keyboardLayoutManager.onInputDeviceAdded(keyboardDevice.id)
+ ExtendedMockito.verify {
+ FrameworkStatsLog.write(
+ ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED),
+ ArgumentMatchers.anyBoolean(),
+ ArgumentMatchers.eq(keyboardDevice.vendorId),
+ ArgumentMatchers.eq(keyboardDevice.productId),
+ ArgumentMatchers.eq(createByteArray(
+ KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
+ LAYOUT_TYPE_DEFAULT,
+ "Default",
+ KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEFAULT,
+ KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
+ LAYOUT_TYPE_DEFAULT))
+ )
+ }
+ }
+ }
+
+ @Test
+ fun testConfigurationNotLogged_onInputDeviceChanged() {
+ val imeInfos = listOf(KeyboardLayoutManager.ImeInfo(0, imeInfo, createImeSubtype()))
+ Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping
+ NewSettingsApiFlag(true).use {
+ keyboardLayoutManager.onInputDeviceChanged(keyboardDevice.id)
+ ExtendedMockito.verify({
+ FrameworkStatsLog.write(
+ ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED),
+ ArgumentMatchers.anyBoolean(),
+ ArgumentMatchers.anyInt(),
+ ArgumentMatchers.anyInt(),
+ ArgumentMatchers.any(ByteArray::class.java)
+ )
+ }, Mockito.times(0))
+ }
+ }
+
private fun assertCorrectLayout(
device: InputDevice,
imeSubtype: InputMethodSubtype,
@@ -842,18 +950,60 @@
}
private fun createImeSubtype(): InputMethodSubtype =
- InputMethodSubtype.InputMethodSubtypeBuilder().setSubtypeId(nextImeSubtypeId++).build()
+ createImeSubtypeForLanguageTagAndLayoutType(null, null)
private fun createImeSubtypeForLanguageTag(languageTag: String): InputMethodSubtype =
- InputMethodSubtype.InputMethodSubtypeBuilder().setSubtypeId(nextImeSubtypeId++)
- .setLanguageTag(languageTag).build()
+ createImeSubtypeForLanguageTagAndLayoutType(languageTag, null)
private fun createImeSubtypeForLanguageTagAndLayoutType(
- languageTag: String,
- layoutType: String
- ): InputMethodSubtype =
- InputMethodSubtype.InputMethodSubtypeBuilder().setSubtypeId(nextImeSubtypeId++)
- .setPhysicalKeyboardHint(ULocale.forLanguageTag(languageTag), layoutType).build()
+ languageTag: String?,
+ layoutType: String?
+ ): InputMethodSubtype {
+ val builder = InputMethodSubtype.InputMethodSubtypeBuilder()
+ .setSubtypeId(nextImeSubtypeId++)
+ .setIsAuxiliary(false)
+ .setSubtypeMode("keyboard")
+ if (languageTag != null && layoutType != null) {
+ builder.setPhysicalKeyboardHint(ULocale.forLanguageTag(languageTag), layoutType)
+ } else if (languageTag != null) {
+ builder.setLanguageTag(languageTag)
+ }
+ return builder.build()
+ }
+
+ private fun createByteArray(
+ expectedLanguageTag: String, expectedLayoutType: Int, expectedLayoutName: String,
+ expectedCriteria: Int, expectedImeLanguageTag: String, expectedImeLayoutType: Int): ByteArray {
+ val proto = ProtoOutputStream()
+ val keyboardLayoutConfigToken = proto.start(
+ KeyboardConfiguredProto.RepeatedKeyboardLayoutConfig.KEYBOARD_LAYOUT_CONFIG)
+ proto.write(
+ KeyboardConfiguredProto.KeyboardLayoutConfig.KEYBOARD_LANGUAGE_TAG,
+ expectedLanguageTag
+ )
+ proto.write(
+ KeyboardConfiguredProto.KeyboardLayoutConfig.KEYBOARD_LAYOUT_TYPE,
+ expectedLayoutType
+ )
+ proto.write(
+ KeyboardConfiguredProto.KeyboardLayoutConfig.KEYBOARD_LAYOUT_NAME,
+ expectedLayoutName
+ )
+ proto.write(
+ KeyboardConfiguredProto.KeyboardLayoutConfig.LAYOUT_SELECTION_CRITERIA,
+ expectedCriteria
+ )
+ proto.write(
+ KeyboardConfiguredProto.KeyboardLayoutConfig.IME_LANGUAGE_TAG,
+ expectedImeLanguageTag
+ )
+ proto.write(
+ KeyboardConfiguredProto.KeyboardLayoutConfig.IME_LAYOUT_TYPE,
+ expectedImeLayoutType
+ )
+ proto.end(keyboardLayoutConfigToken);
+ return proto.bytes
+ }
private fun hasLayout(layoutList: Array<KeyboardLayout>, layoutDesc: String): Boolean {
for (kl in layoutList) {
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/Input/src/com/android/server/input/debug/FocusEventDebugViewTest.java b/tests/Input/src/com/android/server/input/debug/FocusEventDebugViewTest.java
new file mode 100644
index 0000000..ae7fb3b
--- /dev/null
+++ b/tests/Input/src/com/android/server/input/debug/FocusEventDebugViewTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 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.input.debug;
+
+import static org.mockito.Mockito.anyFloat;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.view.InputChannel;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.MotionEvent.PointerCoords;
+import android.view.MotionEvent.PointerProperties;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.input.InputManagerService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Build/Install/Run:
+ * atest FocusEventDebugViewTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class FocusEventDebugViewTest {
+
+ private FocusEventDebugView mFocusEventDebugView;
+ private RotaryInputValueView mRotaryInputValueView;
+ private RotaryInputGraphView mRotaryInputGraphView;
+
+ @Before
+ public void setUp() throws Exception {
+ Context context = InstrumentationRegistry.getContext();
+ InputManagerService mockService = mock(InputManagerService.class);
+ when(mockService.monitorInput(anyString(), anyInt()))
+ .thenReturn(InputChannel.openInputChannelPair("FocusEventDebugViewTest")[1]);
+
+ mRotaryInputValueView = spy(new RotaryInputValueView(context));
+ mRotaryInputGraphView = spy(new RotaryInputGraphView(context));
+ mFocusEventDebugView = new FocusEventDebugView(context, mockService,
+ () -> mRotaryInputValueView, () -> mRotaryInputGraphView);
+ }
+
+ @Test
+ public void handleRotaryInput_sendsMotionEventWhenEnabled() {
+ mFocusEventDebugView.handleUpdateShowRotaryInput(true);
+
+ mFocusEventDebugView.handleRotaryInput(createRotaryMotionEvent(0.5f, 10L));
+
+ verify(mRotaryInputGraphView).addValue(0.5f, 10L);
+ verify(mRotaryInputValueView).updateValue(0.5f);
+ }
+
+ @Test
+ public void handleRotaryInput_doesNotSendMotionEventWhenDisabled() {
+ mFocusEventDebugView.handleUpdateShowRotaryInput(false);
+
+ mFocusEventDebugView.handleRotaryInput(createRotaryMotionEvent(0.5f, 10L));
+
+ verify(mRotaryInputGraphView, never()).addValue(anyFloat(), anyLong());
+ verify(mRotaryInputValueView, never()).updateValue(anyFloat());
+ }
+
+ private MotionEvent createRotaryMotionEvent(float scrollAxisValue, long eventTime) {
+ PointerCoords pointerCoords = new PointerCoords();
+ pointerCoords.setAxisValue(MotionEvent.AXIS_SCROLL, scrollAxisValue);
+ PointerProperties pointerProperties = new PointerProperties();
+
+ return MotionEvent.obtain(
+ /* downTime */ 0,
+ /* eventTime */ eventTime,
+ /* action */ MotionEvent.ACTION_SCROLL,
+ /* pointerCount */ 1,
+ /* pointerProperties */ new PointerProperties[] {pointerProperties},
+ /* pointerCoords */ new PointerCoords[] {pointerCoords},
+ /* metaState */ 0,
+ /* buttonState */ 0,
+ /* xPrecision */ 0,
+ /* yPrecision */ 0,
+ /* deviceId */ 0,
+ /* edgeFlags */ 0,
+ /* source */ InputDevice.SOURCE_ROTARY_ENCODER,
+ /* flags */ 0
+ );
+ }
+}
diff --git a/tests/Input/src/com/android/server/input/debug/RotaryInputGraphViewTest.java b/tests/Input/src/com/android/server/input/debug/RotaryInputGraphViewTest.java
new file mode 100644
index 0000000..af6ece4
--- /dev/null
+++ b/tests/Input/src/com/android/server/input/debug/RotaryInputGraphViewTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 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.input.debug;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Build/Install/Run:
+ * atest RotaryInputGraphViewTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class RotaryInputGraphViewTest {
+
+ private RotaryInputGraphView mRotaryInputGraphView;
+
+ @Before
+ public void setUp() throws Exception {
+ Context context = InstrumentationRegistry.getContext();
+
+ mRotaryInputGraphView = new RotaryInputGraphView(context);
+ }
+
+ @Test
+ public void startsWithDefaultFrameCenter() {
+ assertEquals(0, mRotaryInputGraphView.getFrameCenterPosition(), 0.01);
+ }
+
+ @Test
+ public void addValue_translatesRotaryInputGraphViewWithHighScrollValue() {
+ final float scrollAxisValue = 1000f;
+ final long eventTime = 0;
+
+ mRotaryInputGraphView.addValue(scrollAxisValue, eventTime);
+
+ assertTrue(mRotaryInputGraphView.getFrameCenterPosition() > 0);
+ }
+}
diff --git a/tests/Input/src/com/android/server/input/debug/RotaryInputValueViewTest.java b/tests/Input/src/com/android/server/input/debug/RotaryInputValueViewTest.java
new file mode 100644
index 0000000..e5e3852
--- /dev/null
+++ b/tests/Input/src/com/android/server/input/debug/RotaryInputValueViewTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 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.input.debug;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.content.Context;
+import android.view.ViewConfiguration;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Locale;
+
+/**
+ * Build/Install/Run:
+ * atest RotaryInputValueViewTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class RotaryInputValueViewTest {
+
+ private final Locale mDefaultLocale = Locale.getDefault();
+
+ private RotaryInputValueView mRotaryInputValueView;
+ private float mScaledVerticalScrollFactor;
+
+ @Before
+ public void setUp() throws Exception {
+ Context context = InstrumentationRegistry.getContext();
+ mScaledVerticalScrollFactor =
+ ViewConfiguration.get(context).getScaledVerticalScrollFactor();
+
+ mRotaryInputValueView = new RotaryInputValueView(context);
+ }
+
+ @Test
+ public void startsWithDefaultValue() {
+ assertEquals("+0.0", mRotaryInputValueView.getText().toString());
+ }
+
+ @Test
+ public void updateValue_updatesTextWithScrollValue() {
+ final float scrollAxisValue = 1000f;
+ final String expectedText = String.format(mDefaultLocale, "+%.1f",
+ scrollAxisValue * mScaledVerticalScrollFactor);
+
+ mRotaryInputValueView.updateValue(scrollAxisValue);
+
+ assertEquals(expectedText, mRotaryInputValueView.getText().toString());
+ }
+
+ @Test
+ public void updateActivityStatus_setsAndRemovesColorFilter() {
+ // It should not be active initially.
+ assertNull(mRotaryInputValueView.getBackground().getColorFilter());
+
+ mRotaryInputValueView.updateActivityStatus(true);
+ // It should be active after rotary input.
+ assertNotNull(mRotaryInputValueView.getBackground().getColorFilter());
+
+ mRotaryInputValueView.updateActivityStatus(false);
+ // It should not be active after waiting for mUpdateActivityStatusCallback.
+ assertNull(mRotaryInputValueView.getBackground().getColorFilter());
+ }
+}
diff --git a/tests/InputMethodStressTest/Android.bp b/tests/InputMethodStressTest/Android.bp
index 84845c6..58ceb3f 100644
--- a/tests/InputMethodStressTest/Android.bp
+++ b/tests/InputMethodStressTest/Android.bp
@@ -26,7 +26,7 @@
"compatibility-device-util-axt",
"platform-test-annotations",
"platform-test-rules",
- "truth-prebuilt",
+ "truth",
],
test_suites: [
"general-tests",
diff --git a/tests/InputScreenshotTest/Android.bp b/tests/InputScreenshotTest/Android.bp
index eee486f..15aaa46 100644
--- a/tests/InputScreenshotTest/Android.bp
+++ b/tests/InputScreenshotTest/Android.bp
@@ -29,7 +29,7 @@
"androidx.lifecycle_lifecycle-livedata-ktx",
"androidx.lifecycle_lifecycle-runtime-compose",
"androidx.navigation_navigation-compose",
- "truth-prebuilt",
+ "truth",
"androidx.compose.runtime_runtime",
"androidx.test.core",
"androidx.test.ext.junit",
@@ -47,7 +47,7 @@
"services.core.unboosted",
"testables",
"testng",
- "truth-prebuilt",
+ "truth",
],
libs: [
"android.test.mock",
diff --git a/tests/Internal/Android.bp b/tests/Internal/Android.bp
index ef45864..ddec8fa 100644
--- a/tests/Internal/Android.bp
+++ b/tests/Internal/Android.bp
@@ -19,7 +19,7 @@
"junit",
"androidx.test.rules",
"mockito-target-minus-junit4",
- "truth-prebuilt",
+ "truth",
"platform-test-annotations",
],
java_resource_dirs: ["res"],
diff --git a/tests/LocalizationTest/Android.bp b/tests/LocalizationTest/Android.bp
index 4e0b0a89..909ca59 100644
--- a/tests/LocalizationTest/Android.bp
+++ b/tests/LocalizationTest/Android.bp
@@ -34,7 +34,7 @@
"androidx.test.ext.junit",
"androidx.test.rules",
"mockito-target-extended-minus-junit4",
- "truth-prebuilt",
+ "truth",
],
jni_libs: [
// For mockito extended
diff --git a/tests/MidiTests/Android.bp b/tests/MidiTests/Android.bp
index 254770d..fcacab3 100644
--- a/tests/MidiTests/Android.bp
+++ b/tests/MidiTests/Android.bp
@@ -31,7 +31,7 @@
"mockito-target-inline-minus-junit4",
"platform-test-annotations",
"services.midi",
- "truth-prebuilt",
+ "truth",
],
jni_libs: ["libdexmakerjvmtiagent"],
certificate: "platform",
diff --git a/tests/PackageWatchdog/Android.bp b/tests/PackageWatchdog/Android.bp
index 1e1dc84..e0e6c4c 100644
--- a/tests/PackageWatchdog/Android.bp
+++ b/tests/PackageWatchdog/Android.bp
@@ -32,7 +32,7 @@
"androidx.test.rules",
"services.core",
"services.net",
- "truth-prebuilt",
+ "truth",
],
libs: ["android.test.runner"],
jni_libs: [
diff --git a/tests/PlatformCompatGating/Android.bp b/tests/PlatformCompatGating/Android.bp
index f0f9c4b..fd992cf 100644
--- a/tests/PlatformCompatGating/Android.bp
+++ b/tests/PlatformCompatGating/Android.bp
@@ -38,7 +38,7 @@
"androidx.test.ext.junit",
"mockito-target-minus-junit4",
"testng",
- "truth-prebuilt",
+ "truth",
"platform-compat-test-rules",
],
}
diff --git a/tests/PlatformCompatGating/test-rules/Android.bp b/tests/PlatformCompatGating/test-rules/Android.bp
index 5f91f9d0..f6a41c2 100644
--- a/tests/PlatformCompatGating/test-rules/Android.bp
+++ b/tests/PlatformCompatGating/test-rules/Android.bp
@@ -29,7 +29,7 @@
static_libs: [
"junit",
"androidx.test.core",
- "truth-prebuilt",
- "core-compat-test-rules"
+ "truth",
+ "core-compat-test-rules",
],
}
diff --git a/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt b/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt
index 56ab755..7e43566 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt
@@ -37,6 +37,8 @@
private var window: Window? = null
private var currentModeDisplay: TextView? = null
+ private var desiredRatio = 0.0f
+
override fun onFinishInflate() {
super.onFinishInflate()
val window = window ?: throw IllegalStateException("Failed to attach window")
@@ -67,6 +69,7 @@
override fun onAttachedToWindow() {
super.onAttachedToWindow()
+ desiredRatio = window?.desiredHdrHeadroom ?: 0.0f
val hdrVis = if (display.isHdrSdrRatioAvailable) {
display.registerHdrSdrRatioChangedListener({ it.run() }, hdrSdrListener)
View.VISIBLE
@@ -83,6 +86,11 @@
}
private fun setColorMode(newMode: Int) {
+ if (newMode == ActivityInfo.COLOR_MODE_HDR &&
+ window!!.colorMode == ActivityInfo.COLOR_MODE_HDR) {
+ desiredRatio = (desiredRatio + 1) % 5.0f
+ window!!.desiredHdrHeadroom = desiredRatio
+ }
window!!.colorMode = newMode
updateModeInfoDisplay()
}
diff --git a/tests/SurfaceViewBufferTests/Android.bp b/tests/SurfaceViewBufferTests/Android.bp
index 38313f8..055d625 100644
--- a/tests/SurfaceViewBufferTests/Android.bp
+++ b/tests/SurfaceViewBufferTests/Android.bp
@@ -45,7 +45,7 @@
"kotlinx-coroutines-android",
"flickerlib",
"flickerlib-trace_processor_shell",
- "truth-prebuilt",
+ "truth",
"cts-wm-util",
"CtsSurfaceValidatorLib",
],
diff --git a/tests/TaskOrganizerTest/Android.bp b/tests/TaskOrganizerTest/Android.bp
index bf12f42..d2ade34 100644
--- a/tests/TaskOrganizerTest/Android.bp
+++ b/tests/TaskOrganizerTest/Android.bp
@@ -43,6 +43,6 @@
"kotlinx-coroutines-android",
"flickerlib",
"flickerlib-trace_processor_shell",
- "truth-prebuilt",
+ "truth",
],
}
diff --git a/tests/TelephonyCommonTests/Android.bp b/tests/TelephonyCommonTests/Android.bp
index 81ec265..b968e5d 100644
--- a/tests/TelephonyCommonTests/Android.bp
+++ b/tests/TelephonyCommonTests/Android.bp
@@ -32,11 +32,11 @@
static_libs: [
"mockito-target-extended",
"androidx.test.rules",
- "truth-prebuilt",
+ "truth",
"platform-test-annotations",
"androidx.core_core",
"androidx.fragment_fragment",
- "androidx.test.ext.junit"
+ "androidx.test.ext.junit",
],
jni_libs: ["libdexmakerjvmtiagent"],
diff --git a/tests/TrustTests/Android.bp b/tests/TrustTests/Android.bp
index c216bce..4e75a1d 100644
--- a/tests/TrustTests/Android.bp
+++ b/tests/TrustTests/Android.bp
@@ -28,7 +28,7 @@
"flag-junit",
"mockito-target-minus-junit4",
"servicestests-utils",
- "truth-prebuilt",
+ "truth",
],
libs: [
"android.test.runner",
diff --git a/tests/UpdatableSystemFontTest/Android.bp b/tests/UpdatableSystemFontTest/Android.bp
index 9bfcc18..ddb3649 100644
--- a/tests/UpdatableSystemFontTest/Android.bp
+++ b/tests/UpdatableSystemFontTest/Android.bp
@@ -30,7 +30,7 @@
"androidx.test.uiautomator_uiautomator",
"compatibility-device-util-axt",
"platform-test-annotations",
- "truth-prebuilt",
+ "truth",
],
test_suites: [
"general-tests",
diff --git a/tests/UsbManagerTests/Android.bp b/tests/UsbManagerTests/Android.bp
index 97fbf5b..c02d8e9 100644
--- a/tests/UsbManagerTests/Android.bp
+++ b/tests/UsbManagerTests/Android.bp
@@ -31,7 +31,7 @@
"androidx.test.rules",
"mockito-target-inline-minus-junit4",
"platform-test-annotations",
- "truth-prebuilt",
+ "truth",
"UsbManagerTestLib",
],
jni_libs: ["libdexmakerjvmtiagent"],
diff --git a/tests/UsbManagerTests/lib/Android.bp b/tests/UsbManagerTests/lib/Android.bp
index 994484c..4e5a70fe 100644
--- a/tests/UsbManagerTests/lib/Android.bp
+++ b/tests/UsbManagerTests/lib/Android.bp
@@ -34,7 +34,7 @@
"services.core",
"services.net",
"services.usb",
- "truth-prebuilt",
+ "truth",
"androidx.core_core",
],
libs: [
diff --git a/tests/UsbTests/Android.bp b/tests/UsbTests/Android.bp
index c60c519..92c2711 100644
--- a/tests/UsbTests/Android.bp
+++ b/tests/UsbTests/Android.bp
@@ -34,7 +34,7 @@
"services.core",
"services.net",
"services.usb",
- "truth-prebuilt",
+ "truth",
"UsbManagerTestLib",
],
jni_libs: [
diff --git a/tests/componentalias/Android.bp b/tests/componentalias/Android.bp
index 01d34e4..39037f2 100644
--- a/tests/componentalias/Android.bp
+++ b/tests/componentalias/Android.bp
@@ -25,7 +25,7 @@
"androidx.test.rules",
"compatibility-device-util-axt",
"mockito-target-extended-minus-junit4",
- "truth-prebuilt",
+ "truth",
],
libs: ["android.test.base"],
srcs: [
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/aapt2/Android.bp b/tools/aapt2/Android.bp
index 7323b0f..977b276 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -220,6 +220,7 @@
name: "aapt2-protos",
tools: [":soong_zip"],
srcs: [
+ "ApkInfo.proto",
"Configuration.proto",
"ResourcesInternal.proto",
"ResourceMetadata.proto",
diff --git a/tools/aapt2/java/AnnotationProcessor.cpp b/tools/aapt2/java/AnnotationProcessor.cpp
index 87da09a..8c644cf 100644
--- a/tools/aapt2/java/AnnotationProcessor.cpp
+++ b/tools/aapt2/java/AnnotationProcessor.cpp
@@ -49,16 +49,19 @@
kDeprecated = 0x01,
kSystemApi = 0x02,
kTestApi = 0x04,
+ kFlaggedApi = 0x08,
};
StringPiece doc_str;
uint32_t bit_mask;
StringPiece annotation;
+ bool preserve_params;
};
-static std::array<AnnotationRule, 2> sAnnotationRules = {{
- {"@SystemApi", AnnotationRule::kSystemApi, "@android.annotation.SystemApi"},
- {"@TestApi", AnnotationRule::kTestApi, "@android.annotation.TestApi"},
+static std::array<AnnotationRule, 3> sAnnotationRules = {{
+ {"@SystemApi", AnnotationRule::kSystemApi, "@android.annotation.SystemApi", true},
+ {"@TestApi", AnnotationRule::kTestApi, "@android.annotation.TestApi", false},
+ {"@FlaggedApi", AnnotationRule::kFlaggedApi, "@android.annotation.FlaggedApi", true},
}};
void AnnotationProcessor::AppendCommentLine(std::string comment) {
@@ -73,12 +76,11 @@
std::string::size_type idx = comment.find(rule.doc_str.data());
if (idx != std::string::npos) {
// Captures all parameters associated with the specified annotation rule
- // by matching the first pair of parantheses after the rule.
- std::regex re(std::string(rule.doc_str) += "\\s*\\((.+)\\)");
+ // by matching the first pair of parentheses after the rule.
+ std::regex re(std::string(rule.doc_str).append(R"(\s*\((.+)\))"));
std::smatch match_result;
const bool is_match = std::regex_search(comment, match_result, re);
- // We currently only capture and preserve parameters for SystemApi.
- if (is_match && rule.bit_mask == AnnotationRule::kSystemApi) {
+ if (is_match && rule.preserve_params) {
annotation_parameter_map_[rule.bit_mask] = match_result[1].str();
comment.erase(comment.begin() + match_result.position(),
comment.begin() + match_result.position() + match_result.length());
diff --git a/tools/aapt2/java/AnnotationProcessor_test.cpp b/tools/aapt2/java/AnnotationProcessor_test.cpp
index 6bc8902..e98e96b 100644
--- a/tools/aapt2/java/AnnotationProcessor_test.cpp
+++ b/tools/aapt2/java/AnnotationProcessor_test.cpp
@@ -76,6 +76,36 @@
EXPECT_THAT(annotations, HasSubstr("This is a system API"));
}
+TEST(AnnotationProcessorTest, EmitsFlaggedApiAnnotationAndRemovesFromComment) {
+ AnnotationProcessor processor;
+ processor.AppendComment("@FlaggedApi This is a flagged API");
+
+ std::string annotations;
+ StringOutputStream out(&annotations);
+ Printer printer(&out);
+ processor.Print(&printer);
+ out.Flush();
+
+ EXPECT_THAT(annotations, HasSubstr("@android.annotation.FlaggedApi"));
+ EXPECT_THAT(annotations, Not(HasSubstr("@FlaggedApi")));
+ EXPECT_THAT(annotations, HasSubstr("This is a flagged API"));
+}
+
+TEST(AnnotationProcessorTest, EmitsFlaggedApiAnnotationParamsAndRemovesFromComment) {
+ AnnotationProcessor processor;
+ processor.AppendComment("@FlaggedApi (\"android.flags.my_flag\") This is a flagged API");
+
+ std::string annotations;
+ StringOutputStream out(&annotations);
+ Printer printer(&out);
+ processor.Print(&printer);
+ out.Flush();
+
+ EXPECT_THAT(annotations, HasSubstr("@android.annotation.FlaggedApi(\"android.flags.my_flag\")"));
+ EXPECT_THAT(annotations, Not(HasSubstr("@FlaggedApi")));
+ EXPECT_THAT(annotations, HasSubstr("This is a flagged API"));
+}
+
TEST(AnnotationProcessorTest, EmitsTestApiAnnotationAndRemovesFromComment) {
AnnotationProcessor processor;
processor.AppendComment("@TestApi This is a test API");
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-framework/AndroidHostTest.bp b/tools/hoststubgen/hoststubgen/test-framework/AndroidHostTest.bp
index e7fb2de..b71e5c4 100644
--- a/tools/hoststubgen/hoststubgen/test-framework/AndroidHostTest.bp
+++ b/tools/hoststubgen/hoststubgen/test-framework/AndroidHostTest.bp
@@ -24,7 +24,7 @@
],
static_libs: [
"junit",
- "truth-prebuilt",
+ "truth",
"mockito",
// http://cs/h/googleplex-android/platform/superproject/main/+/main:platform_testing/libraries/annotations/src/android/platform/test/annotations/
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp b/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp
index 05d6a43..f9dc305 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp
@@ -104,7 +104,7 @@
],
static_libs: [
"junit",
- "truth-prebuilt",
+ "truth",
// http://cs/h/googleplex-android/platform/superproject/main/+/main:platform_testing/libraries/annotations/src/android/platform/test/annotations/
"platform-test-annotations",
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
diff --git a/tools/processors/immutability/Android.bp b/tools/processors/immutability/Android.bp
index a7d69039..ecc283b 100644
--- a/tools/processors/immutability/Android.bp
+++ b/tools/processors/immutability/Android.bp
@@ -50,7 +50,7 @@
static_libs: [
"compile-testing-prebuilt",
- "truth-prebuilt",
+ "truth",
"junit",
"kotlin-reflect",
"ImmutabilityAnnotationProcessorHostLibrary",
diff --git a/tools/processors/intdef_mappings/Android.bp b/tools/processors/intdef_mappings/Android.bp
index 7059c52..9c755b7 100644
--- a/tools/processors/intdef_mappings/Android.bp
+++ b/tools/processors/intdef_mappings/Android.bp
@@ -38,7 +38,7 @@
static_libs: [
"compile-testing-prebuilt",
- "truth-prebuilt",
+ "truth",
"junit",
"guava",
"libintdef-annotation-processor",
diff --git a/wifi/tests/Android.bp b/wifi/tests/Android.bp
index 7a29969..5a0f742 100644
--- a/wifi/tests/Android.bp
+++ b/wifi/tests/Android.bp
@@ -40,7 +40,7 @@
"frameworks-base-testutils",
"guava",
"mockito-target-minus-junit4",
- "truth-prebuilt",
+ "truth",
],
libs: [